+
+
+-
+-
+-
+- -
+-
+-
![]()
+-
+- ..
+-
+-
++
+
+-
+-
-
+-
+-
++
++
++
++ -
+
+-
![]()
++
![]()
+
+-
+-
+-
+-
+-
++
..
++
++
+
++
++
-
++
++
++
++
![]()
++
++
++
++
++
++
+
+-
+-
+-
+-
+-
+-
+-
+- .. |
+- |
+- |
+- |
+- |
+-
+-
+
+-
+-
+-
+-
+-
+- |
+- |
+- |
+- |
+- |
+-
+-
+-
+-
++
++
++
++
++
++
++
++ .. |
++ |
++ |
++ |
++ |
++
++
++
++
++
++
++
++
++ |
++ |
++ |
++ |
++ |
++
++
++
++
++
+
+
+
+-
+-
+
+
+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) //