diff --git a/config/filemanager.config.default.json b/config/filemanager.config.default.json index 390d043f..acfd43a8 100644 --- a/config/filemanager.config.default.json +++ b/config/filemanager.config.default.json @@ -4,11 +4,13 @@ "culture": "en", "theme": "flat-dark", "defaultViewMode": "grid", + "filemanagerMode": "split", "localizeGUI": true, "showFullPath": false, "showTitleAttr": false, "showConfirmation": true, "browseOnly": false, + "canCreateFolders": true, "clipboard": true, "searchBox": true, "listFiles": true, @@ -30,6 +32,15 @@ "delete" ] }, + "filetree": { + "enabled": true, + "foldersOnly": false, + "reloadOnClick": true, + "expandSpeed": 200, + "showLine": true, + "width": 200, + "minWidth": 200 + }, "manager": { "dblClickOpen": false, "selection": { @@ -170,6 +181,15 @@ "extra_js": [], "extra_js_async": true }, + "paths": { + "flash": "/scripts/", + "languages": "/languages/", + "images": "/scripts/jQuery-File-Upload/img", + "scripts": "/scripts/", + "styles": "/scripts/", + "themes": "/themes/", + "templates": "/scripts/templates/" + }, "url": "https://github.com/servocoder/RichFilemanager", "version": "2.2.1" } \ No newline at end of file diff --git a/config/filemanager.init.js.example b/config/filemanager.init.js.example deleted file mode 100644 index 938d2b23..00000000 --- a/config/filemanager.init.js.example +++ /dev/null @@ -1,4 +0,0 @@ -$('.fm-container').richFilemanager({ - // configuration options not included in .json configuration file, see: - // https://github.com/servocoder/RichFilemanager/wiki/Configuration-options#client-side-configuration -}); \ No newline at end of file diff --git a/connectors/php/BaseFilemanager.php b/connectors/php/BaseFilemanager.php index 025102de..5f09370c 100644 --- a/connectors/php/BaseFilemanager.php +++ b/connectors/php/BaseFilemanager.php @@ -236,7 +236,7 @@ public function handleRequest() break; case 'getfolder': - if($this->getvar('path') && $this->getvard('skip', true, 0)) { + if($this->getvar('path') && $this->getvard('skip', true, 0) && $this->getvard('load', true, 0)) { $response = $this->actionGetFolder(); } diff --git a/connectors/php/LocalFilemanager.php b/connectors/php/LocalFilemanager.php index f6b16f45..997a1277 100644 --- a/connectors/php/LocalFilemanager.php +++ b/connectors/php/LocalFilemanager.php @@ -145,6 +145,8 @@ public function actionGetFolder() $response_data = []; $target_path = $this->get['path']; $skipCount = $this->get['skip']; + //$load = 0 - both, 1 - folders, 2 - files + $load = $this->get['load']; $filesSkipped = 0; $target_fullpath = $this->getFullPath($target_path, true); $lazyEnabled = $this->config['options']['lazy']; @@ -168,7 +170,8 @@ public function actionGetFolder() if($file != "." && $file != "..") { //@todo Skip files not in that dirty way if ($lazyEnabled && $filesSkipped++ < $skipCount) continue; - array_push($files_list, $file); + if ((is_dir($target_fullpath . $file) && $load != 2) || ($load != 1)) + array_push($files_list, $file); if ($lazyEnabled && count($files_list) >= $lazyLimit) { $lazyFlag = false; break; diff --git a/connectors/php/config.php b/connectors/php/config.php index 3a1559d6..932f18eb 100644 --- a/connectors/php/config.php +++ b/connectors/php/config.php @@ -57,7 +57,7 @@ * - absolute path in case `serverRoot` set to "false", e.g. "/var/www/html/filemanager/userfiles/" * - relative path in case `serverRoot` set to "true", e.g. "/filemanager/userfiles/" */ - "fileRoot" => false, + "fileRoot" => '/source', /** * Format of the date to display. See http://www.php.net/manual/en/function.date.php */ diff --git a/df.txt b/df.txt new file mode 100644 index 00000000..19fbcb2b --- /dev/null +++ b/df.txt @@ -0,0 +1,1707 @@ +diff --git a/changelog b/changelog +index 112c9a8..15a6065 100644 +--- a/changelog ++++ b/changelog +@@ -1,3 +1,13 @@ ++Version 2.2.2 ++--------------------------- ++- new section "filetree" in configuration file; ++- independent ajax calls for filetree and fileview #103, #104; ++- selectable supports custom scrollbar (scrolling + selection); ++- bugfix: missed custom scrollbar for preview mode; ++- bugfix: bugfix: breadcrumbs is broken if "exclusiveFolder" is set #107; ++- bugfix: close button issue on integration with CKEditor #108; ++ ++ + Version 2.2.1 + --------------------------- + - breadcrumbs instead of string for the current path #96; +diff --git a/config/filemanager.config.default.json b/config/filemanager.config.default.json +index 069e8f7..8d16ded 100644 +--- a/config/filemanager.config.default.json ++++ b/config/filemanager.config.default.json +@@ -4,22 +4,17 @@ + "culture": "en", + "theme": "flat-dark", + "defaultViewMode": "grid", +- "filemanagerMode": "split", + "localizeGUI": true, + "showFullPath": false, + "showTitleAttr": false, + "showConfirmation": true, + "browseOnly": false, +- "canCreateFolders": true, + "clipboard": true, + "searchBox": true, +- "listFiles": true, + "fileSorting": "NAME_ASC", + "folderPosition": "bottom", + "quickSelect": false, + "charsLatinOnly": false, +- "splitterWidth": 200, +- "splitterMinWidth": 200, + "logger": false, + "capabilities": [ + "select", +@@ -32,6 +27,15 @@ + "delete" + ] + }, ++ "filetree": { ++ "enabled": true, ++ "foldersOnly": false, ++ "reloadOnClick": true, ++ "expandSpeed": 200, ++ "showLine": true, ++ "width": 200, ++ "minWidth": 200 ++ }, + "manager": { + "dblClickOpen": false, + "selection": { +@@ -172,15 +176,6 @@ + "extra_js": [], + "extra_js_async": true + }, +- "paths": { +- "flash": "/scripts/", +- "languages": "/languages/", +- "images": "/scripts/jQuery-File-Upload/img", +- "scripts": "/scripts/", +- "styles": "/scripts/", +- "themes": "/themes/", +- "templates": "/scripts/templates/" +- }, + "url": "https://github.com/servocoder/RichFilemanager", +- "version": "2.2.1" ++ "version": "2.2.2" + } +\ No newline at end of file +diff --git a/connectors/php/BaseFilemanager.php b/connectors/php/BaseFilemanager.php +index 5f09370..3f73c00 100644 +--- a/connectors/php/BaseFilemanager.php ++++ b/connectors/php/BaseFilemanager.php +@@ -236,8 +236,7 @@ abstract class BaseFilemanager + break; + + case 'getfolder': +- if($this->getvar('path') && $this->getvard('skip', true, 0) && $this->getvard('load', true, 0)) { +- ++ if($this->getvar('path')) { + $response = $this->actionGetFolder(); + } + break; +@@ -445,27 +444,6 @@ abstract class BaseFilemanager + } + + /** +- * Retrieve data from $_GET global var. Sets default value of variable if it wasn't set +- * @param string $var +- * @param bool $sanitize +- * @param object $default +- * @return bool +- */ +- public function getvard($var, $sanitize = true, $default = null) +- { +- if(!isset($_GET[$var]) || $_GET[$var]=='') { +- $this->get[$var] = $default; +- } else { +- if($sanitize) { +- $this->get[$var] = $this->sanitize($_GET[$var]); +- } else { +- $this->get[$var] = $_GET[$var]; +- } +- } +- return true; +- } +- +- /** + * Retrieve data from $_POST global var + * @param string $var + * @param bool $sanitize +diff --git a/connectors/php/LocalFilemanager.php b/connectors/php/LocalFilemanager.php +index 997a127..72854e2 100644 +--- a/connectors/php/LocalFilemanager.php ++++ b/connectors/php/LocalFilemanager.php +@@ -144,14 +144,7 @@ class LocalFilemanager extends BaseFilemanager + $files_list = []; + $response_data = []; + $target_path = $this->get['path']; +- $skipCount = $this->get['skip']; +- //$load = 0 - both, 1 - folders, 2 - files +- $load = $this->get['load']; +- $filesSkipped = 0; + $target_fullpath = $this->getFullPath($target_path, true); +- $lazyEnabled = $this->config['options']['lazy']; +- $lazyLimit = $this->config['options']['lazyLimit']; +- $lazyFlag = true; + Log::info('opening folder "' . $target_fullpath . '"'); + + if(!is_dir($target_fullpath)) { +@@ -168,14 +161,7 @@ class LocalFilemanager extends BaseFilemanager + } else { + while (false !== ($file = readdir($handle))) { + if($file != "." && $file != "..") { +- //@todo Skip files not in that dirty way +- if ($lazyEnabled && $filesSkipped++ < $skipCount) continue; +- if ((is_dir($target_fullpath . $file) && $load != 2) || ($load != 1)) +- array_push($files_list, $file); +- if ($lazyEnabled && count($files_list) >= $lazyLimit) { +- $lazyFlag = false; +- break; +- } ++ array_push($files_list, $file); + } + } + closedir($handle); +@@ -193,7 +179,7 @@ class LocalFilemanager extends BaseFilemanager + } + } + +- return array('tree' => $response_data, 'done' => $lazyFlag); ++ return $response_data; + } + + /** +diff --git a/connectors/php/config.php b/connectors/php/config.php +index 3a1559d..a1558ad 100644 +--- a/connectors/php/config.php ++++ b/connectors/php/config.php +@@ -78,16 +78,6 @@ $config = [ + * For the full list of capabilities @see BaseFilemanager::actions_list + */ + "capabilities" => false, +- /** +- * Default value "true". +- * Files downloads in lazy way +- */ +- "lazy" => true, +- /** +- * Default value "100". +- * Count of files per one lazy operation +- */ +- "lazyLimit" => 100 + ], + /** + * Security section +diff --git a/index.html b/index.html +index 803b9f4..8e2438b 100644 +--- a/index.html ++++ b/index.html +@@ -80,14 +80,12 @@ + + + +- +
+ +
+ +- +
+ +
++ ++
++ ++
++ + + + +
+-
++
+
+
+ +
+
+ +-
+-
    +- +-
  • +-
    +- +-
    +-

    ..

    +-
  • +- ++
    + +- +-
  • +-
    +-
    ++
    ++
      ++ ++
    • +
      +- ++ +
      +-

      +-
    +-
  • +- +-
++

..

++ ++ + ++ ++
  • ++
    ++
    ++
    ++ ++
    ++

    ++
    ++
  • ++ ++ + +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- + +- +- +- +- +- +- +- +- +- +- +-
    ..
    +- +- +-
    ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++
    ..
    ++ ++ ++
    ++
    +
    + + +- +-
    ++
    ++ +
    +
    +
    ++ +
    +- +
    +
    + +diff --git a/package.json b/package.json +index c2fb9f9..481dec3 100644 +--- a/package.json ++++ b/package.json +@@ -1,6 +1,6 @@ + { + "name": "rich-filemanager", +- "version": "2.2.1", ++ "version": "2.2.2", + "description": "Highly customizable open-source file manager", + "main": "scripts/filemanager.js", + "repository": { +diff --git a/scripts/filemanager.js b/scripts/filemanager.js +index 907575a..9624b13 100644 +--- a/scripts/filemanager.js ++++ b/scripts/filemanager.js +@@ -12,16 +12,6 @@ + + (function($) { + +-// function to retrieve GET params +-$.urlParam = function(name) { +- var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href); +- if (results) { +- return results[1]; +- } else { +- return 0; +- } +-}; +- + $.richFilemanagerPlugin = function(element, pluginOptions) + { + /** +@@ -29,9 +19,7 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + */ + var defaults = { + baseUrl: '.', // relative path to the FM plugin folder +- config: { +- 'url': '/config/', +- }, // configuration options ++ config: {}, // configuration options + callbacks: { + beforeCreateImageUrl: function (resourceObject, url) { + return url; +@@ -78,6 +66,7 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + fullexpandedFolder = null, // path to be automatically expanded by filetree plugin + + /** service variables **/ ++ _url_ = purl(), + timeStart = new Date().getTime(); + + /** +@@ -188,24 +177,11 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + // Forces columns to fill the layout vertically. + // Called on initial page load and on resize. + fm.setDimensions = function() { +- var bheight = 0, +- padding = $container.outerHeight(true) - $container.height(); +- +- if($.urlParam('CKEditorCleanUpFuncNum')) bheight +=60; +- +- var newH = $(window).height() - $header.height() - $footer.height() - padding - bheight; +- $splitter.height(newH); ++ var padding = $wrapper.outerHeight(true) - $wrapper.height(), ++ newH = $(window).height() - $header.height() - $footer.height() - padding, ++ newW = $splitter.width() - $splitter.children(".splitter-bar-vertical").outerWidth() - $filetree.outerWidth(); + +- // adjust height of filemanager if there are other DOM elemements on page +- var delta = $(document).height() - $(window).height(); +- if(!$container.parent().is('body') && delta > 0) { +- var diff = newH - delta; +- newH = (diff > 0) ? diff : 1; +- $splitter.height(newH); +- } +- +- // adjustment for window horizontal resize +- var newW = $splitter.width() - $splitter.children(".splitter-bar-vertical").outerWidth() - $filetree.outerWidth(); ++ $splitter.height(newH); + $fileinfo.width(newW); + }; + +@@ -303,8 +279,8 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + + // localize messages based on culture var or from URL + var localize = function() { +- var langCode = $.urlParam('langCode'); +- var langPath = fm.settings.baseUrl + config.paths.languages; ++ var langCode = _url_.param('langCode'); ++ var langPath = fm.settings.baseUrl + '/languages/'; + + function buildLangPath(code) { + return langPath + code + '.json'; +@@ -312,7 +288,7 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + + return $.ajax() + .then(function() { +- if(langCode != 0) { ++ if(langCode) { + // try to load lang file based on langCode in query params + return file_exists(buildLangPath(langCode)) + .done(function() { +@@ -352,11 +328,11 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + secondary = []; + + // theme defined in configuration file +- primary.push(config.paths.themes + config.options.theme + '/styles/theme.css'); ++ primary.push('/themes/' + config.options.theme + '/styles/theme.css'); + + if(config.customScrollbar.enabled) { +- primary.push(config.paths.styles + 'custom-scrollbar-plugin/jquery.mCustomScrollbar.min.css'); +- primary.push(config.paths.scripts + 'custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js'); ++ primary.push('/scripts/custom-scrollbar-plugin/jquery.mCustomScrollbar.min.css'); ++ primary.push('/scripts/custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js'); + } + + // add callback on loaded assets and inject primary ones +@@ -367,33 +343,33 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + if(config.viewer.editable.enabled) { + var editorTheme = config.viewer.editable.theme; + if (editorTheme && editorTheme !== 'default') { +- secondary.push(config.paths.styles + 'CodeMirror/theme/' + editorTheme + '.css'); ++ secondary.push('/scripts/CodeMirror/theme/' + editorTheme + '.css'); + } +- secondary.push(config.paths.styles + 'CodeMirror/lib/codemirror.css'); +- secondary.push(config.paths.scripts + 'CodeMirror/lib/codemirror.js'); +- secondary.push(config.paths.scripts + 'CodeMirror/addon/selection/active-line.js'); +- secondary.push(config.paths.styles + 'CodeMirror/addon/display/fullscreen.css'); +- secondary.push(config.paths.scripts + 'CodeMirror/addon/display/fullscreen.js'); ++ secondary.push('/scripts/CodeMirror/lib/codemirror.css'); ++ secondary.push('/scripts/CodeMirror/lib/codemirror.js'); ++ secondary.push('/scripts/CodeMirror/addon/selection/active-line.js'); ++ secondary.push('/scripts/CodeMirror/addon/display/fullscreen.css'); ++ secondary.push('/scripts/CodeMirror/addon/display/fullscreen.js'); + } + + if(!config.options.browseOnly) { + // Loading jquery file upload library +- secondary.push(config.paths.scripts + 'jQuery-File-Upload/js/vendor/jquery.ui.widget.js'); +- secondary.push(config.paths.scripts + 'jQuery-File-Upload/js/canvas-to-blob.min.js'); +- secondary.push(config.paths.scripts + 'jQuery-File-Upload/js/load-image.all.min.js'); +- secondary.push(config.paths.scripts + 'jQuery-File-Upload/js/jquery.iframe-transport.js'); +- secondary.push(config.paths.scripts + 'jQuery-File-Upload/js/jquery.fileupload.js'); +- secondary.push(config.paths.scripts + 'jQuery-File-Upload/js/jquery.fileupload-process.js'); +- secondary.push(config.paths.scripts + 'jQuery-File-Upload/js/jquery.fileupload-image.js'); +- secondary.push(config.paths.scripts + 'jQuery-File-Upload/js/jquery.fileupload-validate.js'); ++ secondary.push('/scripts/jQuery-File-Upload/js/vendor/jquery.ui.widget.js'); ++ secondary.push('/scripts/jQuery-File-Upload/js/canvas-to-blob.min.js'); ++ secondary.push('/scripts/jQuery-File-Upload/js/load-image.all.min.js'); ++ secondary.push('/scripts/jQuery-File-Upload/js/jquery.iframe-transport.js'); ++ secondary.push('/scripts/jQuery-File-Upload/js/jquery.fileupload.js'); ++ secondary.push('/scripts/jQuery-File-Upload/js/jquery.fileupload-process.js'); ++ secondary.push('/scripts/jQuery-File-Upload/js/jquery.fileupload-image.js'); ++ secondary.push('/scripts/jQuery-File-Upload/js/jquery.fileupload-validate.js'); + + if(config.upload.multiple) { +- secondary.push(config.paths.styles + 'jQuery-File-Upload/css/dropzone.css'); ++ secondary.push('/scripts/jQuery-File-Upload/css/dropzone.css'); + } + } + + if(config.options.charsLatinOnly) { +- secondary.push(config.paths.scripts + 'speakingurl/speakingurl.min.js'); ++ secondary.push('/scripts/speakingurl/speakingurl.min.js'); + } + + if(secondary.length) { +@@ -415,18 +391,17 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + configSortOrder = chunks[1] || 'asc'; + + // changes files root to restrict the view to a given folder +- if($.urlParam('exclusiveFolder') != 0) { +- fileRoot += $.urlParam('exclusiveFolder'); +- if(isFile(fileRoot)) fileRoot += '/'; // add last '/' if needed +- fileRoot = fileRoot.replace(/\/\//g, '\/'); ++ var exclusiveFolder = _url_.param('exclusiveFolder'); ++ if(exclusiveFolder) { ++ fileRoot = '/' + exclusiveFolder + '/'; ++ fileRoot = normalizePath(fileRoot); + } + + // get folder that should be expanded after filemanager is loaded +- var expandedFolder = ''; +- if($.urlParam('expandedFolder') != 0) { +- expandedFolder = $.urlParam('expandedFolder'); +- fullexpandedFolder = fileRoot + expandedFolder; +- fullexpandedFolder = fullexpandedFolder.replace(/\/\//g, '\/'); ++ var expandedFolder = _url_.param('expandedFolder'); ++ if(expandedFolder) { ++ fullexpandedFolder = fileRoot + expandedFolder + '/'; ++ fullexpandedFolder = normalizePath(fullexpandedFolder); + } + + // Activates knockout.js +@@ -444,13 +419,13 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + return false; + } + if(node.isExpanded() === false) { +- $(element).slideDown(fmModel.treeModel.options.expandSpeed, function() { ++ $(element).slideDown(config.filetree.expandSpeed, function() { + node.isSliding(false); + node.isExpanded(true); + }); + } + if(node.isExpanded() === true) { +- $(element).slideUp(fmModel.treeModel.options.expandSpeed, function() { ++ $(element).slideUp(config.filetree.expandSpeed, function() { + node.isSliding(false); + node.isExpanded(false); + }); +@@ -553,9 +528,13 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + filter: "li:not(.directory-parent), tbody > tr:not(.directory-parent)", + cancel: ".directory-parent, thead", + disabled: !config.manager.selection.enabled, +- appendTo: '.fm-container', ++ appendTo: $viewItems, + start: function(event, ui) { + clearSelection(); ++ fmModel.itemsModel.isSelecting(true); ++ }, ++ stop: function(event, ui) { ++ fmModel.itemsModel.isSelecting(false); + }, + selected: function(event, ui) { + var koItem = ko.dataFor(ui.selected); +@@ -668,12 +647,11 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + } + + // adding a close button triggering callback function if CKEditorCleanUpFuncNum passed +- if($.urlParam('CKEditorCleanUpFuncNum')) { +- $("body").append(''); +- +- $('#fm-js-btn-close').click(function () { +- parent.CKEDITOR.tools.callFunction($.urlParam('CKEditorCleanUpFuncNum')); +- }); ++ if(_url_.param('CKEditorCleanUpFuncNum')) { ++ fmModel.headerModel.closeButton(true); ++ fmModel.headerModel.closeButtonOnClick = function() { ++ parent.CKEDITOR.tools.callFunction(_url_.param('CKEditorCleanUpFuncNum')); ++ }; + } + + // input file replacement +@@ -681,6 +659,10 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + $("#filepath").val($(this).val().replace(/.+[\\\/]/, "")); + }); + ++ prepareFileTree(); ++ prepareFileView(); ++ setupUploader(); ++ + // Loading CustomScrollbar if enabled + if(config.customScrollbar.enabled) { + $filetree.mCustomScrollbar({ +@@ -698,37 +680,69 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + }, + onScroll: function() { + fmModel.treeModel.isScrolling(false); +- if (config.options.lazy) +- fmModel.treeModel.onCustomScrolling(this.mcs.top * (-1) + this.mcs.content.parent().height()); + } + }, + axis: "yx" + }); + +- $viewItems.mCustomScrollbar({ +- theme: config.customScrollbar.theme, +- scrollButtons: { +- enable: config.customScrollbar.button +- }, +- advanced: { +- autoExpandHorizontalScroll:true, +- updateOnContentResize: true, +- updateOnSelectorChange: '.grid, .list' +- }, +- callbacks: { +- onScrollStart: function() { +- fmModel.itemsModel.isScrolling(true); +- }, +- onScroll: function() { +- fmModel.itemsModel.isScrolling(false); +- if (config.options.lazy) +- fmModel.itemsModel.onCustomScrolling(this.mcs.topPct); +- } +- }, +- axis: "y", +- alwaysShowScrollbar: 0 ++ $fileinfo.find('.fm-preview').mCustomScrollbar({ ++ theme: config.customScrollbar.theme, ++ scrollButtons: { ++ enable: config.customScrollbar.button ++ }, ++ advanced: { ++ autoExpandHorizontalScroll: true, ++ updateOnContentResize: true, ++ updateOnSelectorChange: '.fm-preview-viewer' ++ } + }); +- } ++ ++ $fileinfo.find('.view-items-wrapper').mCustomScrollbar({ ++ theme: config.customScrollbar.theme, ++ scrollButtons: { ++ enable: config.customScrollbar.button ++ }, ++ advanced: { ++ autoExpandHorizontalScroll:true, ++ updateOnContentResize: true, ++ updateOnSelectorChange: '.grid, .list' ++ }, ++ callbacks: { ++ onScrollStart: function() { ++ if (!fmModel.itemsModel.continiousSelection()) { ++ this.yStartPosition = this.mcs.top; ++ this.yStartTime = (new Date()).getTime(); ++ } ++ fmModel.itemsModel.isScrolling(true); ++ }, ++ onScroll: function() { ++ fmModel.itemsModel.isScrolling(false); ++ }, ++ whileScrolling: function() { ++ if (config.manager.selection.enabled) { ++ // would prefer to get scroll position from [onScrollStart], ++ // but the [onScroll] should fire first, which happens with a big lag ++ var timeDiff = (new Date()).getTime() - this.yStartTime; ++ ++ // check if selection lasso has not been dropped while scrolling ++ if (!fmModel.itemsModel.continiousSelection() && timeDiff > 400) { ++ this.yStartPosition = this.mcs.top; ++ } ++ ++ // set flag if selection lasso is active ++ if (fmModel.itemsModel.isSelecting()) { ++ fmModel.itemsModel.continiousSelection(true); ++ } ++ ++ var yIncrement = Math.abs(this.mcs.top) - Math.abs(this.yStartPosition); ++ $viewItems.selectable("repositionCssHelper", yIncrement, 0); ++ } ++ } ++ }, ++ axis: "y", ++ alwaysShowScrollbar: 0 ++ }); ++ } + + // add useragent string to html element for IE 10/11 detection + var doc = document.documentElement; +@@ -740,19 +754,12 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + console.log('Total execution time : ' + time + ' ms'); + } + +- // Provides support for adjustible columns. +- $splitter.splitter({ +- sizeLeft: config.options.splitterWidth, +- minLeft: config.options.splitterMinWidth, +- minRight: 200 +- }); +- + var $loading = $container.find('.fm-loading-wrap'); +- $loading.fadeOut(800); // remove loading screen div +- $(window).trigger('resize'); +- +- createFileTree(); +- setupUploader(); ++ // remove loading screen div ++ $loading.fadeOut(800, function() { ++ fm.setDimensions(); ++ }); ++ fm.setDimensions(); + }; + + /** +@@ -769,7 +776,6 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + this.viewMode = ko.observable(config.options.defaultViewMode); + this.currentPath = ko.observable(fileRoot); + this.browseOnly = ko.observable(config.options.browseOnly); +- this.canCreateFolders = ko.observable(config.options.canCreateFolders); + + this.previewFile.subscribe(function (value) { + if (!value) { +@@ -901,7 +907,7 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + model.previewFile(true); + + // zeroClipboard code +- ZeroClipboard.config({swfPath: fm.settings.baseUrl + config.paths.flash + 'zeroclipboard/dist/ZeroClipboard.swf'}); ++ ZeroClipboard.config({swfPath: fm.settings.baseUrl + '/scripts/zeroclipboard/dist/ZeroClipboard.swf'}); + var client = new ZeroClipboard(document.getElementById("fm-js-clipboard-copy")); + client.on("ready", function(readyEvent) { + client.on("aftercopy", function(event) { +@@ -942,14 +948,6 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + var tree_model = this; + this.isScrolling = ko.observable(false); + this.selecledNode = ko.observable(null); +- this.currentNode = ko.observable(null); +- +- this.options = { +- showLine: true, +- dblClickOpen: config.manager.dblClickOpen, +- reloadOnClick: false, +- expandSpeed: 200 +- }; + + this.treeData = { + id: fileRoot, +@@ -973,11 +971,11 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + }, parentNode); + + if (node) { +- tree_model.options.expandSpeed = 10; +- tree_model.loadNodes(node, {'refresh': false}); ++ config.filetree.expandSpeed = 10; ++ tree_model.loadNodes(node, false); + } else { + fullexpandedFolder = null; +- tree_model.options.expandSpeed = 200; ++ config.filetree.expandSpeed = 200; + } + } + }; +@@ -1024,54 +1022,34 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + return null; + }; + +- //2nd parameter now 'options' array +- this.loadNodes = function(targetNode, options) { ++ this.loadNodes = function(targetNode, refresh) { + var path = targetNode ? targetNode.id : tree_model.treeData.id; +- var filesCount = model.treeModel.currentNode() ? model.treeModel.currentNode().children().length : 0; + if(targetNode) { + targetNode.isLoaded(false); + } +- if(typeof {} !== typeof options) options = {}; ++ + var queryParams = { + mode: 'getfolder', + path: path + }; + +- if($.urlParam('type')) { +- queryParams.type = $.urlParam('type'); ++ if(_url_.param('type')) { ++ queryParams.type = _url_.param('type'); + } + +- var data = {}; +- if (model.currentPath() === path) data['skip'] = filesCount; +- //This parameter is important for lazy loading and split view mode +- //It will separate loading for folders (1), files (2) or both (0) +- //Default is 0 (both) +- if (options.load && config.options.filemanagerMode === 'split') data['load'] = options.load; +- + $.ajax({ + type: 'GET', + url: buildConnectorUrl(queryParams), + dataType: "json", +- data: data, + cache: false, + success: function(response) { +- //WARNING!!! Now tree inside response.data.tree +- if(response.data && response.data.tree) { +- fmModel.currentPath(path); +- fmModel.breadcrumbsModel.splitCurrent(); +- tree_model.currentNode(targetNode); +- if (!targetNode || undefined === targetNode.isDone) { +- fmModel.itemsModel.setList(response.data.tree || options.refresh); +- } else { +- fmModel.itemsModel.extendList(response.data.tree); +- } +- ++ if(response.data) { + var nodes = []; +- $.each(response.data.tree, function(i, resourceObject) { ++ $.each(response.data, function(i, resourceObject) { + var nodeObject = tree_model.createNode(resourceObject); + nodes.push(nodeObject); + }); +- if(options.refresh) { ++ if(refresh) { + targetNode.children([]); + } + tree_model.addNodes(targetNode, nodes); +@@ -1079,9 +1057,7 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + if(targetNode) { + targetNode.isLoaded(true); + tree_model.expandNode(targetNode); +- targetNode.isDone = response.data.done; + } +- model.itemsModel.isDone(response.data.done); + expandFolderDefault(targetNode); + } + handleAjaxResponseErrors(response); +@@ -1102,7 +1078,7 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + targetNode = tree_model.treeData; + } + // list only folders in tree +- if(!config.options.listFiles) { ++ if(config.filetree.foldersOnly) { + newNodes = $.grep(newNodes, function(node) { + return (node.cdo.isFolder); + }); +@@ -1111,7 +1087,7 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + node.parentNode(targetNode); + }); + var allNodes = targetNode.children().concat(newNodes); +- targetNode.children(sortItems(allNodes, config.options.filemanagerMode === 'split' ? 'files' : '')); ++ targetNode.children(sortItems(allNodes)); + }; + + this.expandNode = function(node) { +@@ -1130,6 +1106,12 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + return false; + }; + ++ this.toggleNode = function(node) { ++ if(!tree_model.collapseNode(node)) { ++ tree_model.expandNode(node); ++ } ++ }; ++ + this.arrangeNode = function(node) { + var childrenLength = node.children().length; + $.each(node.children(), function(index, cNode) { +@@ -1172,16 +1154,6 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + } + }; + +- this.onCustomScrolling = function(offsetTop){ +- var cnt = model.treeModel.currentNode().children().length; +- var n = model.treeModel.currentNode().children()[cnt - 1].rdo.attributes.name; +- //@todo: now I'm looking for element by it's name. It's sad, but there's only way I have found +- var el = $('span.node_name:contains("' + n + '")'); +- if (offsetTop >= el.position().top && true !== model.treeModel.currentNode().isDone) { +- model.treeModel.loadNodes(model.treeModel.currentNode(), {'load': 1}); +- } +- }; +- + var TreeNodeModel = function(resourceObject) { + var tree_node = this; + this.id = resourceObject.id; +@@ -1224,13 +1196,16 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + fm.error(lg.NOT_ALLOWED_SYSTEM); + return false; + } +- tree_node.toggleNode(node, false); ++ if(!node.isLoaded()) { ++ tree_node.openNode(node); ++ } else { ++ tree_model.toggleNode(node); ++ } + }; + + this.nodeClick = function(node) { +- if(!tree_model.options.dblClickOpen) { ++ if(!config.manager.dblClickOpen) { + tree_node.openNode(node); +- tree_node.toggleNode(node, tree_model.options.reloadOnClick); + } + + if(tree_model.selecledNode() !== null) { +@@ -1241,19 +1216,8 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + }; + + this.nodeDblClick = function(node) { +- if(tree_model.options.dblClickOpen) { ++ if(config.manager.dblClickOpen) { + tree_node.openNode(node); +- tree_node.toggleNode(node, tree_model.options.reloadOnClick); +- } +- }; +- +- this.toggleNode = function(node, forceReload) { +- if(node.rdo.type === 'folder') { +- if(!node.isExpanded() && (forceReload || !node.isLoaded() || config.options.filemanagerMode === 'split')) { +- tree_model.loadNodes(node, {'refresh': true}); +- } else { +- node.isSliding(true); +- } + } + }; + +@@ -1261,17 +1225,22 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + if(node.rdo.type === 'file') { + getDetailView(node.rdo); + } +- if(node.rdo.type === 'folder' && node.isLoaded()) { +- var childrenObjects = []; +- if(node.children().length) { +- $.each(node.children(), function(index, cNode) { +- childrenObjects.push(cNode.rdo); ++ if(node.rdo.type === 'folder') { ++ if(!node.isLoaded() || (node.isExpanded() && config.filetree.reloadOnClick)) { ++ tree_model.loadNodes(node, true); ++ model.itemsModel.loadList(node.id); ++ } else { ++ tree_model.toggleNode(node); ++ ++ fmModel.currentPath(node.id); ++ fmModel.breadcrumbsModel.splitCurrent(); ++ var dataObjects = []; ++ $.each(node.children(), function(i, cnode) { ++ dataObjects.push(cnode.rdo); + }); +- } +- model.currentPath(node.rdo.id); +- model.breadcrumbsModel.splitCurrent(); +- model.itemsModel.setList(childrenObjects); +- } ++ fmModel.itemsModel.setList(dataObjects); ++ } ++ } + }; + + this.remove = function() { +@@ -1314,7 +1283,7 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + + this.switcherClass = ko.pureComputed(function() { + var cssClass = []; +- if (tree_model.options.showLine) { ++ if (config.filetree.showLine) { + if (this.level() === 0 && this.isFirstNode() && this.isLastNode()) { + cssClass.push('root'); + } else if (this.level() == 0 && this.isFirstNode()) { +@@ -1337,7 +1306,7 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + }, this); + + this.clusterClass = ko.pureComputed(function() { +- return (tree_model.options.showLine && !this.isLastNode()) ? 'line' : ''; ++ return (config.filetree.showLine && !this.isLastNode()) ? 'line' : ''; + }, this); + }; + }; +@@ -1351,7 +1320,15 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + this.listSortField = ko.observable(configSortField); + this.listSortOrder = ko.observable(configSortOrder); + this.isScrolling = ko.observable(false); +- this.isDone = ko.observable(true); ++ this.isSelecting = ko.observable(false); ++ this.continiousSelection = ko.observable(false); ++ ++ this.isSelecting.subscribe(function(state) { ++ if(!state) { ++ // means selection lasso has been dropped ++ items_model.continiousSelection(false); ++ } ++ }); + + this.createObject = function(resourceObject) { + return new ItemObject(resourceObject); +@@ -1367,44 +1344,27 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + model.itemsModel.sortObjects(); + }; + +- this.loadList = function(path, options) { ++ this.loadList = function(path) { + model.loadingView(true); +- var filesCount = model.itemsModel.objects().length; +- //All directories except root has additional element to go back to parent folder +- if (path !== '/' && config.options.filemanagerMode !== 'split') filesCount--; ++ + var queryParams = { + mode: 'getfolder', + path: path + }; +- if (typeof {} !== typeof options) options = {}; +- if($.urlParam('type')) { +- queryParams.type = $.urlParam('type'); ++ if(_url_.param('type')) { ++ queryParams.type = _url_.param('type'); + } + +- var data = {}; +- if (model.currentPath() === path) data['skip'] = filesCount; +- //This parameter is important for lazy loading and split view mode +- //It will separate loading for folders (1), files (2) or both (0) +- //Default is 0 (both) +- if (options.load && config.options.filemanagerMode === 'split') data['load'] = options.load; +- + $.ajax({ + type: 'GET', + url: buildConnectorUrl(queryParams), + dataType: "json", +- data: data, + cache: false, + success: function(response) { +- if(response.data && response.data.tree) { +- model.itemsModel.isDone(response.data.done); +- if (model.currentPath() == path && model.itemsModel.objects().length > 0) { +- model.itemsModel.extendList(response.data.tree); +- } else { +- model.currentPath(path); +- model.itemsModel.setList(response.data.tree); +- } ++ if(response.data) { + model.currentPath(path); +- model.breadcrumbsModel.splitCurrent(); ++ model.breadcrumbsModel.splitCurrent(); ++ model.itemsModel.setList(response.data); + } + handleAjaxResponseErrors(response); + }, +@@ -1444,16 +1404,6 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + model.loadingView(false); + }; + +- this.extendList = function(dataObjects) { +- var objects = []; +- $.each(dataObjects, function (i, resourceObject) { +- objects.push(items_model.createObject(resourceObject)); +- }); +- ko.utils.arrayPushAll(model.itemsModel.objects(), objects); +- model.itemsModel.sortObjects(); +- model.loadingView(false); +- }; +- + this.findByParam = function(key, value) { + return ko.utils.arrayFirst(model.itemsModel.objects(), function(object) { + return object[key] === value; +@@ -1481,7 +1431,7 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + }; + + this.sortObjects = function() { +- var sortedList = sortItems(items_model.objects(), config.options.filemanagerMode === 'split' ? 'folders' : ''); ++ var sortedList = sortItems(items_model.objects()); + items_model.objects(sortedList); + }; + +@@ -1653,12 +1603,6 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + + return true; + } +- +- this.onCustomScrolling = function(topPct){ +- if (topPct >= 95 && true !== model.itemsModel.isDone()) { +- model.itemsModel.loadList(model.currentPath(), {'load': 2}); +- } +- }; + }; + + var TableViewModel = function() { +@@ -1693,6 +1637,12 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + }; + + var HeaderModel = function() { ++ this.closeButton = ko.observable(false); ++ ++ this.closeButtonOnClick = function() { ++ console.log('clicked'); ++ }; ++ + this.goHome = function() { + model.previewFile(false); + model.itemsModel.loadList(fileRoot); +@@ -1899,29 +1849,32 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + this.items = ko.observableArray([]); + + this.add = function(path, label) { +- bc_model.items.unshift(new BcItem(path, label)); ++ bc_model.items.push(new BcItem(path, label)); + }; + + this.splitCurrent = function() { +- var chunks = model.currentPath().split('/'); +- bc_model.items([]); ++ var path = fileRoot, ++ cPath = model.currentPath(), ++ chunks = cPath.replace(new RegExp('^' + fileRoot), '').split('/'); ++ ++ // reset breadcrumbs ++ bc_model.items([]); ++ // push root node ++ bc_model.add(fileRoot, ''); + +- while (chunks.length > 0) { +- var chunk = chunks.pop(); ++ while (chunks.length > 0) { ++ var chunk = chunks.shift(); + if (chunk) { +- var paths = chunks.slice(0); +- paths.push(chunk, ''); +- bc_model.add(paths.join('/'), chunk); ++ path += chunk + '/'; ++ bc_model.add(path, chunk); + } + } +- +- bc_model.add(fileRoot, ''); + }; + + var BcItem = function(path, label) { + var bc_item = this; + this.path = path; +- this.label = label; ++ this.label = label; + this.isRoot = (path === fileRoot); + this.active = (path === model.currentPath()); + +@@ -1960,7 +1913,7 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + Helper functions + ---------------------------------------------------------*/ + +- var sortItems = function(items, exclude) { ++ var sortItems = function(items) { + var sortOrder = (fmModel.viewMode() === 'list') ? fmModel.itemsModel.listSortOrder() : configSortOrder; + var sortParams = { + natural: true, +@@ -1968,38 +1921,6 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + cases: false + }; + +- var folderItems = []; +- var parentItem; +- var i = items.length; +- while(i--) { +- if(items[i].rdo.type === 'folder') { +- folderItems.push(items[i]); +- items.splice(i, 1); +- } else if(items[i].rdo.type === 'parent') { +- parentItem = items.splice(i, 1)[0]; +- } +- } +- switch (exclude) { +- case 'folders': +- break; +- case 'files': +- items = folderItems; +- break; +- default: +- if (config.options.folderPosition !== 'top') { +- folderItems.reverse(); +- } +- for (var k = 0, fl = folderItems.length; k < fl; k++) { +- if (config.options.folderPosition === 'top') { +- items.unshift(folderItems[k]); +- } else { +- items.push(folderItems[k]); +- } +- } +- if (parentItem) items.unshift(parentItem); +- break; +- } +- + items.sort(function(a, b) { + if(a.rdo.type === 'parent' || b.rdo.type === 'parent') { + return -1; +@@ -2103,6 +2024,26 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + } + return tz; + } ++ ++ // handle folders position ++ var folderItems = []; ++ var i = items.length; ++ while(i--) { ++ if(items[i].rdo.type === 'folder') { ++ folderItems.push(items[i]); ++ items.splice(i, 1); ++ } ++ } ++ if(config.options.folderPosition !== 'top') { ++ folderItems.reverse(); ++ } ++ for(var k = 0, fl = folderItems.length; k < fl; k++) { ++ if(config.options.folderPosition === 'top') { ++ items.unshift(folderItems[k]); ++ } else { ++ items.push(folderItems[k]); ++ } ++ } + return items; + }; + +@@ -2120,13 +2061,13 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + type = (typeof type === "undefined") ? "user" : type; + + if(type === 'user') { +- if($.urlParam('config') != 0) { +- url = fm.settings.baseUrl + fm.settings.config.url + $.urlParam('config'); ++ if(_url_.param('config')) { ++ url = fm.settings.baseUrl + '/config/' + _url_.param('config'); + } else { +- url = fm.settings.baseUrl + fm.settings.config.url + 'filemanager.config.json'; ++ url = fm.settings.baseUrl + '/config/filemanager.config.json'; + } + } else { +- url = fm.settings.baseUrl + fm.settings.config.url + 'filemanager.config.default.json'; ++ url = fm.settings.baseUrl + '/config/filemanager.config.default.json'; + } + + return $.ajax({ +@@ -2155,7 +2096,7 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + var loadTemplate = function(id, data) { + return $.ajax({ + type: 'GET', +- url: fm.settings.baseUrl + config.paths.templates + id + '.html', ++ url: fm.settings.baseUrl + '/scripts/templates/' + id + '.html', + error: handleAjaxError + }); + }; +@@ -2294,6 +2235,11 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + return parts.join('/'); + }; + ++ // invert backslashes and remove duplicated ones ++ var normalizePath = function(path) { ++ return path.replace(/\\/g, '/').replace(/\/+/g, '/'); ++ }; ++ + // return filename extension + var getExtension = function(filename) { + if(filename.split('.').length === 1) { +@@ -2488,19 +2434,37 @@ $.richFilemanagerPlugin = function(element, pluginOptions) + } + }; + +- // Create FileTree and bind events +- var createFileTree = function() { +- fmModel.treeModel.loadNodes(null, {'refresh': false}); +- }; ++ // Build FileTree and bind events ++ function prepareFileTree() { ++ if(!config.filetree.enabled) { ++ return; ++ } ++ ++ $filetree.show(); ++ ++ // Provides support for adjustible columns. ++ $splitter.splitter({ ++ sizeLeft: config.filetree.width, ++ minLeft: config.filetree.minWidth, ++ minRight: 200 ++ }); ++ ++ fmModel.treeModel.loadNodes(null, false); ++ } ++ ++ // Build FileTree and bind events ++ function prepareFileView() { ++ fmModel.itemsModel.loadList(fileRoot); ++ } + + // check if plugin instance created inside some context + function hasContext() { + return window.opener // window.open() + || (window.parent && window.self !== window.parent) //