From 735d964cac1d587a9f40b1dad6de1da1777becbd Mon Sep 17 00:00:00 2001 From: Jaehyun Sim Date: Tue, 15 Jun 2021 14:28:16 +0900 Subject: [PATCH 1/5] integrate an external Filemanager to TinyMCE v5. --- src/js/filemanager.js | 9 +++++++++ src/js/filemanager.min.js | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/js/filemanager.js b/src/js/filemanager.js index bdf1561d..9590b6ce 100644 --- a/src/js/filemanager.js +++ b/src/js/filemanager.js @@ -3899,6 +3899,15 @@ $.richFilemanagerPlugin = function(element, pluginOptions) } } + // tinymce 5 + if(typeof parent.tinyMCE !== "undefined" && parent.tinyMCE.majorVersion === "5") { + parent.postMessage({mceAction: 'FileSelected', content: previewUrl}); + + setTimeout(function() { + parent.tinyMCE.activeEditor.windowManager.close(); + }, 500); + } + if(_url_.param('ImperaviElementId')) { // use Imperavi Redactor I, tested on v.10.x.x if (window.opener) { diff --git a/src/js/filemanager.min.js b/src/js/filemanager.min.js index 735cd78f..f798b7ac 100644 --- a/src/js/filemanager.min.js +++ b/src/js/filemanager.min.js @@ -1,3 +1,3 @@ -!function(a){a.richFilemanagerPlugin=function(b,c){function d(a){return A.indexOf(a)>-1}function e(b,c){if(!d(c))return!1;if("select"===c&&"folder"===b.type)return!1;if("extract"===c){var e=ka(b.attributes.name);return"file"===b.type&&"zip"===e}return"download"===c&&"folder"===b.type?x.options.allowFolderDownload===!0:"undefined"==typeof b.attributes.capabilities||a.inArray(c,b.attributes.capabilities)>-1}function f(){x.filetree.enabled&&(s.show(),p.splitter({sizeLeft:x.filetree.width,minLeft:x.filetree.minWidth,minRight:200}))}function g(){return window.opener||window.parent&&window.self!==window.parent||window.tinyMCEPopup||I.param("field_name")||I.param("CKEditor")||I.param("ImperaviElementId")}function h(a){return a.attributes.readable?("file"===a.type&&D.previewModel.applyObject(a),void("folder"!==a.type&&"parent"!==a.type||(D.previewFile(!1),D.itemsModel.loadDataList(a.id)))):(k.error(T("NOT_ALLOWED_SYSTEM")),!1)}function i(a){var b=!D.clipboardModel.enabled(),c={select:{name:T("action_select"),className:"select"},download:{name:T("action_download"),className:"download"},rename:{name:T("action_rename"),className:"rename"},move:{name:T("action_move"),className:"move"},separator1:"-----",copy:{name:T("clipboard_copy"),className:"copy"},cut:{name:T("clipboard_cut"),className:"cut"},"delete":{name:T("action_delete"),className:"delete"},extract:{name:T("action_extract"),className:"extract"},copyUrl:{name:T("copy_to_clipboard"),className:"copy-url"}};return e(a,"download")||delete c.download,e(a,"select")&&g()||delete c.select,e(a,"rename")&&x.options.browseOnly!==!0||delete c.rename,e(a,"delete")&&x.options.browseOnly!==!0||delete c["delete"],e(a,"extract")&&x.options.browseOnly!==!0||delete c.extract,e(a,"copy")&&x.options.browseOnly!==!0&&!b||delete c.copy,e(a,"move")&&x.options.browseOnly!==!0&&!b||(delete c.cut,delete c.move),c}var j={baseUrl:".",configUrl:null,config:{},callbacks:{beforeCreateImageUrl:function(a,b){return b},beforeCreatePreviewUrl:function(a,b){return b},beforeSelectItem:function(a,b){return b},afterSelectItem:function(a,b,c){},beforeSetRequestParams:function(a,b){return b},beforeSendRequest:function(a,b){return!0}}},k=this,l=a(b),m=l.children(".fm-wrapper"),n=m.find(".fm-header"),o=n.find(".fm-uploader"),p=m.children(".fm-splitter"),q=m.children(".fm-footer"),r=p.children(".fm-fileinfo"),s=p.children(".fm-filetree"),t=r.find(".view-items-wrapper"),u=r.find(".fm-preview-wrapper"),v=t.find(".view-items"),w=o.children(".fm-upload"),x=null,y="/",z=null,A=[],B=null,C=null,D=null,E=null,F=null,G=null,H=null,I=purl();(new Date).getTime();k.settings=a.extend(!0,j,c),k.write=function(b,c){var d=alertify,e=a.extend({},{reset:!0,delay:5e3,logMaxItems:5,logPosition:"bottom right",logContainerClass:"fm-log",logMessageTemplate:null,parent:document.body,onClick:void 0,unique:!1,type:"log"},c);if(e.logClass&&e.unique&&a(".fm-log").children("."+e.logClass).length>0)return d;e.reset&&d.reset(),d.parent(e.parent),d.logDelay(e.delay),d.logMaxItems(e.logMaxItems),d.logPosition(e.logPosition),d.logContainerClass(e.logContainerClass),d.logMessageTemplate(e.logMessageTemplate),d[e.type](b,e.onClick);var f=d.getLogs();return f[f.length-1]},k.error=function(b,c){return k.write(b,a.extend({},{type:"error",delay:1e4},c))},k.warning=function(b,c){return k.write(b,a.extend({},{type:"warning",delay:1e4},c))},k.success=function(b,c){return k.write(b,a.extend({},{type:"success",delay:6e3},c))},k.alert=function(a){alertify.reset().dialogContainerClass("fm-popup").alert(a)},k.confirm=function(a){alertify.reset().dialogWidth(a.width).dialogPersistent(a.persistent).dialogContainerClass("fm-popup").confirm(a.message,a.okBtn,a.cancelBtn)},k.prompt=function(a){alertify.reset().dialogWidth(a.width).dialogPersistent(a.persistent).dialogContainerClass("fm-popup").theme(a.template).prompt(a.message,a.value||"",a.okBtn,a.cancelBtn)},k.dialog=function(a){alertify.reset().dialogWidth(a.width).dialogPersistent(a.persistent).dialogContainerClass("fm-popup").dialog(a.message,a.buttons)},k.setDimensions=function(){var b=m.outerHeight(!0)-m.height(),c=a(window).height()-n.height()-q.height()-b,d=p.width()-p.children(".splitter-bar-vertical").outerWidth()-s.outerWidth();p.height(c),r.width(d)},k.console=function(){x.options.logger&&arguments&&[].unshift.call(arguments,(new Date).getTime())},k.refreshFolder=function(a){D.loadPath(D.currentPath(),a)},k.loadFolder=function(a,b){a="/"+ea(a,"/")+"/",D.loadPath(a,b)};var J=function(){var b=a.Deferred();b.then(function(){return K()}).then(function(){return M()}).then(function(a,b){return L()}).then(function(){return N()}).then(function(){O(function(){P()})}),b.resolve()},K=function(){return a.when(W("default"),W("user")).done(function(b,c){var d=b[0],e=c[0];if(void 0!==e&&null!==e&&delete e.version,x=a.extend({},d,e),x.api.connectorUrl)z=x.api.connectorUrl;else{var f=location.origin+location.pathname,g="connectors/"+x.api.lang+"/filemanager."+x.api.lang;ka(f).length>0&&(f=f.substring(0,f.lastIndexOf("/")+1)),z=f+g}})},L=function(){return za("GET",{mode:"initiate"}).done(function(b){if(b.data){var c=b.data.attributes.config;a.each(c,function(b,c){a.each(c,function(a,c){return null===c||(void 0===x[b]&&(x[b]=[]),void(x[b][a]=c))})}),c.options&&c.options.capabilities&&(x.options.capabilities=c.options.capabilities)}}).fail(function(a){k.error("Unable to perform initial request to server."),ba(a)}).then(function(b){if(b.errors)return a.Deferred().reject()})},M=function(){return E=new Q,a.ajax().then(function(){var a=I.param("langCode");return a?V(E.buildLangFileUrl(a)).done(function(){E.setLang(a)}).fail(function(){setTimeout(function(){k.error("Given language file ("+E.buildLangFileUrl(a)+") does not exist!")},500)}):void E.setLang(x.language["default"])}).then(function(){var b=E.buildLangFileUrl(E.getLang())+"?_="+(new Date).getTime();return a.ajax({type:"GET",url:b,dataType:"json"}).done(function(a){E.setTranslations(a)})}).then(function(){var b=E.getLang().substr(0,2),c=k.settings.baseUrl;return a.when(a.get(c+"/libs/cldrjs/cldr-dates/"+b+"/ca-gregorian.json"),a.get(c+"/libs/cldrjs/cldr-numbers/"+b+"/numbers.json"),a.get(c+"/libs/cldrjs/cldr-core/supplemental/likelySubtags.json"),a.get(c+"/libs/cldrjs/cldr-core/supplemental/timeData.json"),a.get(c+"/libs/cldrjs/cldr-core/supplemental/weekData.json")).fail(function(){k.error('CLDR files for "'+b+'" language do not exist!')}).then(function(){return[].slice.apply(arguments,[0]).map(function(a){return a[0]})}).then(Globalize.load).then(function(){F=Globalize(b)})})},N=function(){return a.when(Y("upload-container"),Y("upload-item")).done(function(a,b){var c=a[0],d=b[0];m.append(c).append(d)})},O=function(a){var b=[],c=[];if(b.push("/themes/"+x.options.theme+"/styles/theme.css"),x.viewer.image.lazyLoad&&b.push("/libs/lazyload/dist/lazyload.min.js"),x.customScrollbar.enabled&&(b.push("/libs/custom-scrollbar-plugin/jquery.mCustomScrollbar.min.css"),b.push("/libs/custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js")),b.push(a),X(b),x.editor.enabled){var d=x.editor.theme;d&&"default"!==d&&c.push("/libs/CodeMirror/theme/"+d+".css"),c.push("/libs/CodeMirror/lib/codemirror.css"),c.push("/libs/CodeMirror/lib/codemirror.js"),c.push("/libs/CodeMirror/addon/selection/active-line.js"),c.push("/libs/CodeMirror/addon/display/fullscreen.css"),c.push("/libs/CodeMirror/addon/display/fullscreen.js")}x.viewer.markdownRenderer.enabled&&(c.push("/src/css/fm-markdown.css"),c.push("/libs/markdown-it/markdown-it.min.js"),c.push("/libs/markdown-it/default.min.css"),c.push("/libs/markdown-it/highlight.min.js"),c.push("/libs/markdown-it/markdown-it-footnote.min.js"),c.push("/libs/markdown-it/markdown-it-replace-link.min.js")),x.options.browseOnly||(c.push("/src/js/libs-fileupload.js"),x.upload.multiple&&c.push("/libs/jQuery-File-Upload/css/dropzone.css")),c.length&&X(c)},P=function(){G=new R,A=x.options.capabilities||["upload","select","download","rename","copy","move","delete","extract","createFolder"],x.security.readOnly&&(x.options.browseOnly=!0),x.upload.paramName||(x.upload.paramName="files");var b=[];x.options.fileSorting&&(b=x.options.fileSorting.toLowerCase().split("_")),B=b[0]||"name",C=b[1]||"asc";var c=I.param("exclusiveFolder");c&&(y="/"+c+"/",y=ja(y));var e=I.param("expandedFolder");if(e&&(H=y+e+"/",H=ja(H)),D=new S,ko.applyBindings(D),D.itemsModel.initiateLazyLoad(),D.filterModel.setName(I.param("filter")),ko.bindingHandlers.toggleNodeVisibility={init:function(b,c){var d=c();a(b).toggle(d.isExpanded())},update:function(b,c){var d=c();return d.isSliding()!==!1&&(d.isExpanded()===!1&&a(b).slideDown(x.filetree.expandSpeed,function(){d.isSliding(!1),d.isExpanded(!0)}),void(d.isExpanded()===!0&&a(b).slideUp(x.filetree.expandSpeed,function(){d.isSliding(!1),d.isExpanded(!1)})))}},ko.bindingHandlers.draggableView={init:function(a,b,c){D.ddModel.makeDraggable(b(),a)}},ko.bindingHandlers.droppableView={init:function(a,b,c){D.ddModel.makeDroppable(b(),a)}},ko.bindingHandlers.draggableTree={init:function(a,b,c){D.ddModel.makeDraggable(b(),a)}},ko.bindingHandlers.droppableTree={init:function(a,b,c){D.ddModel.makeDroppable(b(),a)}},m.mousewheel(function(b){if(!D.ddModel.dragHelper)return!0;var c,d=null;if(c=x.customScrollbar.enabled?a([t[0],s[0]]):p.children(".splitter-pane"),c.each(function(c){var e=a(this),f=e.offset().top,g=e.offset().left;if(b.offsetY>=f&&b.offsetY<=f+e.height()&&b.offsetX>=g&&b.offsetX<=g+e.width())return d=e,!1}),null===d)return!1;if(x.customScrollbar.enabled){var e=d.find(".mCSB_scrollTools_vertical"),f=1===b.deltaY?"+":"-";e.is(":visible")&&d.mCustomScrollbar("scrollTo",[f+"=250",0],{scrollInertia:500,scrollEasing:"easeOut",callbacks:!0})}else if(d[0].scrollHeight>d[0].clientHeight){var g=d.scrollTop(),h=g-200*b.deltaY;D.ddModel.isScrolling=!0,h=h<0?0:h,d.stop().animate({scrollTop:h},100,"linear",function(){D.ddModel.isScrolling=!1,D.ddModel.isScrolled=!0})}}),v.selectable({filter:"li:not(.directory-parent), tbody > tr:not(.directory-parent)",cancel:".directory-parent, thead",disabled:!x.manager.selection.enabled,appendTo:v,start:function(a,b){Ga(),D.itemsModel.isSelecting(!0)},stop:function(a,b){D.itemsModel.isSelecting(!1)},selected:function(a,b){var c=ko.dataFor(b.selected);c.selected(!0)},unselected:function(a,b){var c=ko.dataFor(b.unselected);c.selected(!1)}}),r.contextMenu({selector:".view-items",zIndex:10,build:function(b,c){var e={createFolder:{name:T("create_folder"),className:"create-folder"},paste:{name:T("clipboard_paste"),className:"paste",disabled:function(a,b){return D.clipboardModel.isEmpty()}}};return D.clipboardModel.enabled()&&x.options.browseOnly!==!0||delete e.paste,d("createFolder")&&x.options.browseOnly!==!0||delete e.createFolder,!a.isEmptyObject(e)&&{appendTo:".fm-container",items:e,reposition:!1,callback:function(a,b){switch(a){case"createFolder":D.headerModel.createFolder();break;case"paste":D.clipboardModel.paste()}}}}}),x.extras.extra_js)for(var g=0;g400&&(this.yStartPosition=this.mcs.top),D.itemsModel.isSelecting()&&D.itemsModel.continiousSelection(!0);var b=Math.abs(this.mcs.top)-Math.abs(this.yStartPosition);v.selectable("repositionCssHelper",b,0)}D.itemsModel.lazyLoad&&D.itemsModel.lazyLoad.handleScroll()}},axis:"y",alwaysShowScrollbar:0}));var h=document.documentElement;if(h.setAttribute("data-useragent",navigator.userAgent),x.options.logger){(new Date).getTime()}var i=l.find(".fm-loading-wrap");i.fadeOut(800,function(){k.setDimensions()}),k.setDimensions()},Q=function(){var a=null,b={},c=k.settings.baseUrl+"/languages/";this.buildLangFileUrl=function(a){return c+a+".json"},this.setLang=function(b){a=b},this.getLang=function(){return a},this.setTranslations=function(a){b=a},this.getTranslations=function(){return b},this.translate=function(a){return b[a]}},R=function(){var a={},b=this;this.push=function(c,d,e){b.removeTimer(c),a[c]=setTimeout(d,e)},this.getTimer=function(b){return a[b]},this.removeTimer=function(b){a[b]&&(clearTimeout(a[b]),delete a[b])}},S=function(){function b(a){return(!x.manager.selection.enabled||!x.manager.selection.useCtrlKey||a.ctrlKey!==!0)&&(!x.manager.dblClickOpen||"click"!==a.type)}var c=this;this.config=ko.observable(x),this.loadingView=ko.observable(!0),this.previewFile=ko.observable(!1),this.viewMode=ko.observable(x.manager.defaultViewMode),this.currentPath=ko.observable(y),this.browseOnly=ko.observable(x.options.browseOnly),this.previewModel=ko.observable(null),this.currentLang=E.getLang(),this.lg=E.getTranslations(),this.previewFile.subscribe(function(a){a||(c.previewModel.closeEditor(),c.itemsModel.descriptivePanel.rdo().id===c.previewModel.rdo().id&&c.itemsModel.descriptivePanel.render(c.previewModel.viewer.content()))}),this.isCapable=function(a){return d(a)},this.loadPath=function(a,b){var d,e=new s(a);b&&(d=D.treeModel.findByParam("id",a)),d&&e.setPreloader(D.treeModel.getPreloader(d)),e.setPreloader(c.itemsModel.getPreloader()).setDataHandler(function(a,b){d&&D.treeModel.addNodes(a,d,!0),c.itemsModel.addItems(a,b,!0),c.searchModel.clearInput()}).load(function(){return Ra(a)})},this.addElements=function(a,b,d){var e=c.treeModel.findByParam("id",b);e&&c.treeModel.addNodes(a,e,d),c.currentPath()===b&&c.itemsModel.addItems(a,b,d)},this.removeElement=function(a){var b=c.treeModel.findByParam("id",a.id);b&&b.remove();var d=c.itemsModel.findByParam("id",a.id);d&&d.remove()},this.fetchSelectedItems=function(a){var b,d;if(a===q.name)return c.itemsModel.getSelected();if(a===o.name)return c.treeModel.getSelected();if(!a)return b=c.treeModel.getSelected(),d=c.itemsModel.getSelected(),d.length>0?d:b;throw new Error("Unknown item type.")},this.fetchSelectedObjects=function(b){var d=[];return a.each(c.fetchSelectedItems(b.constructor.name),function(a,b){d.push(b.rdo)}),d};var f=function(){this.beforeLoad=function(a){},this.afterLoad=function(a,b){}},j=function(){var a=this,b=null;this.rdo=ko.observable({}),this.cdo=ko.observable({}),this.viewer={type:ko.observable("default"),isEditable:ko.observable(!1),url:ko.observable(null),pureUrl:ko.observable(null),options:ko.observable({}),content:ko.observable(null),codeMirror:ko.observable(null)},this.renderer=new L,this.editor=new M,this.rdo.subscribe(function(b){a.cdo({isFolder:"folder"===b.type,sizeFormatted:Z(b.attributes.size),createdFormatted:$(b.attributes.created),modifiedFormatted:$(b.attributes.modified),extension:"file"===b.type?ka(b.id):null,dimensions:b.attributes.width?b.attributes.width+"x"+b.attributes.height:null})}),this.editor.content.subscribe(function(b){a.editor.isInteractive()&&a.renderer.render(b)}),this.applyObject=function(d){b&&b.destroy(),c.previewFile(!1);var e=d.attributes.name,f={interactive:!1},g={type:"default",url:null,options:{}};a.rdo(d),qa(e)&&(g.type="image",g.url=Ca(d,!1,!0)),sa(e)&&x.viewer.audio.enabled===!0&&(g.type="audio",g.url=Ba(d,!0)),ra(e)&&x.viewer.video.enabled===!0&&(g.type="video",g.url=Ba(d,!0),g.options={width:x.viewer.video.playerWidth,height:x.viewer.video.playerHeight}),ua(e)&&x.viewer.opendoc.enabled===!0&&(g.type="opendoc",g.url=k.settings.baseUrl+"/libs/ViewerJS/index.html#"+Ba(d,!0),g.options={width:x.viewer.opendoc.readerWidth,height:x.viewer.opendoc.readerHeight}),va(e)&&x.viewer.google.enabled===!0&&(g.type="google",g.url="https://docs.google.com/viewer?url="+encodeURIComponent(Ba(d,!1))+"&embedded=true",g.options={width:x.viewer.google.readerWidth,height:x.viewer.google.readerHeight}),ta(e)&&x.viewer.iframe.enabled===!0&&(g.type="iframe",g.url=Ba(d,!0),g.options={width:x.viewer.iframe.readerWidth,height:x.viewer.iframe.readerHeight}),(wa(e)&&x.viewer.codeMirrorRenderer.enabled===!0||xa(e)&&x.viewer.markdownRenderer.enabled===!0)&&(g.type="renderer",g.options={is_writable:d.attributes.writable},a.renderer.setRenderer(d),f.interactive=a.renderer.renderer().interactive),a.viewer.type(g.type),a.viewer.url(g.url),a.viewer.options(g.options),a.viewer.pureUrl(Ea(d)),a.viewer.isEditable(pa(e)&&x.editor.enabled===!0),a.editor.isInteractive(f.interactive),"renderer"===g.type||a.viewer.isEditable()?Qa(d).then(function(b){a.viewer.content(b),c.previewFile(!0)}):c.previewFile(!0)},this.afterRender=function(){a.renderer.render(a.viewer.content());var c=u.find(".btn-copy-url")[0];b=new Clipboard(c),b.on("success",function(a){k.success(T("copied"))})},this.initiateEditor=function(b){var c=u.find(".fm-cm-editor-content")[0];a.editor.createInstance(a.cdo().extension,c,{readOnly:!1,styleActiveLine:!0})},this.bindToolbar=function(b){e(a.rdo(),b)&&Xa(b,{},a.rdo())},this.previewIconClass=ko.pureComputed(function(){var b=[],c=["ico"];return"default"!==a.viewer.type()&&a.viewer.url()||(b.push("grid-icon"),this.cdo().isFolder===!0?(b.push("ico_folder"),c.push("folder"),this.rdo().attributes.readable||c.push("lock")):(b.push("ico_file"),this.rdo().attributes.readable?c.push("ext",this.cdo().extension):c.push("file","lock")),b.push(c.join("_"))),b.join(" ")},this),this.closePreview=function(){c.previewFile(!1)},this.editFile=function(){var b=a.viewer.content();a.renderer.render(b),a.editor.render(b)},this.saveFile=function(){Pa(a.rdo())},this.closeEditor=function(){a.editor.enabled(!1),a.renderer.render(a.viewer.content())},this.buttonVisibility=function(b){switch(b){case"select":return e(a.rdo(),b)&&g();case"move":case"rename":case"delete":case"download":return e(a.rdo(),b)}}},n=function(){function b(a){if(null!==H){a||(a=d.rootNode);var b=d.findByFilter(function(a){return 0===H.indexOf(a.id)},a);b?(x.filetree.expandSpeed=10,d.loadDataNode(b,!1,!0)):(H=null,x.filetree.expandSpeed=200,d.setItemsFromNode(a))}}var d=this;this.selectedNode=ko.observable(null);var e=new o({attributes:{}});e.id=y,e.level=ko.observable(-1),this.rootNode=e,this.mapNodes=function(a,b){b||(b=d.rootNode),b.isRoot()||a.call(this,b);var c=b.children();if(!c||0===c.length)return null;for(var e=0,f=c.length;e0,this.closeButtonOnClick=function(){k.console("CLOSE button is clicked")},this.navHome=function(){c.previewFile(!1),c.itemsModel.loadDataList(y)},this.navLevelUp=function(){var a=c.previewFile()?ma(c.previewModel.rdo().id):na(c.currentPath());c.previewFile()&&c.previewFile(!1),a!==c.currentPath()&&c.itemsModel.loadDataList(a)},this.navRefresh=function(){c.previewFile()?(c.previewFile(!1),c.previewFile(!0)):c.itemsModel.loadDataList(c.currentPath())},this.displayGrid=function(){c.viewMode("grid"),c.previewFile(!1),c.itemsModel.lazyLoad&&c.itemsModel.lazyLoad.update()},this.displayList=function(){c.viewMode("list"),c.previewFile(!1)},this.switchLang=function(b){var c=b.target.value,d=E.getLang();if(c&&c.toLowerCase()!==d.toLowerCase()){var e,f=window.location.toString(),g=new RegExp("(langCode=)"+d);e=g.test(f)?f.replace(g,"$1"+c):f+(a.isEmptyObject(I.param())?"?":"#")+"langCode="+c,window.location.href=e}},this.createFolder=function(){function a(a,b){var c=b.getInputValue();return c?void za("GET",{mode:"addfolder",path:D.currentPath(),name:c}).done(function(a){a.data&&(D.addElements(a.data,D.currentPath()),b.closeDialog(),x.options.showConfirmation&&k.success(T("successful_added_folder")))}).fail(ba):void k.error(T("no_foldername"))}return d("createFolder")?void k.prompt({message:T("prompt_foldername"),value:T("default_foldername"),okBtn:{label:T("create_folder"),autoClose:!1,click:a},cancelBtn:{label:T("cancel")}}):(k.error(T("NOT_ALLOWED")),!1)}},z=function(){this.files=ko.observable(null),this.folders=ko.observable(null),this.size=ko.observable(null),this.enabled=ko.observable(!1),this.doSummarize=function(){Ua()}},A=function(){var b=this;this.name=ko.observable(null),this.setName=function(c){c&&x.filter&&a.isArray(x.filter[c])&&b.name(c)},this.getExtensions=function(){return b.name()?x.filter[b.name()]:null},this.filterItem=function(c){var d=b.getExtensions(),e=!c.cdo.hiddenBySearch;if(c.cdo.hiddenByType=!1,"file"===c.rdo.type&&a.isArray(d)){var f=ka(c.id),g=d.indexOf(f)!==-1;e=e&&g,c.cdo.hiddenByType=!g}c.visible(e)},this.filter=function(d){b.setName(d),a.each(c.itemsModel.objects(),function(a,c){b.filterItem(c)}),c.treeModel.mapNodes(function(a){b.filterItem(a)}),c.itemsModel.lazyLoad&&c.itemsModel.lazyLoad.update()},this.reset=function(){b.name(null),b.filter(null)}},F=function(){function b(){h?G.push("search",function(){d()},x.search.typingDelay):d()}function d(){var b=f.value(),d=x.search.caseSensitive?b:b.toLowerCase();if(""===b)return void(b!==g?e():k.warning(T("search_string_empty")));if(x.search.recursive){var h=c.currentPath(),i=new s(h);i.setPreloader(c.itemsModel.getPreloader()).setDataHandler(function(b,e){var g=[];x.search.caseSensitive?a.each(b,function(a,b){0===b.attributes.name.indexOf(d)&&g.push(b)}):g=b;var h=c.itemsModel.createItems(g);c.itemsModel.setItemsList(h),f.isRendered(!0)}).load(function(){return Sa(h,b)})}else a.each(c.itemsModel.objects(),function(a,b){var c=b.rdo.attributes.name;x.search.caseSensitive||(c=c.toLowerCase());var e=0===c.indexOf(d),f=!b.cdo.hiddenByType;f=f&&e,b.cdo.hiddenBySearch=!e,b.visible(f)}),f.isRendered(!0)}function e(){f.clearInput(),x.search.recursive?c.itemsModel.loadDataList(c.currentPath()):a.each(c.itemsModel.objects(),function(a,b){b.cdo.hiddenBySearch=!1,b.visible(!b.cdo.hiddenByType)})}var f=this,g="",h=!!x.search.typingDelay;this.value=ko.observable(""),this.isRendered=ko.observable(!1),this.value.subscribe(function(a){g=a},null,"beforeChange"),this.inputKeyUp=function(a,c){var d=c.which||c.keyCode,e=[16,17,18,27,37,38,39,40];if(h){if(e.indexOf(d)>-1)return;f.value(c.target.value)}(h||13===d)&&b()},this.seekItems=function(a,c){b()},this.reset=function(a,b){e()},this.clearInput=function(){g="",f.value(""),f.isRendered(!1),G.removeTimer("search")}},J=function(){function a(){e=[],b=null,f.itemsNum(0)}var b=null,e=[],f=this,g=d("copy")&&d("move");this.itemsNum=ko.observable(0),this.enabled=ko.observable(c.config().clipboard.enabled&&g),this.copy=function(){f.hasCapability("copy")&&(b="copy",e=c.fetchSelectedItems(),f.itemsNum(e.length))},this.cut=function(){f.hasCapability("cut")&&(b="cut",e=c.fetchSelectedItems(),f.itemsNum(e.length))},this.paste=function(){var d=c.currentPath();if(f.hasCapability("paste")&&!f.isEmpty())return null===b||0===e.length?void k.warning(T("clipboard_empty")):void Fa(e,function(a,c){return"cut"===b?La(c,d):"copy"===b?Ka(c,d):void 0},a)},this.clear=function(){f.hasCapability("clear")&&!f.isEmpty()&&(a(),k.success(T("clipboard_cleared")))},this.isEmpty=function(){return 0===e.length},this.hasCapability=function(a){if(!f.enabled)return!1;switch(a){case"copy":return d("copy");case"cut":return d("move");default:return!0}}},K=function(){var a=this;this.items=ko.observableArray([]),this.clean=function(){a.items([]),a.add(y,"")},this.add=function(c,d){a.items.push(new b(c,d))},this.splitPath=function(b){var c=y,d=b.replace(new RegExp("^"+y),"").split("/");for(a.clean();d.length>0;){var e=d.shift();e&&(c+=e+"/",a.add(c,e))}},this.splitCurrent=function(){a.splitPath(c.currentPath())},this.getLabel=ko.pureComputed(function(){var a=T(c.searchModel.isRendered()?"search_results":"current_folder");return a+": "},this);var b=function(a,b){var d=this;this.path=a,this.label=b,this.isRoot=a===y,this.active=a===c.currentPath(),this.itemClass=function(){var a=["nav-item"];return d.isRoot&&a.push("root"),d.active&&a.push("active"),a.join(" ")},this["goto"]=function(a,b){a.active||c.itemsModel.loadDataList(a.path)}}},L=function(){function b(a){return xa(a)?new f:wa(a)?new e:void 0}var c,d=this;this.rdo=ko.observable({}),this.content=ko.observable(null),this.renderer=ko.observable(null),this.render=function(a){d.renderer()&&d.renderer().processContent(a)},this.setRenderer=function(a){d.rdo(a),d.renderer(b(a.attributes.name))},this.setContainer=function(b){a.each(b,function(){if(a(this).hasClass("fm-renderer-container"))return c=a(this),!1}),d.renderer().processDomElements(c)};var e=function(){this.name="codeMirror",this.interactive=!1;var a=new M;this.processContent=function(b){a.render(b),d.content(b)},this.processDomElements=function(b){if(!a.instance){var c=b.find(".fm-cm-renderer-content")[0],e=ka(d.rdo().id);a.createInstance(e,c,{readOnly:"nocursor",styleActiveLine:!1,lineNumbers:!1})}}},f=function(){function b(){c.find("a").each(function(){var b=a(this).attr("href"),c=D.previewModel.editor;if(c.enabled()&&c.isInteractive())a(this).off("click"),a(this).on("click",function(){return!1});else{if(b.search("://")!==-1||ha(b,"mailto:"))return;xa(b)&&a(this).on("click",function(a){return Ta(b).then(function(a){a.data&&h(a.data)}),!1})}})}this.name="markdown",this.interactive=!0;var e=window.markdownit({html:!0,linkify:!0,typographer:!0,highlight:function(a,b){if(b&&hljs.getLanguage(b))try{return'
'+hljs.highlight(b,a,!0).value+"
"}catch(c){}return'
'+e.utils.escapeHtml(a)+"
"},replaceLink:function(a,b){if(a.search("://")!==-1||ha(a,"mailto:"))return a;var c=ha(a,"/")?y:ma(d.rdo().id),e=c+fa(a,"/");if(xa(e))return e;var f=ya("GET",{mode:"readfile",path:e});return Aa(f)}}).use(window.markdownitReplaceLink);this.processContent=function(a){var c=e.render(a);d.content(c),b()},this.processDomElements=function(a){}}},M=function(){function b(a){d.enabled(!0),d.instance.setValue(a),setTimeout(function(){d.instance.refresh()},0)}function c(a){var b=[],c="default";x.editor.codeHighlight&&("js"===a&&(b.push("/libs/CodeMirror/mode/javascript/javascript.js"),c="javascript"),"css"===a&&(b.push("/libs/CodeMirror/mode/css/css.js"),c="css"),"html"===a&&(b.push("/libs/CodeMirror/mode/xml/xml.js"),c="text/html"),"xml"===a&&(b.push("/libs/CodeMirror/mode/xml/xml.js"),c="application/xml"),"php"===a&&(b.push("/libs/CodeMirror/mode/htmlmixed/htmlmixed.js"),b.push("/libs/CodeMirror/mode/xml/xml.js"),b.push("/libs/CodeMirror/mode/javascript/javascript.js"),b.push("/libs/CodeMirror/mode/css/css.js"),b.push("/libs/CodeMirror/mode/clike/clike.js"),b.push("/libs/CodeMirror/mode/php/php.js"),c="application/x-httpd-php"),"java"===a&&(b.push("/libs/CodeMirror/mode/clike/clike.js"),c="text/x-java"),"sql"===a&&(b.push("/libs/CodeMirror/mode/sql/sql.js"),c="text/x-mysql"),"md"===a&&(b.push("/libs/CodeMirror/addon/mode/overlay.js"),b.push("/libs/CodeMirror/mode/xml/xml.js"),b.push("/libs/CodeMirror/mode/markdown/markdown.js"),b.push("/libs/CodeMirror/mode/gfm/gfm.js"),b.push("/libs/CodeMirror/mode/javascript/javascript.js"),b.push("/libs/CodeMirror/mode/css/css.js"),b.push("/libs/CodeMirror/mode/htmlmixed/htmlmixed.js"),b.push("/libs/CodeMirror/mode/clike/clike.js"),b.push("/libs/CodeMirror/mode/shell/shell.js"),b.push("/libs/CodeMirror/mode/meta.js"),c="gfm"),"sh"===a&&(b.push("/libs/CodeMirror/addon/mode/overlay.js"),b.push("/libs/CodeMirror/mode/markdown/markdown.js"),b.push("/libs/CodeMirror/mode/gfm/gfm.js"),b.push("/libs/CodeMirror/mode/javascript/javascript.js"),b.push("/libs/CodeMirror/mode/css/css.js"),b.push("/libs/CodeMirror/mode/htmlmixed/htmlmixed.js"),b.push("/libs/CodeMirror/mode/clike/clike.js"),b.push("/libs/CodeMirror/mode/meta.js"),b.push("/libs/CodeMirror/mode/shell/shell.js"),c="shell")),b.length?(b.push(function(){d.mode(c)}),X(b)):d.mode(c)}var d=this,e=null;this.instance=null,this.enabled=ko.observable(!1),this.content=ko.observable(null),this.mode=ko.observable(null),this.isInteractive=ko.observable(!1),this.mode.subscribe(function(a){a&&(d.instance.setOption("mode",a),e&&(b(e),e=null))}),this.render=function(a){d.mode()?b(a):e=a},this.createInstance=function(b,e,f){var g,h={readOnly:"nocursor",styleActiveLine:!1,viewportMargin:1/0,lineNumbers:x.editor.lineNumbers,lineWrapping:x.editor.lineWrapping,theme:x.editor.theme,matchBrackets:x.editor.matchBrackets,extraKeys:{F11:function(a){a.setOption("fullScreen",!a.getOption("fullScreen"))},Esc:function(a){a.getOption("fullScreen")&&a.setOption("fullScreen",!1)}}};g=CodeMirror.fromTextArea(e,a.extend({},h,f)),g.on("changes",function(a,b){d.content(a.getValue())}),d.instance=g,c(b)}},N=function(){function b(b){var c=a.grep(f.items,function(a,c){if("folder"===b.rdo.type||"parent"===b.rdo.type){if(ha(b.rdo.id,a.rdo.id))return!0;if(b.rdo.id===oa(a.rdo.id))return!0}return a.id===b.id});return b.rdo.attributes.writable&&0===c.length}function d(a){null!==f.hoveredItem&&f.hoveredItem.dragHovered(!1),f.hoveredItem=a,a&&a.dragHovered(!0)}function e(a,b){b?a.addClass(g):a.removeClass(g)}var f=this,g="drop-restricted",h=a("#drag-helper-template");this.items=[],this.hoveredItem=null,this.dragHelper=null,this.isScrolling=!1,this.isScrolled=!1,this.hoveredCssClass="drop-hover",this.makeDraggable=function(b,d){"file"!==b.rdo.type&&"folder"!==b.rdo.type||a(d).draggable({distance:3,cursor:"pointer",cursorAt:{left:Math.floor(h.width()/2),bottom:15},scroll:!1,appendTo:m,containment:l,refreshPositions:!1,helper:function(){var a,d;return d=c.fetchSelectedItems(b.constructor.name).length>1?"ico_multiple":"folder"===b.rdo.type?"ico_folder":"ico_file ico_ext_"+ka(b.rdo.id),a=h.children(".drag-helper").clone(),a.find(".clip").addClass(d),f.dragHelper=a,a},start:function(a,d){f.items=c.fetchSelectedItems(b.constructor.name)},drag:function(b,c){a(this).draggable("option","refreshPositions",f.isScrolling||f.isScrolled),f.isScrolled=!1},stop:function(a,b){f.items=[],f.dragHelper=null}})},this.makeDroppable=function(c,g){"folder"!==c.rdo.type&&"parent"!==c.rdo.type||a(g).droppable({tolerance:"pointer",enableExtendedEvents:c instanceof q,accept:function(a){var b=ko.dataFor(a[0]),c=b?b.rdo.type:null;return"file"===c||"folder"===c},over:function(a,f){setTimeout(function(){d(null),e(f.helper,!1),b(c)||e(f.helper,!0),d(c)},0)},out:function(a,b){d(null),e(b.helper,!1)},drop:function(a,e){return d(null),!!b(c)&&void Fa(f.items,function(a,b){return La(b.rdo,c.id)})}})}},O=function(){this.unselect=!1};this.treeModel=new n,this.itemsModel=new p,this.tableViewModel=new t,this.previewModel=new j,this.headerModel=new w,this.summaryModel=new z,this.filterModel=new A,this.searchModel=new F,this.clipboardModel=new J,this.breadcrumbsModel=new K,this.ddModel=new N,this.selectionModel=new O},T=function(a){return E.translate(a)},U=function(a){function b(a){var b,c=B;switch("list"===D.viewMode()&&(c=D.itemsModel.listSortField()),c){case"type":b=a.cdo.extension||"";break;case"size":b=a.rdo.attributes.size;break;case"modified":b=a.rdo.attributes.modified;break;case"dimensions":b=a.cdo.dimensions||"";break;default:b=a.rdo.attributes.name}return"string"==typeof b&&(f.cases||(b=b.toLowerCase()),b=b.replace(/\s+/g," ")),b}function c(a,b){for(var c=d(a.toString()),e=d(b.toString()),f=0;c[f]&&e[f];f++)if(c[f]!==e[f]){var g=Number(c[f]),h=Number(e[f]);return g==c[f]&&h==e[f]?g-h:c[f]>e[f]?1:-1}return c.length-e.length}function d(a){for(var b,c,d=[],e=0,f=-1,g=0;b=(c=a.charAt(e++)).charCodeAt(0);){var h=46==b||b>=48&&b<=57;h!==g&&(d[++f]="",g=h),d[f]+=c}return d}var e="list"===D.viewMode()?D.itemsModel.listSortOrder():C,f={natural:!0,order:"asc"===e?1:-1,cases:!1};a.sort(function(a,d){var e,g=b(a),h=b(d);return e=g===h?0:void 0===g||void 0===h?0:f.natural&&(isNaN(g)||isNaN(h))?c(g,h):gh?1:0,e*=f.order});for(var g=[],h=a.length;h--;)"folder"===a[h].rdo.type&&(g.push(a[h]),a.splice(h,1));"top"!==x.options.folderPosition&&g.reverse();for(var i=0,j=g.length;i1&&(e=k.write(g.getMessage(),{delay:0,logMessageTemplate:function(a){var b=(g.getProgress(),g.isProcessed()?"striped":"striped animated");return"
"+a+'
'+g.getProgress()+'%
'}}),e.stick(!0)),a.each(b,function(a,b){h=h.then(function(){return c(a,b)}).then(function(a){a&&a.data?g.succeeded():g.failed(),e&&e.setMessage(g.getMessage())})}),h.then(function(){e&&g.isProcessed()&&(e.stick(!1),setTimeout(function(){e.remove()},6e3))}),h.then(function(){"function"==typeof d&&d()})},Ga=function(){if(document.selection&&document.selection.empty)document.selection.empty();else if(window.getSelection){var a=window.getSelection();a.removeAllRanges()}},Ha=function(a){var b=null,c=Ba(a,!0);if(c=k.settings.callbacks.beforeSelectItem(a,c),window.tinyMCEPopup){var d=tinyMCEPopup.getWindowArg("window");return d.document.getElementById(tinyMCEPopup.getWindowArg("input")).value=c,"undefined"!=typeof d.ImageDialog&&(d.ImageDialog.getImageData&&d.ImageDialog.getImageData(),d.ImageDialog.showPreviewImage&&d.ImageDialog.showPreviewImage(c)),void tinyMCEPopup.close()}if(I.param("field_name")&&(parent.document.getElementById(I.param("field_name")).value=c,"undefined"!=typeof parent.tinyMCE&&parent.tinyMCE.activeEditor.windowManager.close(),"undefined"!=typeof parent.$.fn.colorbox&&parent.$.fn.colorbox.close()),I.param("ImperaviElementId"))if(window.opener);else{var e=I.param("ImperaviElementId"),f=parent.$("#"+e).redactor("core.getObject");f&&(f.modal.close(),f.buffer.set(),qa(a.attributes.name)?f.insert.html(''):f.insert.html(''+a.attributes.name+""))}if(I.param("CKEditor")&&(window.opener?window.opener.CKEDITOR.tools.callFunction(I.param("CKEditorFuncNum"),c):(parent.CKEDITOR.tools.callFunction(I.param("CKEditorFuncNum"),c),parent.CKEDITOR.tools.callFunction(I.param("CKEditorCleanUpFuncNum")))),window.opener&&"function"==typeof window.opener.SetUrl)if(a.attributes.width){var g=c,h=a.attributes.width,i=a.attributes.height;window.opener.SetUrl(g,h,i)}else window.opener.SetUrl(c);window.opener&&(b=window.opener),window.parent&&window.self!==window.parent&&(b=window.parent),b&&b.postMessage({source:"richfilemanager",resourceObject:a,preview_url:c},"*"),k.settings.callbacks.afterSelectItem(a,c,b)},Ia=function(a){var b=function(b,c){var d=a.id,e=c.getInputValue();if(!e)return void k.error(T("new_filename"));if(!x.options.allowChangeExtensions){var f=ka(a.attributes.name);f.length>0&&(e=e+"."+f)}if(da(d)&&!ca(e)){var g="

"+T("INVALID_FILE_TYPE")+"

";return"ALLOW_LIST"===x.security.extensions.policy&&(g+="

"+T("ALLOWED_FILE_TYPE").replace("%s",x.security.extensions.restrictions.join(", "))+".

"),"DISALLOW_LIST"===x.security.extensions.policy&&(g+="

"+T("DISALLOWED_FILE_TYPE").replace("%s",x.security.extensions.restrictions.join(", "))+".

"),void k.error(g)}za("GET",{mode:"rename",old:d,"new":e}).done(function(a){if(a.data){var b=a.data,e=D.treeModel.findByParam("id",d);if(e&&("folder"===e.rdo.type&&(e.nodeTitle(b.attributes.name),D.treeModel.actualizeNodeObject(e,d,b.id)),"file"===e.rdo.type)){var f=e.parentNode(),g=D.treeModel.createNode(b);e.remove(),f&&D.treeModel.appendNodes(f,g)}var h=D.itemsModel.parentItem();if(h&&h.id===d)D.itemsModel.parentItem().id=b.id;else{var i=D.itemsModel.findByParam("id",d);if(i){i.remove();var j=D.itemsModel.createItem(b);D.itemsModel.appendItems(j)}}D.currentPath()===d&&D.itemsModel.loadDataList(b.id),D.previewFile()&&D.previewModel.rdo().id===d&&D.previewModel.applyObject(b),c.closeDialog(),x.options.showConfirmation&&k.success(T("successful_rename"))}}).fail(ba)};k.prompt({message:T("new_filename"),value:x.options.allowChangeExtensions?a.attributes.name:la(a.attributes.name),okBtn:{label:T("action_rename"),autoClose:!1,click:b},cancelBtn:{label:T("cancel")}})},Ja=function(a,b){var c=function(a,c){var d=c.getInputValue();return d?(d=ga(d,"/")+"/",void b(d)):void k.error(T("prompt_foldername"))},d=a.length,e=d>1?T("prompt_move_multiple").replace("%s",d):T("prompt_move");k.prompt({message:e,value:D.currentPath(),okBtn:{label:T("action_move"),autoClose:!1,click:c},cancelBtn:{label:T("cancel")},template:{dialogInput:'
'+T("help_move")+"
"}})},Ka=function(a,b){return za("GET",{mode:"copy",source:a.id,target:b}).done(function(a){if(a.data){var c=a.data;D.addElements(c,b),alertify.clearDialogs(),x.options.showConfirmation&&k.success(T("successful_copied"))}}).fail(ba)},La=function(a,b){return za("GET",{mode:"move",old:a.id,"new":b}).done(function(c){if(c.data){var d=c.data;D.removeElement(a),D.addElements(d,b),D.currentPath()===a.id&&D.itemsModel.loadDataList(d.id),D.previewFile()&&D.previewModel.rdo().id===a.id&&D.previewFile(!1),alertify.clearDialogs(),x.options.showConfirmation&&k.success(T("successful_moved"))}}).fail(ba)},Ma=function(a,b){var c=a.length,d=c>1?T("confirm_delete_multiple").replace("%s",c):T("confirm_delete");k.confirm({message:d,okBtn:{label:T("yes"),click:function(a,c){b()}},cancelBtn:{label:T("no")}})},Na=function(a){return za("GET",{mode:"delete",path:a}).done(function(a){if(a.data){var b=a.data;if(D.removeElement(b),"folder"===b.type&&ha(D.currentPath(),b.id)){var c=na(b.id);D.itemsModel.loadDataList(c)}D.previewFile()&&D.previewModel.rdo().id===b.id&&D.previewFile(!1),x.options.showConfirmation&&k.success(T("successful_delete"))}}).fail(ba)},Oa=function(b){var c={mode:"download",path:b.id};a.fileDownload(Aa(ya("GET",c)),{failCallback:function(b,c,d){var e=a(b).text(),f=a.parseJSON(e);a.isPlainObject(f)&&f.errors&&aa(f.errors)}})},Pa=function(b){var c=a("#fm-js-editor-form").serializeArray();za("POST",c).done(function(a){if(a.data){var b=a.data,c=D.previewModel,d=c.editor.content();c.rdo(b),c.viewer.content(d),c.closeEditor();var e=D.itemsModel.createItem(b),f=D.itemsModel.findByParam("id",b.id);D.itemsModel.objects.replace(f,e),k.success(T("successful_edit"))}}).fail(ba)},Qa=function(a){return za("GET",{mode:"readfile",path:a.id},"text").fail(ba)},Ra=function(a){return za("GET",{mode:"readfolder",path:a}).fail(ba)},Sa=function(a,b){return za("GET",{mode:"seekfolder",path:a,string:b}).fail(ba)},Ta=function(a){return za("GET",{mode:"getinfo",path:a}).fail(ba)},Ua=function(){return za("GET",{mode:"summarize"}).done(function(b){if(b.data){var c=b.data.attributes,d=Z(c.size,!0);if(c.sizeLimit>0){var e=Z(c.sizeLimit,!0),f=100*c.size/c.sizeLimit,g=Math.round(100*f)/100;d+=" ("+g+"%) "+T("of")+" "+e}D.summaryModel.files(c.files),D.summaryModel.folders(c.folders),D.summaryModel.size(d),D.summaryModel.enabled(!0);var h=a("#summary-popup").clone().show();D.summaryModel.enabled(!1),k.alert(h[0].outerHTML)}}).fail(ba)},Va=function(a){var b=function(b,c){var d=c.getInputValue();return d?(d=ga(d,"/")+"/",void Wa(a,d)):void k.error(T("prompt_foldername"))};k.prompt({message:T("prompt_extract"),value:D.currentPath(),okBtn:{label:T("action_extract"),autoClose:!1,click:b},cancelBtn:{label:T("cancel")}})},Wa=function(a,b){za("POST",{mode:"extract",source:a.id,target:b}).done(function(a){a.data&&(D.addElements(a.data,b),alertify.clearDialogs(),x.options.showConfirmation&&k.success(T("successful_extracted")))}).fail(ba)},Xa=function(b,c,d,e){var f=e?e:[d];switch(b){case"select":Ha(d);break;case"download":a.each(f,function(a,b){Oa(b)});break;case"rename":Ia(d);break;case"move":Ja(f,function(a){Fa(f,function(b,c){return La(c,a)})});break;case"delete":Ma(f,function(){Fa(f,function(a,b){return Na(b.id)})});break;case"extract":Va(d);break;case"copy":D.clipboardModel.copy(f);break;case"cut":D.clipboardModel.cut(f);break;case"copyUrl":var g=new Clipboard(c.$selected[0],{text:function(a){return Ea(d)}});g.on("success",function(a){k.success(T("copied")),g.destroy()})}},Ya=function(){return!x.options.browseOnly&&void(x.upload.multiple?w.unbind().click(function(){if(!d("upload"))return k.error(T("NOT_ALLOWED")),!1;var b=null,c=D.currentPath(),e=tmpl("tmpl-fileupload-container",{folder:T("current_folder")+c,info:T("upload_files_number_limit").replace("%s",x.upload.maxNumberOfFiles)+" "+T("upload_file_size_limit").replace("%s",Z(x.upload.fileSizeLimit,!0)),lang:E.getTranslations()});"ALLOW_LIST"===x.security.extensions.policy&&(b=new RegExp("(\\.|\\/)("+x.security.extensions.restrictions.join("|")+")$","i")),k.dialog({message:e,width:"auto",buttons:[{type:"ok",label:T("action_upload"),autoClose:!1,click:function(a,b){g.children(".upload-item").length>0?g.find(".button-start").trigger("click"):k.error(T("upload_choose_file"))}},{label:T("action_select"),closeOnClick:!1,click:function(b,c){a("#fileupload",f).trigger("click")}},{type:"cancel",label:T("close")}]});var f=a(".fm-fileupload-container"),g=a(".dropzone",f),h=a(".dropzone-wrapper",f),i=a("#total-progress",f).children();x.customScrollbar.enabled&&h.mCustomScrollbar({theme:x.customScrollbar.theme,scrollButtons:{enable:x.customScrollbar.button},advanced:{autoExpandHorizontalScroll:!0,updateOnContentResize:!0},callbacks:{onOverflowY:function(){h.find(".mCSB_container").css({"margin-right":h.find(".mCSB_scrollTools").width()})},onOverflowYNone:function(){h.find(".mCSB_container").css({"margin-right":"auto"})}},axis:"y"}),h.on("click",function(b){(b.target===this||a(b.target).parent()[0]===this||b.target===g[0]||a(b.target).parent().hasClass("default-message"))&&a("#fileupload",f).trigger("click")}),g.on("click",".button-start",function(b){var c=a(this),d=c.parent().parent(),e=d.data();e.submit(),c.remove()}),g.on("click",".button-abort",function(b){var c=a(this),d=c.parent().parent(),e=d.data(),f=e.files[0].context;e.abort(),f.find(".error-message").text(T("upload_aborted")),f.addClass("aborted")}),g.on("click",".button-resume",function(b){function d(c){a.blueimp.fileupload.prototype.options.add.call(a("#fileupload")[0],b,c),c.submit()}var e=a(this),f=e.parent().parent(),g=f.data(),h=g.files[0];if(h.chunkUploaded){var i=c+h.serverName;Ta(i).then(function(a){a.data&&(g.uploadedBytes=Number(a.data.attributes.size),g.uploadedBytes||(h.chunkUploaded=void 0),d(g))})}else d(g)}),g.on("click",".button-remove",function(b){var d=a(this),e=d.parent().parent(),f=e.data(),g=f.files[0];g.chunkUploaded&&Na(c+g.serverName),d.closest(".upload-item").remove(),j()}),g.on("click",".button-info",function(b){var c=a(this),d=c.closest(".upload-item");if(d.hasClass("error")){var e=d.find(".error-message");k.error(e.text())}});var j=function(){g.children(".upload-item").length>0?g.addClass("started"):g.removeClass("started")},l=D.filterModel.getExtensions();l&&a("#fileupload").attr("accept",l.map(function(a){return"."+a}).join()),a("#fileupload",f).fileupload({autoUpload:!1,sequentialUploads:!0,dataType:"json",dropZone:g,maxChunkSize:x.upload.chunkSize,url:Aa(),paramName:x.upload.paramName,singleFileUploads:!0,formData:ya("POST",{mode:"upload",path:c}),maxNumberOfFiles:x.upload.maxNumberOfFiles,acceptFileTypes:b,maxFileSize:x.upload.fileSizeLimit,messages:{maxNumberOfFiles:T("upload_files_number_limit").replace("%s",x.upload.maxNumberOfFiles),acceptFileTypes:T("upload_file_type_invalid"),maxFileSize:T("upload_file_too_big")+" "+T("upload_file_size_limit").replace("%s",Z(x.upload.fileSizeLimit,!0))},previewMaxHeight:120,previewMaxWidth:120,previewCrop:!0}).on("fileuploadadd",function(b,c){var d=g.children(".upload-item");a.each(c.files,function(b,e){if(d.length>=x.upload.maxNumberOfFiles)return k.error(T("upload_files_number_limit").replace("%s",x.upload.maxNumberOfFiles),{logClass:"fileuploadadd",unique:!0}),!1;e.formattedSize=Z(e.size);var f=a(tmpl("tmpl-upload-item",{file:e,lang:E.getTranslations(),imagesPath:k.settings.baseUrl+"/libs/jQuery-File-Upload/img"}));e.context=f,f.find(".buttons").data(c), -f.appendTo(g)}),j()}).on("fileuploadsend",function(b,c){return k.settings.callbacks.beforeSendRequest(c.type,c.formData)===!1?(a.each(c.files,function(a,b){var c=b.context;c.find(".error-message").text(T("NOT_ALLOWED")),c.removeClass("added process").addClass("error")}),!1):void a.each(c.files,function(a,b){var d=b.context;d.removeClass("added aborted error").addClass("process"),b.chunkUploaded&&c.total===c.uploadedBytes&&d.remove()})}).on("fileuploadfail",function(b,c){var d,e=c.jqXHR;d=a.isPlainObject(e.responseJSON)&&e.responseJSON.errors?_(e.responseJSON.errors[0]):T("upload_failed"),a.each(c.files,function(a,b){var c=b.context;c.removeClass("added process").addClass("error"),c.find(".error-message").text(d),c.find(".button-start").remove()})}).on("fileuploaddone",function(b,c){var d=c.result;a.each(c.files,function(a,b){d&&d.data&&d.data[a]&&b.context.remove()})}).on("fileuploadalways",function(b,c){var d=c.result;a.each(c.files,function(a,b){if(d&&d.data&&d.data[a]){var c=d.data[a];D.removeElement(c),D.addElements(c,D.currentPath())}});var e=g.children(".upload-item");0===e.filter(".added").length&&0===e.filter(".process").length&&(0===e.length&&(alertify.clearDialogs(),x.options.showConfirmation&&k.success(T("upload_successful_files"))),e.filter(".error").length&&k.error(T("upload_partially")+"
"+T("upload_failed_details"))),j()}).on("fileuploadchunkdone",function(b,c){var d=c.result;a.each(c.files,function(a,b){if(d.data&&d.data[a]){var c=d.data[a];D.removeElement(c),D.addElements(c,D.currentPath()),b.serverName=c.attributes.name,b.chunkUploaded=1}})}).on("fileuploadprocessalways",function(b,c){a.each(c.files,function(a,b){var c=b.context;"undefined"!=typeof c&&(b.preview&&(c.find(".image").append(b.preview),c.find(".preview").removeClass("file-preview").addClass("image-preview")),b.error&&(c.removeClass("added process").addClass("error"),c.find(".error-message").text(b.error),c.find(".button-start").remove()))})}).on("fileuploadprogress",function(b,c){a.each(c.files,function(a,b){var d=b.context,e=parseInt(c.loaded/c.total*100,10);d.find(".progress-bar").css("width",e+"%")})}).on("fileuploadprogressall",function(a,b){var c=parseInt(b.loaded/b.total*100,10);i.css("width",c+"%")})}):(w.unbind().click(function(){return d("upload")?void a("#newfile").trigger("click"):(k.error(T("NOT_ALLOWED")),!1)}),o.fileupload({autoUpload:!0,dataType:"json",url:Aa(),paramName:x.upload.paramName,maxChunkSize:x.upload.chunkSize}).on("fileuploadadd",function(a,b){w.data(b)}).on("fileuploadsubmit",function(a,b){b.formData=ya("POST",{mode:"upload",path:D.currentPath()}),w.addClass("loading").prop("disabled",!0),w.children("span").text(T("loading_data"))}).on("fileuploadsend",function(a,b){if(k.settings.callbacks.beforeSendRequest(b.type,b.formData)===!1)return k.error(T("NOT_ALLOWED")),!1}).on("fileuploadalways",function(a,b){w.removeData().removeClass("loading").prop("disabled",!1),w.children("span").text(T("action_upload"));var c=b.result;if(c&&c.data){var d=c.data[0];D.removeElement(d),D.addElements(d,D.currentPath()),x.options.showConfirmation&&k.success(T("upload_successful_file"))}}).on("fileuploadchunkdone",function(a,b){var c=b.result;if(c.data&&c.data[0]){var d=c.data[0];D.removeElement(d),D.addElements(d,D.currentPath())}}).on("fileuploadfail",function(b,c){var d,e=c.jqXHR;d=a.isPlainObject(e.responseJSON)&&e.responseJSON.errors?_(e.responseJSON.errors[0]):T("upload_failed"),k.error(d)})))};J(),a(window).resize(k.setDimensions)}}(jQuery),$.fn.richFilemanager=function(a){return this.each(function(){if(void 0===$(this).data("richFilemanager")){var b=new $.richFilemanagerPlugin(this,a);$(this).data("richFilemanager",b)}})},window.location.origin||(window.location.origin=window.location.protocol+"//"+window.location.hostname+(window.location.port?":"+window.location.port:"")); \ No newline at end of file +!function(a){a.richFilemanagerPlugin=function(b,c){function d(a){return A.indexOf(a)>-1}function e(b,c){if(!d(c))return!1;if("select"===c&&"folder"===b.type)return!1;if("extract"===c){var e=ka(b.attributes.name);return"file"===b.type&&"zip"===e}return"download"===c&&"folder"===b.type?x.options.allowFolderDownload===!0:"undefined"==typeof b.attributes.capabilities||a.inArray(c,b.attributes.capabilities)>-1}function f(){x.filetree.enabled&&(s.show(),p.splitter({sizeLeft:x.filetree.width,minLeft:x.filetree.minWidth,minRight:200}))}function g(){return window.opener||window.parent&&window.self!==window.parent||window.tinyMCEPopup||I.param("field_name")||I.param("CKEditor")||I.param("ImperaviElementId")}function h(a){return a.attributes.readable?("file"===a.type&&D.previewModel.applyObject(a),void("folder"!==a.type&&"parent"!==a.type||(D.previewFile(!1),D.itemsModel.loadDataList(a.id)))):(k.error(T("NOT_ALLOWED_SYSTEM")),!1)}function i(a){var b=!D.clipboardModel.enabled(),c={select:{name:T("action_select"),className:"select"},download:{name:T("action_download"),className:"download"},rename:{name:T("action_rename"),className:"rename"},move:{name:T("action_move"),className:"move"},separator1:"-----",copy:{name:T("clipboard_copy"),className:"copy"},cut:{name:T("clipboard_cut"),className:"cut"},"delete":{name:T("action_delete"),className:"delete"},extract:{name:T("action_extract"),className:"extract"},copyUrl:{name:T("copy_to_clipboard"),className:"copy-url"}};return e(a,"download")||delete c.download,e(a,"select")&&g()||delete c.select,e(a,"rename")&&x.options.browseOnly!==!0||delete c.rename,e(a,"delete")&&x.options.browseOnly!==!0||delete c["delete"],e(a,"extract")&&x.options.browseOnly!==!0||delete c.extract,e(a,"copy")&&x.options.browseOnly!==!0&&!b||delete c.copy,e(a,"move")&&x.options.browseOnly!==!0&&!b||(delete c.cut,delete c.move),c}var j={baseUrl:".",configUrl:null,config:{},callbacks:{beforeCreateImageUrl:function(a,b){return b},beforeCreatePreviewUrl:function(a,b){return b},beforeSelectItem:function(a,b){return b},afterSelectItem:function(a,b,c){},beforeSetRequestParams:function(a,b){return b},beforeSendRequest:function(a,b){return!0}}},k=this,l=a(b),m=l.children(".fm-wrapper"),n=m.find(".fm-header"),o=n.find(".fm-uploader"),p=m.children(".fm-splitter"),q=m.children(".fm-footer"),r=p.children(".fm-fileinfo"),s=p.children(".fm-filetree"),t=r.find(".view-items-wrapper"),u=r.find(".fm-preview-wrapper"),v=t.find(".view-items"),w=o.children(".fm-upload"),x=null,y="/",z=null,A=[],B=null,C=null,D=null,E=null,F=null,G=null,H=null,I=purl();(new Date).getTime();k.settings=a.extend(!0,j,c),k.write=function(b,c){var d=alertify,e=a.extend({},{reset:!0,delay:5e3,logMaxItems:5,logPosition:"bottom right",logContainerClass:"fm-log",logMessageTemplate:null,parent:document.body,onClick:void 0,unique:!1,type:"log"},c);if(e.logClass&&e.unique&&a(".fm-log").children("."+e.logClass).length>0)return d;e.reset&&d.reset(),d.parent(e.parent),d.logDelay(e.delay),d.logMaxItems(e.logMaxItems),d.logPosition(e.logPosition),d.logContainerClass(e.logContainerClass),d.logMessageTemplate(e.logMessageTemplate),d[e.type](b,e.onClick);var f=d.getLogs();return f[f.length-1]},k.error=function(b,c){return k.write(b,a.extend({},{type:"error",delay:1e4},c))},k.warning=function(b,c){return k.write(b,a.extend({},{type:"warning",delay:1e4},c))},k.success=function(b,c){return k.write(b,a.extend({},{type:"success",delay:6e3},c))},k.alert=function(a){alertify.reset().dialogContainerClass("fm-popup").alert(a)},k.confirm=function(a){alertify.reset().dialogWidth(a.width).dialogPersistent(a.persistent).dialogContainerClass("fm-popup").confirm(a.message,a.okBtn,a.cancelBtn)},k.prompt=function(a){alertify.reset().dialogWidth(a.width).dialogPersistent(a.persistent).dialogContainerClass("fm-popup").theme(a.template).prompt(a.message,a.value||"",a.okBtn,a.cancelBtn)},k.dialog=function(a){alertify.reset().dialogWidth(a.width).dialogPersistent(a.persistent).dialogContainerClass("fm-popup").dialog(a.message,a.buttons)},k.setDimensions=function(){var b=m.outerHeight(!0)-m.height(),c=a(window).height()-n.height()-q.height()-b,d=p.width()-p.children(".splitter-bar-vertical").outerWidth()-s.outerWidth();p.height(c),r.width(d)},k.console=function(){x.options.logger&&arguments&&[].unshift.call(arguments,(new Date).getTime())},k.refreshFolder=function(a){D.loadPath(D.currentPath(),a)},k.loadFolder=function(a,b){"/"!==a&&(a="/"+ea(a,"/")+"/"),D.loadPath(a,b)};var J=function(){var b=a.Deferred();b.then(function(){return K()}).then(function(){return M()}).then(function(a,b){return L()}).then(function(){return N()}).then(function(){O(function(){P()})}),b.resolve()},K=function(){return a.when(W("default"),W("user")).done(function(b,c){var d=b[0],e=c[0];if(void 0!==e&&null!==e&&delete e.version,x=a.extend({},d,e),x.api.connectorUrl)z=x.api.connectorUrl;else{var f=location.origin+location.pathname,g="connectors/"+x.api.lang+"/filemanager."+x.api.lang;ka(f).length>0&&(f=f.substring(0,f.lastIndexOf("/")+1)),z=f+g}})},L=function(){return za("GET",{mode:"initiate"}).done(function(b){if(b.data){var c=b.data.attributes.config;a.each(c,function(b,c){a.each(c,function(a,c){return null===c||(void 0===x[b]&&(x[b]=[]),void(x[b][a]=c))})}),c.options&&c.options.capabilities&&(x.options.capabilities=c.options.capabilities)}}).fail(function(a){k.error("Unable to perform initial request to server."),ba(a)}).then(function(b){if(b.errors)return a.Deferred().reject()})},M=function(){return E=new Q,a.ajax().then(function(){var a=I.param("langCode");return a?V(E.buildLangFileUrl(a)).done(function(){E.setLang(a)}).fail(function(){setTimeout(function(){k.error("Given language file ("+E.buildLangFileUrl(a)+") does not exist!")},500)}):void E.setLang(x.language["default"])}).then(function(){var b=E.buildLangFileUrl(E.getLang())+"?_="+(new Date).getTime();return a.ajax({type:"GET",url:b,dataType:"json"}).done(function(a){E.setTranslations(a)})}).then(function(){var b=E.getLang().substr(0,2),c=k.settings.baseUrl;return a.when(a.get(c+"/libs/cldrjs/cldr-dates/"+b+"/ca-gregorian.json"),a.get(c+"/libs/cldrjs/cldr-numbers/"+b+"/numbers.json"),a.get(c+"/libs/cldrjs/cldr-core/supplemental/likelySubtags.json"),a.get(c+"/libs/cldrjs/cldr-core/supplemental/timeData.json"),a.get(c+"/libs/cldrjs/cldr-core/supplemental/weekData.json")).fail(function(){k.error('CLDR files for "'+b+'" language do not exist!')}).then(function(){return[].slice.apply(arguments,[0]).map(function(a){return a[0]})}).then(Globalize.load).then(function(){F=Globalize(b)})})},N=function(){return a.when(Y("upload-container"),Y("upload-item")).done(function(a,b){var c=a[0],d=b[0];m.append(c).append(d)})},O=function(a){var b=[],c=[];if(b.push("/themes/"+x.options.theme+"/styles/theme.css"),x.viewer.image.lazyLoad&&b.push("/libs/lazyload/dist/lazyload.min.js"),x.customScrollbar.enabled&&(b.push("/libs/custom-scrollbar-plugin/jquery.mCustomScrollbar.min.css"),b.push("/libs/custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js")),b.push(a),X(b),x.editor.enabled){var d=x.editor.theme;d&&"default"!==d&&c.push("/libs/CodeMirror/theme/"+d+".css"),c.push("/libs/CodeMirror/lib/codemirror.css"),c.push("/libs/CodeMirror/lib/codemirror.js"),c.push("/libs/CodeMirror/addon/selection/active-line.js"),c.push("/libs/CodeMirror/addon/display/fullscreen.css"),c.push("/libs/CodeMirror/addon/display/fullscreen.js")}x.viewer.markdownRenderer.enabled&&(c.push("/src/css/fm-markdown.css"),c.push("/libs/markdown-it/markdown-it.min.js"),c.push("/libs/markdown-it/default.min.css"),c.push("/libs/markdown-it/highlight.min.js"),c.push("/libs/markdown-it/markdown-it-footnote.min.js"),c.push("/libs/markdown-it/markdown-it-replace-link.min.js")),x.options.browseOnly||(c.push("/src/js/libs-fileupload.js"),x.upload.multiple&&c.push("/libs/jQuery-File-Upload/css/dropzone.css")),c.length&&X(c)},P=function(){G=new R,A=x.options.capabilities||["upload","select","download","rename","copy","move","delete","extract","createFolder"],x.security.readOnly&&(x.options.browseOnly=!0),x.upload.paramName||(x.upload.paramName="files");var b=[];x.options.fileSorting&&(b=x.options.fileSorting.toLowerCase().split("_")),B=b[0]||"name",C=b[1]||"asc";var c=I.param("exclusiveFolder");c&&(y="/"+c+"/",y=ja(y));var e=I.param("expandedFolder");if(e&&(H=y+e+"/",H=ja(H)),D=new S,ko.applyBindings(D),D.itemsModel.initiateLazyLoad(),D.filterModel.setName(I.param("filter")),ko.bindingHandlers.toggleNodeVisibility={init:function(b,c){var d=c();a(b).toggle(d.isExpanded())},update:function(b,c){var d=c();return d.isSliding()!==!1&&(d.isExpanded()===!1&&a(b).slideDown(x.filetree.expandSpeed,function(){d.isSliding(!1),d.isExpanded(!0)}),void(d.isExpanded()===!0&&a(b).slideUp(x.filetree.expandSpeed,function(){d.isSliding(!1),d.isExpanded(!1)})))}},ko.bindingHandlers.draggableView={init:function(a,b,c){D.ddModel.makeDraggable(b(),a)}},ko.bindingHandlers.droppableView={init:function(a,b,c){D.ddModel.makeDroppable(b(),a)}},ko.bindingHandlers.draggableTree={init:function(a,b,c){D.ddModel.makeDraggable(b(),a)}},ko.bindingHandlers.droppableTree={init:function(a,b,c){D.ddModel.makeDroppable(b(),a)}},m.mousewheel(function(b){if(!D.ddModel.dragHelper)return!0;var c,d=null;if(c=x.customScrollbar.enabled?a([t[0],s[0]]):p.children(".splitter-pane"),c.each(function(c){var e=a(this),f=e.offset().top,g=e.offset().left;if(b.offsetY>=f&&b.offsetY<=f+e.height()&&b.offsetX>=g&&b.offsetX<=g+e.width())return d=e,!1}),null===d)return!1;if(x.customScrollbar.enabled){var e=d.find(".mCSB_scrollTools_vertical"),f=1===b.deltaY?"+":"-";e.is(":visible")&&d.mCustomScrollbar("scrollTo",[f+"=250",0],{scrollInertia:500,scrollEasing:"easeOut",callbacks:!0})}else if(d[0].scrollHeight>d[0].clientHeight){var g=d.scrollTop(),h=g-200*b.deltaY;D.ddModel.isScrolling=!0,h=h<0?0:h,d.stop().animate({scrollTop:h},100,"linear",function(){D.ddModel.isScrolling=!1,D.ddModel.isScrolled=!0})}}),v.selectable({filter:"li:not(.directory-parent), tbody > tr:not(.directory-parent)",cancel:".directory-parent, thead",disabled:!x.manager.selection.enabled,appendTo:v,start:function(a,b){Ga(),D.itemsModel.isSelecting(!0)},stop:function(a,b){D.itemsModel.isSelecting(!1)},selected:function(a,b){var c=ko.dataFor(b.selected);c.selected(!0)},unselected:function(a,b){var c=ko.dataFor(b.unselected);c.selected(!1)}}),r.contextMenu({selector:".view-items",zIndex:10,build:function(b,c){var e={createFolder:{name:T("create_folder"),className:"create-folder"},paste:{name:T("clipboard_paste"),className:"paste",disabled:function(a,b){return D.clipboardModel.isEmpty()}}};return D.clipboardModel.enabled()&&x.options.browseOnly!==!0||delete e.paste,d("createFolder")&&x.options.browseOnly!==!0||delete e.createFolder,!a.isEmptyObject(e)&&{appendTo:".fm-container",items:e,reposition:!1,callback:function(a,b){switch(a){case"createFolder":D.headerModel.createFolder();break;case"paste":D.clipboardModel.paste()}}}}}),x.extras.extra_js)for(var g=0;g400&&(this.yStartPosition=this.mcs.top),D.itemsModel.isSelecting()&&D.itemsModel.continiousSelection(!0);var b=Math.abs(this.mcs.top)-Math.abs(this.yStartPosition);v.selectable("repositionCssHelper",b,0)}D.itemsModel.lazyLoad&&D.itemsModel.lazyLoad.handleScroll()}},axis:"y",alwaysShowScrollbar:0}));var h=document.documentElement;if(h.setAttribute("data-useragent",navigator.userAgent),x.options.logger){(new Date).getTime()}var i=l.find(".fm-loading-wrap");i.fadeOut(800,function(){k.setDimensions()}),k.setDimensions()},Q=function(){var a=null,b={},c=k.settings.baseUrl+"/languages/";this.buildLangFileUrl=function(a){return c+a+".json"},this.setLang=function(b){a=b},this.getLang=function(){return a},this.setTranslations=function(a){b=a},this.getTranslations=function(){return b},this.translate=function(a){return b[a]}},R=function(){var a={},b=this;this.push=function(c,d,e){b.removeTimer(c),a[c]=setTimeout(d,e)},this.getTimer=function(b){return a[b]},this.removeTimer=function(b){a[b]&&(clearTimeout(a[b]),delete a[b])}},S=function(){function b(a){return(!x.manager.selection.enabled||!x.manager.selection.useCtrlKey||a.ctrlKey!==!0)&&(!x.manager.dblClickOpen||"click"!==a.type)}var c=this;this.config=ko.observable(x),this.loadingView=ko.observable(!0),this.previewFile=ko.observable(!1),this.viewMode=ko.observable(x.manager.defaultViewMode),this.currentPath=ko.observable(y),this.browseOnly=ko.observable(x.options.browseOnly),this.previewModel=ko.observable(null),this.currentLang=E.getLang(),this.lg=E.getTranslations(),this.previewFile.subscribe(function(a){a||(c.previewModel.closeEditor(),c.itemsModel.descriptivePanel.rdo().id===c.previewModel.rdo().id&&c.itemsModel.descriptivePanel.render(c.previewModel.viewer.content()))}),this.isCapable=function(a){return d(a)},this.loadPath=function(a,b){var d,e=new s(a);b&&(d=D.treeModel.findByParam("id",a)),d&&e.setPreloader(D.treeModel.getPreloader(d)),e.setPreloader(c.itemsModel.getPreloader()).setDataHandler(function(a,b){d&&D.treeModel.addNodes(a,d,!0),c.itemsModel.addItems(a,b,!0),c.searchModel.clearInput()}).load(function(){return Ra(a)})},this.addElements=function(a,b,d){var e=c.treeModel.findByParam("id",b);e&&c.treeModel.addNodes(a,e,d),c.currentPath()===b&&c.itemsModel.addItems(a,b,d)},this.removeElement=function(a){var b=c.treeModel.findByParam("id",a.id);b&&b.remove();var d=c.itemsModel.findByParam("id",a.id);d&&d.remove()},this.fetchSelectedItems=function(a){var b,d;if(a===q.name)return c.itemsModel.getSelected();if(a===o.name)return c.treeModel.getSelected();if(!a)return b=c.treeModel.getSelected(),d=c.itemsModel.getSelected(),d.length>0?d:b;throw new Error("Unknown item type.")},this.fetchSelectedObjects=function(b){var d=[];return a.each(c.fetchSelectedItems(b.constructor.name),function(a,b){d.push(b.rdo)}),d};var f=function(){this.beforeLoad=function(a){},this.afterLoad=function(a,b){}},j=function(){var a=this,b=null;this.rdo=ko.observable({}),this.cdo=ko.observable({}),this.viewer={type:ko.observable("default"),isEditable:ko.observable(!1),url:ko.observable(null),pureUrl:ko.observable(null),options:ko.observable({}),content:ko.observable(null),codeMirror:ko.observable(null)},this.renderer=new L,this.editor=new M,this.rdo.subscribe(function(b){a.cdo({isFolder:"folder"===b.type,sizeFormatted:Z(b.attributes.size),createdFormatted:$(b.attributes.created),modifiedFormatted:$(b.attributes.modified),extension:"file"===b.type?ka(b.id):null,dimensions:b.attributes.width?b.attributes.width+"x"+b.attributes.height:null})}),this.editor.content.subscribe(function(b){a.editor.isInteractive()&&a.renderer.render(b)}),this.applyObject=function(d){b&&b.destroy(),c.previewFile(!1);var e=d.attributes.name,f={interactive:!1},g={type:"default",url:null,options:{}};a.rdo(d),qa(e)&&(g.type="image",g.url=Ca(d,!1,!0)),sa(e)&&x.viewer.audio.enabled===!0&&(g.type="audio",g.url=Ba(d,!0)),ra(e)&&x.viewer.video.enabled===!0&&(g.type="video",g.url=Ba(d,!0),g.options={width:x.viewer.video.playerWidth,height:x.viewer.video.playerHeight}),ua(e)&&x.viewer.opendoc.enabled===!0&&(g.type="opendoc",g.url=k.settings.baseUrl+"/libs/ViewerJS/index.html#"+Ba(d,!0),g.options={width:x.viewer.opendoc.readerWidth,height:x.viewer.opendoc.readerHeight}),va(e)&&x.viewer.google.enabled===!0&&(g.type="google",g.url="https://docs.google.com/viewer?url="+encodeURIComponent(Ba(d,!1))+"&embedded=true",g.options={width:x.viewer.google.readerWidth,height:x.viewer.google.readerHeight}),ta(e)&&x.viewer.iframe.enabled===!0&&(g.type="iframe",g.url=Ba(d,!0),g.options={width:x.viewer.iframe.readerWidth,height:x.viewer.iframe.readerHeight}),(wa(e)&&x.viewer.codeMirrorRenderer.enabled===!0||xa(e)&&x.viewer.markdownRenderer.enabled===!0)&&(g.type="renderer",g.options={is_writable:d.attributes.writable},a.renderer.setRenderer(d),f.interactive=a.renderer.renderer().interactive),a.viewer.type(g.type),a.viewer.url(g.url),a.viewer.options(g.options),a.viewer.pureUrl(Ea(d)),a.viewer.isEditable(pa(e)&&x.editor.enabled===!0),a.editor.isInteractive(f.interactive),"renderer"===g.type||a.viewer.isEditable()?Qa(d).then(function(b){a.viewer.content(b),c.previewFile(!0)}):c.previewFile(!0)},this.afterRender=function(){a.renderer.render(a.viewer.content());var c=u.find(".btn-copy-url")[0];b=new Clipboard(c),b.on("success",function(a){k.success(T("copied"))})},this.initiateEditor=function(b){var c=u.find(".fm-cm-editor-content")[0];a.editor.createInstance(a.cdo().extension,c,{readOnly:!1,styleActiveLine:!0})},this.bindToolbar=function(b){e(a.rdo(),b)&&Xa(b,{},a.rdo())},this.previewIconClass=ko.pureComputed(function(){var b=[],c=["ico"];return"default"!==a.viewer.type()&&a.viewer.url()||(b.push("grid-icon"),this.cdo().isFolder===!0?(b.push("ico_folder"),c.push("folder"),this.rdo().attributes.readable||c.push("lock")):(b.push("ico_file"),this.rdo().attributes.readable?c.push("ext",this.cdo().extension):c.push("file","lock")),b.push(c.join("_"))),b.join(" ")},this),this.closePreview=function(){c.previewFile(!1)},this.editFile=function(){var b=a.viewer.content();a.renderer.render(b),a.editor.render(b)},this.saveFile=function(){Pa(a.rdo())},this.closeEditor=function(){a.editor.enabled(!1),a.renderer.render(a.viewer.content())},this.buttonVisibility=function(b){switch(b){case"select":return e(a.rdo(),b)&&g();case"move":case"rename":case"delete":case"download":return e(a.rdo(),b)}}},n=function(){function b(a){if(null!==H){a||(a=d.rootNode);var b=d.findByFilter(function(a){return 0===H.indexOf(a.id)},a);b?(x.filetree.expandSpeed=10,d.loadDataNode(b,!1,!0)):(H=null,x.filetree.expandSpeed=200,d.setItemsFromNode(a))}}var d=this;this.selectedNode=ko.observable(null);var e=new o({attributes:{}});e.id=y,e.level=ko.observable(-1),this.rootNode=e,this.mapNodes=function(a,b){b||(b=d.rootNode),b.isRoot()||a.call(this,b);var c=b.children();if(!c||0===c.length)return null;for(var e=0,f=c.length;e0,this.closeButtonOnClick=function(){k.console("CLOSE button is clicked")},this.navHome=function(){c.previewFile(!1),c.itemsModel.loadDataList(y)},this.navLevelUp=function(){var a=c.previewFile()?ma(c.previewModel.rdo().id):na(c.currentPath());c.previewFile()&&c.previewFile(!1),a!==c.currentPath()&&c.itemsModel.loadDataList(a)},this.navRefresh=function(){c.previewFile()?(c.previewFile(!1),c.previewFile(!0)):c.itemsModel.loadDataList(c.currentPath())},this.displayGrid=function(){c.viewMode("grid"),c.previewFile(!1),c.itemsModel.lazyLoad&&c.itemsModel.lazyLoad.update()},this.displayList=function(){c.viewMode("list"),c.previewFile(!1)},this.switchLang=function(b){var c=b.target.value,d=E.getLang();if(c&&c.toLowerCase()!==d.toLowerCase()){var e,f=window.location.toString(),g=new RegExp("(langCode=)"+d);e=g.test(f)?f.replace(g,"$1"+c):f+(a.isEmptyObject(I.param())?"?":"#")+"langCode="+c,window.location.href=e}},this.createFolder=function(){function a(a,b){var c=b.getInputValue();return c?void za("GET",{mode:"addfolder",path:D.currentPath(),name:c}).done(function(a){a.data&&(D.addElements(a.data,D.currentPath()),b.closeDialog(),x.options.showConfirmation&&k.success(T("successful_added_folder")))}).fail(ba):void k.error(T("no_foldername"))}return d("createFolder")?void k.prompt({message:T("prompt_foldername"),value:T("default_foldername"),okBtn:{label:T("create_folder"),autoClose:!1,click:a},cancelBtn:{label:T("cancel")}}):(k.error(T("NOT_ALLOWED")),!1)}},z=function(){this.files=ko.observable(null),this.folders=ko.observable(null),this.size=ko.observable(null),this.enabled=ko.observable(!1),this.doSummarize=function(){Ua()}},A=function(){var b=this;this.name=ko.observable(null),this.setName=function(c){c&&x.filter&&a.isArray(x.filter[c])&&b.name(c)},this.getExtensions=function(){return b.name()?x.filter[b.name()]:null},this.filterItem=function(c){var d=b.getExtensions(),e=!c.cdo.hiddenBySearch;if(c.cdo.hiddenByType=!1,"file"===c.rdo.type&&a.isArray(d)){var f=ka(c.id),g=d.indexOf(f)!==-1;e=e&&g,c.cdo.hiddenByType=!g}c.visible(e)},this.filter=function(d){b.setName(d),a.each(c.itemsModel.objects(),function(a,c){b.filterItem(c)}),c.treeModel.mapNodes(function(a){b.filterItem(a)}),c.itemsModel.lazyLoad&&c.itemsModel.lazyLoad.update()},this.reset=function(){b.name(null),b.filter(null)}},F=function(){function b(){h?G.push("search",function(){d()},x.search.typingDelay):d()}function d(){var b=f.value(),d=x.search.caseSensitive?b:b.toLowerCase();if(""===b)return void(b!==g?e():k.warning(T("search_string_empty")));if(x.search.recursive){var h=c.currentPath(),i=new s(h);i.setPreloader(c.itemsModel.getPreloader()).setDataHandler(function(b,e){var g=[];x.search.caseSensitive?a.each(b,function(a,b){0===b.attributes.name.indexOf(d)&&g.push(b)}):g=b;var h=c.itemsModel.createItems(g);c.itemsModel.setItemsList(h),f.isRendered(!0)}).load(function(){return Sa(h,b)})}else a.each(c.itemsModel.objects(),function(a,b){var c=b.rdo.attributes.name;x.search.caseSensitive||(c=c.toLowerCase());var e=0===c.indexOf(d),f=!b.cdo.hiddenByType;f=f&&e,b.cdo.hiddenBySearch=!e,b.visible(f)}),f.isRendered(!0)}function e(){f.clearInput(),x.search.recursive?c.itemsModel.loadDataList(c.currentPath()):a.each(c.itemsModel.objects(),function(a,b){b.cdo.hiddenBySearch=!1,b.visible(!b.cdo.hiddenByType)})}var f=this,g="",h=!!x.search.typingDelay;this.value=ko.observable(""),this.isRendered=ko.observable(!1),this.value.subscribe(function(a){g=a},null,"beforeChange"),this.inputKeyUp=function(a,c){var d=c.which||c.keyCode,e=[16,17,18,27,37,38,39,40];if(h){if(e.indexOf(d)>-1)return;f.value(c.target.value)}(h||13===d)&&b()},this.seekItems=function(a,c){b()},this.reset=function(a,b){e()},this.clearInput=function(){g="",f.value(""),f.isRendered(!1),G.removeTimer("search")}},J=function(){function a(){e=[],b=null,f.itemsNum(0)}var b=null,e=[],f=this,g=d("copy")&&d("move");this.itemsNum=ko.observable(0),this.enabled=ko.observable(c.config().clipboard.enabled&&g),this.copy=function(){f.hasCapability("copy")&&(b="copy",e=c.fetchSelectedItems(),f.itemsNum(e.length))},this.cut=function(){f.hasCapability("cut")&&(b="cut",e=c.fetchSelectedItems(),f.itemsNum(e.length))},this.paste=function(){var d=c.currentPath();if(f.hasCapability("paste")&&!f.isEmpty())return null===b||0===e.length?void k.warning(T("clipboard_empty")):void Fa(e,function(a,c){return"cut"===b?La(c,d):"copy"===b?Ka(c,d):void 0},a)},this.clear=function(){f.hasCapability("clear")&&!f.isEmpty()&&(a(),k.success(T("clipboard_cleared")))},this.isEmpty=function(){return 0===e.length},this.hasCapability=function(a){if(!f.enabled)return!1;switch(a){case"copy":return d("copy");case"cut":return d("move");default:return!0}}},K=function(){var a=this;this.items=ko.observableArray([]),this.clean=function(){a.items([]),a.add(y,"")},this.add=function(c,d){a.items.push(new b(c,d))},this.splitPath=function(b){var c=y,d=b.replace(new RegExp("^"+y),"").split("/");for(a.clean();d.length>0;){var e=d.shift();e&&(c+=e+"/",a.add(c,e))}},this.splitCurrent=function(){a.splitPath(c.currentPath())},this.getLabel=ko.pureComputed(function(){var a=T(c.searchModel.isRendered()?"search_results":"current_folder");return a+": "},this);var b=function(a,b){var d=this;this.path=a,this.label=b,this.isRoot=a===y,this.active=a===c.currentPath(),this.itemClass=function(){var a=["nav-item"];return d.isRoot&&a.push("root"),d.active&&a.push("active"),a.join(" ")},this["goto"]=function(a,b){a.active||c.itemsModel.loadDataList(a.path)}}},L=function(){function b(a){return xa(a)?new f:wa(a)?new e:void 0}var c,d=this;this.rdo=ko.observable({}),this.content=ko.observable(null),this.renderer=ko.observable(null),this.render=function(a){d.renderer()&&d.renderer().processContent(a)},this.setRenderer=function(a){d.rdo(a),d.renderer(b(a.attributes.name))},this.setContainer=function(b){a.each(b,function(){if(a(this).hasClass("fm-renderer-container"))return c=a(this),!1}),d.renderer().processDomElements(c)};var e=function(){this.name="codeMirror",this.interactive=!1;var a=new M;this.processContent=function(b){a.render(b),d.content(b)},this.processDomElements=function(b){if(!a.instance){var c=b.find(".fm-cm-renderer-content")[0],e=ka(d.rdo().id);a.createInstance(e,c,{readOnly:"nocursor",styleActiveLine:!1,lineNumbers:!1})}}},f=function(){function b(){c.find("a").each(function(){var b=a(this).attr("href"),c=D.previewModel.editor;if(c.enabled()&&c.isInteractive())a(this).off("click"),a(this).on("click",function(){return!1});else{if(b.search("://")!==-1||ha(b,"mailto:"))return;xa(b)&&a(this).on("click",function(a){return Ta(b).then(function(a){a.data&&h(a.data)}),!1})}})}this.name="markdown",this.interactive=!0;var e=window.markdownit({html:!0,linkify:!0,typographer:!0,highlight:function(a,b){if(b&&hljs.getLanguage(b))try{return'
'+hljs.highlight(b,a,!0).value+"
"}catch(c){}return'
'+e.utils.escapeHtml(a)+"
"},replaceLink:function(a,b){if(a.search("://")!==-1||ha(a,"mailto:"))return a;var c=ha(a,"/")?y:ma(d.rdo().id),e=c+fa(a,"/");if(xa(e))return e;var f=ya("GET",{mode:"readfile",path:e});return Aa(f)}}).use(window.markdownitReplaceLink);this.processContent=function(a){var c=e.render(a);d.content(c),b()},this.processDomElements=function(a){}}},M=function(){function b(a){d.enabled(!0),d.instance.setValue(a),setTimeout(function(){d.instance.refresh()},0)}function c(a){var b=[],c="default";x.editor.codeHighlight&&("js"===a&&(b.push("/libs/CodeMirror/mode/javascript/javascript.js"),c="javascript"),"css"===a&&(b.push("/libs/CodeMirror/mode/css/css.js"),c="css"),"html"===a&&(b.push("/libs/CodeMirror/mode/xml/xml.js"),c="text/html"),"xml"===a&&(b.push("/libs/CodeMirror/mode/xml/xml.js"),c="application/xml"),"php"===a&&(b.push("/libs/CodeMirror/mode/htmlmixed/htmlmixed.js"),b.push("/libs/CodeMirror/mode/xml/xml.js"),b.push("/libs/CodeMirror/mode/javascript/javascript.js"),b.push("/libs/CodeMirror/mode/css/css.js"),b.push("/libs/CodeMirror/mode/clike/clike.js"),b.push("/libs/CodeMirror/mode/php/php.js"),c="application/x-httpd-php"),"java"===a&&(b.push("/libs/CodeMirror/mode/clike/clike.js"),c="text/x-java"),"sql"===a&&(b.push("/libs/CodeMirror/mode/sql/sql.js"),c="text/x-mysql"),"md"===a&&(b.push("/libs/CodeMirror/addon/mode/overlay.js"),b.push("/libs/CodeMirror/mode/xml/xml.js"),b.push("/libs/CodeMirror/mode/markdown/markdown.js"),b.push("/libs/CodeMirror/mode/gfm/gfm.js"),b.push("/libs/CodeMirror/mode/javascript/javascript.js"),b.push("/libs/CodeMirror/mode/css/css.js"),b.push("/libs/CodeMirror/mode/htmlmixed/htmlmixed.js"),b.push("/libs/CodeMirror/mode/clike/clike.js"),b.push("/libs/CodeMirror/mode/shell/shell.js"),b.push("/libs/CodeMirror/mode/meta.js"),c="gfm"),"sh"===a&&(b.push("/libs/CodeMirror/addon/mode/overlay.js"),b.push("/libs/CodeMirror/mode/markdown/markdown.js"),b.push("/libs/CodeMirror/mode/gfm/gfm.js"),b.push("/libs/CodeMirror/mode/javascript/javascript.js"),b.push("/libs/CodeMirror/mode/css/css.js"),b.push("/libs/CodeMirror/mode/htmlmixed/htmlmixed.js"),b.push("/libs/CodeMirror/mode/clike/clike.js"),b.push("/libs/CodeMirror/mode/meta.js"),b.push("/libs/CodeMirror/mode/shell/shell.js"),c="shell")),b.length?(b.push(function(){d.mode(c)}),X(b)):d.mode(c)}var d=this,e=null;this.instance=null,this.enabled=ko.observable(!1),this.content=ko.observable(null),this.mode=ko.observable(null),this.isInteractive=ko.observable(!1),this.mode.subscribe(function(a){a&&(d.instance.setOption("mode",a),e&&(b(e),e=null))}),this.render=function(a){d.mode()?b(a):e=a},this.createInstance=function(b,e,f){var g,h={readOnly:"nocursor",styleActiveLine:!1,viewportMargin:1/0,lineNumbers:x.editor.lineNumbers,lineWrapping:x.editor.lineWrapping,theme:x.editor.theme,matchBrackets:x.editor.matchBrackets,extraKeys:{F11:function(a){a.setOption("fullScreen",!a.getOption("fullScreen"))},Esc:function(a){a.getOption("fullScreen")&&a.setOption("fullScreen",!1)}}};g=CodeMirror.fromTextArea(e,a.extend({},h,f)),g.on("changes",function(a,b){d.content(a.getValue())}),d.instance=g,c(b)}},N=function(){function b(b){var c=a.grep(f.items,function(a,c){if("folder"===b.rdo.type||"parent"===b.rdo.type){if(ha(b.rdo.id,a.rdo.id))return!0;if(b.rdo.id===oa(a.rdo.id))return!0}return a.id===b.id});return b.rdo.attributes.writable&&0===c.length}function d(a){null!==f.hoveredItem&&f.hoveredItem.dragHovered(!1),f.hoveredItem=a,a&&a.dragHovered(!0)}function e(a,b){b?a.addClass(g):a.removeClass(g)}var f=this,g="drop-restricted",h=a("#drag-helper-template");this.items=[],this.hoveredItem=null,this.dragHelper=null,this.isScrolling=!1,this.isScrolled=!1,this.hoveredCssClass="drop-hover",this.makeDraggable=function(b,d){"file"!==b.rdo.type&&"folder"!==b.rdo.type||a(d).draggable({distance:3,cursor:"pointer",cursorAt:{left:Math.floor(h.width()/2),bottom:15},scroll:!1,appendTo:m,containment:l,refreshPositions:!1,helper:function(){var a,d;return d=c.fetchSelectedItems(b.constructor.name).length>1?"ico_multiple":"folder"===b.rdo.type?"ico_folder":"ico_file ico_ext_"+ka(b.rdo.id),a=h.children(".drag-helper").clone(),a.find(".clip").addClass(d),f.dragHelper=a,a},start:function(a,d){f.items=c.fetchSelectedItems(b.constructor.name)},drag:function(b,c){a(this).draggable("option","refreshPositions",f.isScrolling||f.isScrolled),f.isScrolled=!1},stop:function(a,b){f.items=[],f.dragHelper=null}})},this.makeDroppable=function(c,g){"folder"!==c.rdo.type&&"parent"!==c.rdo.type||a(g).droppable({tolerance:"pointer",enableExtendedEvents:c instanceof q,accept:function(a){var b=ko.dataFor(a[0]),c=b?b.rdo.type:null;return"file"===c||"folder"===c},over:function(a,f){setTimeout(function(){d(null),e(f.helper,!1),b(c)||e(f.helper,!0),d(c)},0)},out:function(a,b){d(null),e(b.helper,!1)},drop:function(a,e){return d(null),!!b(c)&&void Fa(f.items,function(a,b){return La(b.rdo,c.id)})}})}},O=function(){this.unselect=!1};this.treeModel=new n,this.itemsModel=new p,this.tableViewModel=new t,this.previewModel=new j,this.headerModel=new w,this.summaryModel=new z,this.filterModel=new A,this.searchModel=new F,this.clipboardModel=new J,this.breadcrumbsModel=new K,this.ddModel=new N,this.selectionModel=new O},T=function(a){return E.translate(a)},U=function(a){function b(a){var b,c=B;switch("list"===D.viewMode()&&(c=D.itemsModel.listSortField()),c){case"type":b=a.cdo.extension||"";break;case"size":b=a.rdo.attributes.size;break;case"modified":b=a.rdo.attributes.modified;break;case"dimensions":b=a.cdo.dimensions||"";break;default:b=a.rdo.attributes.name}return"string"==typeof b&&(f.cases||(b=b.toLowerCase()),b=b.replace(/\s+/g," ")),b}function c(a,b){for(var c=d(a.toString()),e=d(b.toString()),f=0;c[f]&&e[f];f++)if(c[f]!==e[f]){var g=Number(c[f]),h=Number(e[f]);return g==c[f]&&h==e[f]?g-h:c[f]>e[f]?1:-1}return c.length-e.length}function d(a){for(var b,c,d=[],e=0,f=-1,g=0;b=(c=a.charAt(e++)).charCodeAt(0);){var h=46==b||b>=48&&b<=57;h!==g&&(d[++f]="",g=h),d[f]+=c}return d}var e="list"===D.viewMode()?D.itemsModel.listSortOrder():C,f={natural:!0,order:"asc"===e?1:-1,cases:!1};a.sort(function(a,d){var e,g=b(a),h=b(d);return e=g===h?0:void 0===g||void 0===h?0:f.natural&&(isNaN(g)||isNaN(h))?c(g,h):gh?1:0,e*=f.order});for(var g=[],h=a.length;h--;)"folder"===a[h].rdo.type&&(g.push(a[h]),a.splice(h,1));"top"!==x.options.folderPosition&&g.reverse();for(var i=0,j=g.length;i1&&(e=k.write(g.getMessage(),{delay:0,logMessageTemplate:function(a){var b=(g.getProgress(),g.isProcessed()?"striped":"striped animated");return"
"+a+'
'+g.getProgress()+'%
'}}),e.stick(!0)),a.each(b,function(a,b){h=h.then(function(){return c(a,b)}).then(function(a){a&&a.data?g.succeeded():g.failed(),e&&e.setMessage(g.getMessage())})}),h.then(function(){e&&g.isProcessed()&&(e.stick(!1),setTimeout(function(){e.remove()},6e3))}),h.then(function(){"function"==typeof d&&d()})},Ga=function(){if(document.selection&&document.selection.empty)document.selection.empty();else if(window.getSelection){var a=window.getSelection();a.removeAllRanges()}},Ha=function(a){var b=null,c=Ba(a,!0);if(c=k.settings.callbacks.beforeSelectItem(a,c),window.tinyMCEPopup){var d=tinyMCEPopup.getWindowArg("window");return d.document.getElementById(tinyMCEPopup.getWindowArg("input")).value=c,"undefined"!=typeof d.ImageDialog&&(d.ImageDialog.getImageData&&d.ImageDialog.getImageData(),d.ImageDialog.showPreviewImage&&d.ImageDialog.showPreviewImage(c)),void tinyMCEPopup.close()}if(I.param("field_name")&&(parent.document.getElementById(I.param("field_name")).value=c,"undefined"!=typeof parent.tinyMCE&&parent.tinyMCE.activeEditor.windowManager.close(),"undefined"!=typeof parent.$.fn.colorbox&&parent.$.fn.colorbox.close()),"undefined"!=typeof parent.tinyMCE&&"5"===parent.tinyMCE.majorVersion&&(parent.postMessage({mceAction:"FileSelected",content:c}),setTimeout(function(){parent.tinyMCE.activeEditor.windowManager.close()},500)),I.param("ImperaviElementId"))if(window.opener);else{var e=I.param("ImperaviElementId"),f=parent.$("#"+e).redactor("core.getObject");f&&(f.modal.close(),f.buffer.set(),qa(a.attributes.name)?f.insert.html(''):f.insert.html(''+a.attributes.name+""))}if(I.param("CKEditor")&&(window.opener?window.opener.CKEDITOR.tools.callFunction(I.param("CKEditorFuncNum"),c):(parent.CKEDITOR.tools.callFunction(I.param("CKEditorFuncNum"),c),parent.CKEDITOR.tools.callFunction(I.param("CKEditorCleanUpFuncNum")))),window.opener&&"function"==typeof window.opener.SetUrl)if(a.attributes.width){var g=c,h=a.attributes.width,i=a.attributes.height;window.opener.SetUrl(g,h,i)}else window.opener.SetUrl(c);window.opener&&(b=window.opener),window.parent&&window.self!==window.parent&&(b=window.parent),b&&b.postMessage({source:"richfilemanager",resourceObject:a,preview_url:c},"*"),k.settings.callbacks.afterSelectItem(a,c,b)},Ia=function(a){var b=function(b,c){var d=a.id,e=c.getInputValue();if(!e)return void k.error(T("new_filename"));if(!x.options.allowChangeExtensions){var f=ka(a.attributes.name);f.length>0&&(e=e+"."+f)}if(da(d)&&!ca(e)){var g="

"+T("INVALID_FILE_TYPE")+"

";return"ALLOW_LIST"===x.security.extensions.policy&&(g+="

"+T("ALLOWED_FILE_TYPE").replace("%s",x.security.extensions.restrictions.join(", "))+".

"),"DISALLOW_LIST"===x.security.extensions.policy&&(g+="

"+T("DISALLOWED_FILE_TYPE").replace("%s",x.security.extensions.restrictions.join(", "))+".

"),void k.error(g)}za("GET",{mode:"rename",old:d,"new":e}).done(function(a){if(a.data){var b=a.data,e=D.treeModel.findByParam("id",d);if(e&&("folder"===e.rdo.type&&(e.nodeTitle(b.attributes.name),D.treeModel.actualizeNodeObject(e,d,b.id)),"file"===e.rdo.type)){var f=e.parentNode(),g=D.treeModel.createNode(b),h=D.treeModel.findByParam("id",b.id);h&&h.remove(),e.remove(),f&&D.treeModel.appendNodes(f,g)}var i=D.itemsModel.parentItem();if(i&&i.id===d)D.itemsModel.parentItem().id=b.id;else{var j=D.itemsModel.findByParam("id",d);if(j){j.remove();var l=D.itemsModel.findByParam("id",b.id);l&&l.remove();var m=D.itemsModel.createItem(b);D.itemsModel.appendItems(m)}}D.currentPath()===d&&D.itemsModel.loadDataList(b.id),D.previewFile()&&D.previewModel.rdo().id===d&&D.previewModel.applyObject(b),c.closeDialog(),x.options.showConfirmation&&k.success(T("successful_rename"))}}).fail(ba)};k.prompt({message:T("new_filename"),value:x.options.allowChangeExtensions?a.attributes.name:la(a.attributes.name),okBtn:{label:T("action_rename"),autoClose:!1,click:b},cancelBtn:{label:T("cancel")}})},Ja=function(a,b){var c=function(a,c){var d=c.getInputValue();return d?(d=ga(d,"/")+"/",void b(d)):void k.error(T("prompt_foldername"))},d=a.length,e=d>1?T("prompt_move_multiple").replace("%s",d):T("prompt_move");k.prompt({message:e,value:D.currentPath(),okBtn:{label:T("action_move"),autoClose:!1,click:c},cancelBtn:{label:T("cancel")},template:{dialogInput:'
'+T("help_move")+"
"}})},Ka=function(a,b){return za("GET",{mode:"copy",source:a.id,target:b}).done(function(a){if(a.data){var c=a.data;D.addElements(c,b),alertify.clearDialogs(),x.options.showConfirmation&&k.success(T("successful_copied"))}}).fail(ba)},La=function(a,b){return za("GET",{mode:"move",old:a.id,"new":b}).done(function(c){if(c.data){var d=c.data;D.removeElement(a),D.addElements(d,b),D.currentPath()===a.id&&D.itemsModel.loadDataList(d.id),D.previewFile()&&D.previewModel.rdo().id===a.id&&D.previewFile(!1),alertify.clearDialogs(),x.options.showConfirmation&&k.success(T("successful_moved"))}}).fail(ba)},Ma=function(a,b){var c=a.length,d=c>1?T("confirm_delete_multiple").replace("%s",c):T("confirm_delete");k.confirm({message:d,okBtn:{label:T("yes"),click:function(a,c){b()}},cancelBtn:{label:T("no")}})},Na=function(a){return za("GET",{mode:"delete",path:a}).done(function(a){if(a.data){var b=a.data;if(D.removeElement(b),"folder"===b.type&&ha(D.currentPath(),b.id)){var c=na(b.id);D.itemsModel.loadDataList(c)}D.previewFile()&&D.previewModel.rdo().id===b.id&&D.previewFile(!1),x.options.showConfirmation&&k.success(T("successful_delete"))}}).fail(ba)},Oa=function(b){var c={mode:"download",path:b.id};a.fileDownload(Aa(ya("GET",c)),{failCallback:function(b,c,d){var e=a(b).text(),f=a.parseJSON(e);a.isPlainObject(f)&&f.errors&&aa(f.errors)}})},Pa=function(b){var c=a("#fm-js-editor-form").serializeArray();za("POST",c).done(function(a){if(a.data){var b=a.data,c=D.previewModel,d=c.editor.content();c.rdo(b),c.viewer.content(d),c.closeEditor();var e=D.itemsModel.createItem(b),f=D.itemsModel.findByParam("id",b.id);D.itemsModel.objects.replace(f,e),k.success(T("successful_edit"))}}).fail(ba)},Qa=function(a){return za("GET",{mode:"readfile",path:a.id},"text").fail(ba)},Ra=function(a){return za("GET",{mode:"readfolder",path:a}).fail(ba)},Sa=function(a,b){return za("GET",{mode:"seekfolder",path:a,string:b}).fail(ba)},Ta=function(a){return za("GET",{mode:"getinfo",path:a}).fail(ba)},Ua=function(){return za("GET",{mode:"summarize"}).done(function(b){if(b.data){var c=b.data.attributes,d=Z(c.size,!0);if(c.sizeLimit>0){var e=Z(c.sizeLimit,!0),f=100*c.size/c.sizeLimit,g=Math.round(100*f)/100;d+=" ("+g+"%) "+T("of")+" "+e}D.summaryModel.files(c.files),D.summaryModel.folders(c.folders),D.summaryModel.size(d),D.summaryModel.enabled(!0);var h=a("#summary-popup").clone().show();D.summaryModel.enabled(!1),k.alert(h[0].outerHTML)}}).fail(ba)},Va=function(a){var b=function(b,c){var d=c.getInputValue();return d?(d=ga(d,"/")+"/",void Wa(a,d)):void k.error(T("prompt_foldername"))};k.prompt({message:T("prompt_extract"),value:D.currentPath(),okBtn:{label:T("action_extract"),autoClose:!1,click:b},cancelBtn:{label:T("cancel")}})},Wa=function(a,b){za("POST",{mode:"extract",source:a.id,target:b}).done(function(a){a.data&&(D.addElements(a.data,b),alertify.clearDialogs(),x.options.showConfirmation&&k.success(T("successful_extracted")))}).fail(ba)},Xa=function(b,c,d,e){var f=e?e:[d];switch(b){case"select":Ha(d);break;case"download":a.each(f,function(a,b){Oa(b)});break;case"rename":Ia(d);break;case"move":Ja(f,function(a){Fa(f,function(b,c){return La(c,a)})});break;case"delete":Ma(f,function(){Fa(f,function(a,b){return Na(b.id)})});break;case"extract":Va(d);break;case"copy":D.clipboardModel.copy(f);break;case"cut":D.clipboardModel.cut(f);break;case"copyUrl":var g=new Clipboard(c.$selected[0],{text:function(a){return Ea(d)}});g.on("success",function(a){k.success(T("copied")),g.destroy()})}},Ya=function(){return!x.options.browseOnly&&void(x.upload.multiple?w.unbind().click(function(){if(!d("upload"))return k.error(T("NOT_ALLOWED")),!1;var b=null,c=D.currentPath(),e=tmpl("tmpl-fileupload-container",{folder:T("current_folder")+c,info:T("upload_files_number_limit").replace("%s",x.upload.maxNumberOfFiles)+" "+T("upload_file_size_limit").replace("%s",Z(x.upload.fileSizeLimit,!0)),lang:E.getTranslations()});"ALLOW_LIST"===x.security.extensions.policy&&(b=new RegExp("(\\.|\\/)("+x.security.extensions.restrictions.join("|")+")$","i")),k.dialog({message:e,width:"auto",buttons:[{type:"ok",label:T("action_upload"),autoClose:!1,click:function(a,b){g.children(".upload-item").length>0?g.find(".button-start").trigger("click"):k.error(T("upload_choose_file"))}},{label:T("action_select"),closeOnClick:!1,click:function(b,c){a("#fileupload",f).trigger("click")}},{type:"cancel",label:T("close")}]});var f=a(".fm-fileupload-container"),g=a(".dropzone",f),h=a(".dropzone-wrapper",f),i=a("#total-progress",f).children();x.customScrollbar.enabled&&h.mCustomScrollbar({theme:x.customScrollbar.theme,scrollButtons:{enable:x.customScrollbar.button},advanced:{autoExpandHorizontalScroll:!0,updateOnContentResize:!0},callbacks:{onOverflowY:function(){h.find(".mCSB_container").css({"margin-right":h.find(".mCSB_scrollTools").width()})},onOverflowYNone:function(){h.find(".mCSB_container").css({"margin-right":"auto"})}},axis:"y"}),h.on("click",function(b){(b.target===this||a(b.target).parent()[0]===this||b.target===g[0]||a(b.target).parent().hasClass("default-message"))&&a("#fileupload",f).trigger("click")}),g.on("click",".button-start",function(b){var c=a(this),d=c.parent().parent(),e=d.data();e.submit(),c.remove()}),g.on("click",".button-abort",function(b){var c=a(this),d=c.parent().parent(),e=d.data(),f=e.files[0].context;e.abort(),f.find(".error-message").text(T("upload_aborted")),f.addClass("aborted")}),g.on("click",".button-resume",function(b){function d(c){a.blueimp.fileupload.prototype.options.add.call(a("#fileupload")[0],b,c),c.submit()}var e=a(this),f=e.parent().parent(),g=f.data(),h=g.files[0];if(h.chunkUploaded){var i=c+h.serverName;Ta(i).then(function(a){a.data&&(g.uploadedBytes=Number(a.data.attributes.size),g.uploadedBytes||(h.chunkUploaded=void 0),d(g))})}else d(g)}),g.on("click",".button-remove",function(b){var d=a(this),e=d.parent().parent(),f=e.data(),g=f.files[0];g.chunkUploaded&&Na(c+g.serverName),d.closest(".upload-item").remove(),j()}),g.on("click",".button-info",function(b){var c=a(this),d=c.closest(".upload-item");if(d.hasClass("error")){var e=d.find(".error-message");k.error(e.text())}});var j=function(){g.children(".upload-item").length>0?g.addClass("started"):g.removeClass("started")},l=D.filterModel.getExtensions();l&&a("#fileupload").attr("accept",l.map(function(a){return"."+a}).join()),a("#fileupload",f).fileupload({autoUpload:!1,sequentialUploads:!0,dataType:"json",dropZone:g,maxChunkSize:x.upload.chunkSize,url:Aa(),paramName:x.upload.paramName,singleFileUploads:!0,formData:ya("POST",{mode:"upload",path:c}),maxNumberOfFiles:x.upload.maxNumberOfFiles,acceptFileTypes:b,maxFileSize:x.upload.fileSizeLimit,messages:{maxNumberOfFiles:T("upload_files_number_limit").replace("%s",x.upload.maxNumberOfFiles),acceptFileTypes:T("upload_file_type_invalid"),maxFileSize:T("upload_file_too_big")+" "+T("upload_file_size_limit").replace("%s",Z(x.upload.fileSizeLimit,!0))},previewMaxHeight:120,previewMaxWidth:120,previewCrop:!0}).on("fileuploadadd",function(b,c){var d=g.children(".upload-item");a.each(c.files,function(b,e){if(d.length>=x.upload.maxNumberOfFiles)return k.error(T("upload_files_number_limit").replace("%s",x.upload.maxNumberOfFiles),{ +logClass:"fileuploadadd",unique:!0}),!1;e.formattedSize=Z(e.size);var f=a(tmpl("tmpl-upload-item",{file:e,lang:E.getTranslations(),imagesPath:k.settings.baseUrl+"/libs/jQuery-File-Upload/img"}));e.context=f,f.find(".buttons").data(c),f.appendTo(g)}),j()}).on("fileuploadsend",function(b,c){return k.settings.callbacks.beforeSendRequest(c.type,c.formData)===!1?(a.each(c.files,function(a,b){var c=b.context;c.find(".error-message").text(T("NOT_ALLOWED")),c.removeClass("added process").addClass("error")}),!1):void a.each(c.files,function(a,b){var d=b.context;d.removeClass("added aborted error").addClass("process"),b.chunkUploaded&&c.total===c.uploadedBytes&&d.remove()})}).on("fileuploadfail",function(b,c){var d,e=c.jqXHR;d=a.isPlainObject(e.responseJSON)&&e.responseJSON.errors?_(e.responseJSON.errors[0]):T("upload_failed"),a.each(c.files,function(a,b){var c=b.context;c.removeClass("added process").addClass("error"),c.find(".error-message").text(d),c.find(".button-start").remove()})}).on("fileuploaddone",function(b,c){var d=c.result;a.each(c.files,function(a,b){d&&d.data&&d.data[a]&&b.context.remove()})}).on("fileuploadalways",function(b,c){var d=c.result;a.each(c.files,function(a,b){if(d&&d.data&&d.data[a]){var c=d.data[a];D.removeElement(c),D.addElements(c,D.currentPath())}});var e=g.children(".upload-item");0===e.filter(".added").length&&0===e.filter(".process").length&&(0===e.length&&(alertify.clearDialogs(),x.options.showConfirmation&&k.success(T("upload_successful_files"))),e.filter(".error").length&&k.error(T("upload_partially")+"
"+T("upload_failed_details"))),j()}).on("fileuploadchunkdone",function(b,c){var d=c.result;a.each(c.files,function(a,b){if(d.data&&d.data[a]){var c=d.data[a];D.removeElement(c),D.addElements(c,D.currentPath()),b.serverName=c.attributes.name,b.chunkUploaded=1}})}).on("fileuploadprocessalways",function(b,c){a.each(c.files,function(a,b){var c=b.context;"undefined"!=typeof c&&(b.preview&&(c.find(".image").append(b.preview),c.find(".preview").removeClass("file-preview").addClass("image-preview")),b.error&&(c.removeClass("added process").addClass("error"),c.find(".error-message").text(b.error),c.find(".button-start").remove()))})}).on("fileuploadprogress",function(b,c){a.each(c.files,function(a,b){var d=b.context,e=parseInt(c.loaded/c.total*100,10);d.find(".progress-bar").css("width",e+"%")})}).on("fileuploadprogressall",function(a,b){var c=parseInt(b.loaded/b.total*100,10);i.css("width",c+"%")})}):(w.unbind().click(function(){return d("upload")?void a("#newfile").trigger("click"):(k.error(T("NOT_ALLOWED")),!1)}),o.fileupload({autoUpload:!0,dataType:"json",url:Aa(),paramName:x.upload.paramName,maxChunkSize:x.upload.chunkSize}).on("fileuploadadd",function(a,b){w.data(b)}).on("fileuploadsubmit",function(a,b){b.formData=ya("POST",{mode:"upload",path:D.currentPath()}),w.addClass("loading").prop("disabled",!0),w.children("span").text(T("loading_data"))}).on("fileuploadsend",function(a,b){if(k.settings.callbacks.beforeSendRequest(b.type,b.formData)===!1)return k.error(T("NOT_ALLOWED")),!1}).on("fileuploadalways",function(a,b){w.removeData().removeClass("loading").prop("disabled",!1),w.children("span").text(T("action_upload"));var c=b.result;if(c&&c.data){var d=c.data[0];D.removeElement(d),D.addElements(d,D.currentPath()),x.options.showConfirmation&&k.success(T("upload_successful_file"))}}).on("fileuploadchunkdone",function(a,b){var c=b.result;if(c.data&&c.data[0]){var d=c.data[0];D.removeElement(d),D.addElements(d,D.currentPath())}}).on("fileuploadfail",function(b,c){var d,e=c.jqXHR;d=a.isPlainObject(e.responseJSON)&&e.responseJSON.errors?_(e.responseJSON.errors[0]):T("upload_failed"),k.error(d)})))};J(),a(window).resize(k.setDimensions)}}(jQuery),$.fn.richFilemanager=function(a){return this.each(function(){if(void 0===$(this).data("richFilemanager")){var b=new $.richFilemanagerPlugin(this,a);$(this).data("richFilemanager",b)}})},window.location.origin||(window.location.origin=window.location.protocol+"//"+window.location.hostname+(window.location.port?":"+window.location.port:"")); \ No newline at end of file From 5206f7983fe95fba40a175ce8c3f7d3144330eb7 Mon Sep 17 00:00:00 2001 From: Jaehyun Sim Date: Sun, 29 Aug 2021 14:21:45 +0900 Subject: [PATCH 2/5] File name and extension forwarding and file filtering --- package.json | 5 +- src/js/filemanager.js | 9538 +++++++++++++++++++------------------ src/js/filemanager.min.js | 6 +- 3 files changed, 4791 insertions(+), 4758 deletions(-) diff --git a/package.json b/package.json index fc28c933..400c93c5 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,9 @@ "version": "2.7.6", "description": "Highly customizable open-source file manager", "main": "scripts/filemanager.js", + "scripts": { + "build": "grunt" + }, "repository": { "type": "git", "url": "git+https://github.com/servocoder/RichFilemanager.git" @@ -36,4 +39,4 @@ "node-sass": "^4.9.0", "sass": "^1.0.0-beta.5.3" } -} +} \ No newline at end of file diff --git a/src/js/filemanager.js b/src/js/filemanager.js index 9590b6ce..424d7b6f 100644 --- a/src/js/filemanager.js +++ b/src/js/filemanager.js @@ -10,4986 +10,5016 @@ * @copyright Authors */ -(function($) { + (function($) { -$.richFilemanagerPlugin = function(element, pluginOptions) -{ - /** - * Plugin's default options - */ - var defaults = { - baseUrl: '.', // relative path to the FM plugin folder - configUrl: null, - config: {}, // configuration options - callbacks: { - beforeCreateImageUrl: function (resourceObject, url) { - return url; - }, - beforeCreatePreviewUrl: function (resourceObject, url) { - return url; - }, - beforeSelectItem: function (resourceObject, url) { - return url; - }, - afterSelectItem: function (resourceObject, url, contextWindow) {}, - beforeSetRequestParams: function (requestMethod, requestParams) { - return requestParams; - }, - beforeSendRequest: function (requestMethod, requestParams) { - return true; + $.richFilemanagerPlugin = function(element, pluginOptions) + { + /** + * Plugin's default options + */ + var defaults = { + baseUrl: '.', // relative path to the FM plugin folder + configUrl: null, + config: {}, // configuration options + callbacks: { + beforeCreateImageUrl: function (resourceObject, url) { + return url; + }, + beforeCreatePreviewUrl: function (resourceObject, url) { + return url; + }, + beforeSelectItem: function (resourceObject, url) { + return url; + }, + afterSelectItem: function (resourceObject, url, contextWindow) {}, + beforeSetRequestParams: function (requestMethod, requestParams) { + return requestParams; + }, + beforeSendRequest: function (requestMethod, requestParams) { + return true; + } } - } - }; - - /** - * The reference the current instance of the object - */ - var fm = this; - - /** - * Private properties accessible only from inside the plugin - */ - var $container = $(element), // reference to the jQuery version of DOM element the plugin is attached to - $wrapper = $container.children('.fm-wrapper'), - $header = $wrapper.find('.fm-header'), - $uploader = $header.find('.fm-uploader'), - $splitter = $wrapper.children('.fm-splitter'), - $footer = $wrapper.children('.fm-footer'), - $fileinfo = $splitter.children('.fm-fileinfo'), - $filetree = $splitter.children('.fm-filetree'), - $viewItemsWrapper = $fileinfo.find('.view-items-wrapper'), - $previewWrapper = $fileinfo.find('.fm-preview-wrapper'), - $viewItems = $viewItemsWrapper.find('.view-items'), - $uploadButton = $uploader.children('.fm-upload'), - - config = null, // configuration options - fileRoot = '/', // relative files root, may be changed with some query params - apiConnector = null, // API connector URL to perform requests to server - capabilities = [], // allowed actions to perform in FM - configSortField = null, // items sort field name - configSortOrder = null, // items sort order 'asc'/'desc' - fmModel = null, // filemanager knockoutJS model - langModel = null, // language model - globalize = null, // formatting and parsing tool (numbers, dates, etc.) - delayStack = null, // function execution delay manager - - /** variables to keep request options data **/ - fullexpandedFolder = null, // path to be automatically expanded by filetree plugin - - /** service variables **/ - _url_ = purl(), - timeStart = new Date().getTime(); - - /** - * This holds the merged default and user-provided options. - * Plugin's properties will be available through this object like: - * - fm.propertyName from inside the plugin - * - element.data('richFilemanager').propertyName from outside the plugin, where "element" is the element the plugin is attached to; - * @type {{}} - */ - - // The plugin's final settings, contains the merged default and user-provided options (if any) - fm.settings = $.extend(true, defaults, pluginOptions); - - - /*-------------------------------------------------------------------------------------------------------------- - Public methods - Can be called like: - - fm.methodName(arg1, arg2, ... argn) from inside the plugin - - element.data('richFilemanager').publicMethod(arg1, arg2, ... argn) from outside the plugin, - where "element" is the element the plugin is attached to - --------------------------------------------------------------------------------------------------------------*/ - - fm.write = function(message, obj) { - var log = alertify; - var options = $.extend({}, { - reset: true, - delay: 5000, - logMaxItems: 5, - logPosition: 'bottom right', - logContainerClass: 'fm-log', - logMessageTemplate: null, - parent: document.body, - onClick: undefined, - unique: false, - type: 'log' - }, obj); - - // display only one log for the specified 'logClass' - if(options.logClass && options.unique && $('.fm-log').children('.' + options.logClass).length > 0) { - return log; - } - - if(options.reset) log.reset(); - log.parent(options.parent); - log.logDelay(options.delay); - log.logMaxItems(options.logMaxItems); - log.logPosition(options.logPosition); - log.logContainerClass(options.logContainerClass); - log.logMessageTemplate(options.logMessageTemplate); - log[options.type](message, options.onClick); - - var logs = log.getLogs(); - return logs[logs.length-1]; - }; - - fm.error = function(message, options) { - return fm.write(message, $.extend({}, { - type: 'error', - delay: 10000 - }, options)); - }; - - fm.warning = function(message, options) { - return fm.write(message, $.extend({}, { - type: 'warning', - delay: 10000 - }, options)); - }; - - fm.success = function(message, options) { - return fm.write(message, $.extend({}, { - type: 'success', - delay: 6000 - }, options)); - }; - - fm.alert = function(message) { - alertify - .reset() - .dialogContainerClass('fm-popup') - .alert(message); - }; - - fm.confirm = function(obj) { - alertify - .reset() - .dialogWidth(obj.width) - .dialogPersistent(obj.persistent) - .dialogContainerClass('fm-popup') - .confirm(obj.message, obj.okBtn, obj.cancelBtn); - }; - - fm.prompt = function(obj) { - alertify - .reset() - .dialogWidth(obj.width) - .dialogPersistent(obj.persistent) - .dialogContainerClass('fm-popup') - .theme(obj.template) - .prompt(obj.message, obj.value || '', obj.okBtn, obj.cancelBtn); - }; - - fm.dialog = function(obj) { - alertify - .reset() - .dialogWidth(obj.width) - .dialogPersistent(obj.persistent) - .dialogContainerClass('fm-popup') - .dialog(obj.message, obj.buttons); - }; - - // Forces columns to fill the layout vertically. - // Called on initial page load and on resize. - fm.setDimensions = function() { - 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(); - - $splitter.height(newH); - $fileinfo.width(newW); - }; - - fm.console = function() { - if(config.options.logger && arguments) { - [].unshift.call(arguments, new Date().getTime()); - console.log.apply(this, arguments); + }; + + /** + * The reference the current instance of the object + */ + var fm = this; + + /** + * Private properties accessible only from inside the plugin + */ + var $container = $(element), // reference to the jQuery version of DOM element the plugin is attached to + $wrapper = $container.children('.fm-wrapper'), + $header = $wrapper.find('.fm-header'), + $uploader = $header.find('.fm-uploader'), + $splitter = $wrapper.children('.fm-splitter'), + $footer = $wrapper.children('.fm-footer'), + $fileinfo = $splitter.children('.fm-fileinfo'), + $filetree = $splitter.children('.fm-filetree'), + $viewItemsWrapper = $fileinfo.find('.view-items-wrapper'), + $previewWrapper = $fileinfo.find('.fm-preview-wrapper'), + $viewItems = $viewItemsWrapper.find('.view-items'), + $uploadButton = $uploader.children('.fm-upload'), + + config = null, // configuration options + fileRoot = '/', // relative files root, may be changed with some query params + apiConnector = null, // API connector URL to perform requests to server + capabilities = [], // allowed actions to perform in FM + configSortField = null, // items sort field name + configSortOrder = null, // items sort order 'asc'/'desc' + fmModel = null, // filemanager knockoutJS model + langModel = null, // language model + globalize = null, // formatting and parsing tool (numbers, dates, etc.) + delayStack = null, // function execution delay manager + + /** variables to keep request options data **/ + fullexpandedFolder = null, // path to be automatically expanded by filetree plugin + + /** service variables **/ + _url_ = purl(), + timeStart = new Date().getTime(); + + /** + * This holds the merged default and user-provided options. + * Plugin's properties will be available through this object like: + * - fm.propertyName from inside the plugin + * - element.data('richFilemanager').propertyName from outside the plugin, where "element" is the element the plugin is attached to; + * @type {{}} + */ + + // The plugin's final settings, contains the merged default and user-provided options (if any) + fm.settings = $.extend(true, defaults, pluginOptions); + + + /*-------------------------------------------------------------------------------------------------------------- + Public methods + Can be called like: + - fm.methodName(arg1, arg2, ... argn) from inside the plugin + - element.data('richFilemanager').publicMethod(arg1, arg2, ... argn) from outside the plugin, + where "element" is the element the plugin is attached to + --------------------------------------------------------------------------------------------------------------*/ + + fm.write = function(message, obj) { + var log = alertify; + var options = $.extend({}, { + reset: true, + delay: 5000, + logMaxItems: 5, + logPosition: 'bottom right', + logContainerClass: 'fm-log', + logMessageTemplate: null, + parent: document.body, + onClick: undefined, + unique: false, + type: 'log' + }, obj); + + // display only one log for the specified 'logClass' + if(options.logClass && options.unique && $('.fm-log').children('.' + options.logClass).length > 0) { + return log; + } + + if(options.reset) log.reset(); + log.parent(options.parent); + log.logDelay(options.delay); + log.logMaxItems(options.logMaxItems); + log.logPosition(options.logPosition); + log.logContainerClass(options.logContainerClass); + log.logMessageTemplate(options.logMessageTemplate); + log[options.type](message, options.onClick); + + var logs = log.getLogs(); + return logs[logs.length-1]; + }; + + fm.error = function(message, options) { + return fm.write(message, $.extend({}, { + type: 'error', + delay: 10000 + }, options)); + }; + + fm.warning = function(message, options) { + return fm.write(message, $.extend({}, { + type: 'warning', + delay: 10000 + }, options)); + }; + + fm.success = function(message, options) { + return fm.write(message, $.extend({}, { + type: 'success', + delay: 6000 + }, options)); + }; + + fm.alert = function(message) { + alertify + .reset() + .dialogContainerClass('fm-popup') + .alert(message); + }; + + fm.confirm = function(obj) { + alertify + .reset() + .dialogWidth(obj.width) + .dialogPersistent(obj.persistent) + .dialogContainerClass('fm-popup') + .confirm(obj.message, obj.okBtn, obj.cancelBtn); + }; + + fm.prompt = function(obj) { + alertify + .reset() + .dialogWidth(obj.width) + .dialogPersistent(obj.persistent) + .dialogContainerClass('fm-popup') + .theme(obj.template) + .prompt(obj.message, obj.value || '', obj.okBtn, obj.cancelBtn); + }; + + fm.dialog = function(obj) { + alertify + .reset() + .dialogWidth(obj.width) + .dialogPersistent(obj.persistent) + .dialogContainerClass('fm-popup') + .dialog(obj.message, obj.buttons); + }; + + // Forces columns to fill the layout vertically. + // Called on initial page load and on resize. + fm.setDimensions = function() { + 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(); + + $splitter.height(newH); + $fileinfo.width(newW); + }; + + fm.console = function() { + if(config.options.logger && arguments) { + [].unshift.call(arguments, new Date().getTime()); + console.log.apply(this, arguments); + } + }; + + // Reload currently open folder content + fm.refreshFolder = function(applyTreeNode) { + fmModel.loadPath(fmModel.currentPath(), applyTreeNode); + }; + + // Load content of specified relative folder path + fm.loadFolder = function(path, applyTreeNode) { + if (path !== '/') { + path = '/' + trim(path, '/') + '/'; } - }; - - // Reload currently open folder content - fm.refreshFolder = function(applyTreeNode) { - fmModel.loadPath(fmModel.currentPath(), applyTreeNode); - }; - - // Load content of specified relative folder path - fm.loadFolder = function(path, applyTreeNode) { - if (path !== '/') { - path = '/' + trim(path, '/') + '/'; - } - fmModel.loadPath(path, applyTreeNode); - }; - - - /*-------------------------------------------------------------------------------------------------------------- - Private methods - These methods can be called only from inside the plugin like: methodName(arg1, arg2, ... argn) - --------------------------------------------------------------------------------------------------------------*/ - - /** - * The "constructor" method that gets called when the object is created - */ - var construct = function() { - var deferred = $.Deferred(); - - deferred - .then(function() { - return configure(); - }) - .then(function() { - return localize(); - }) - .then(function(conf_d, conf_u) { - return performInitialRequest(); - }) - .then(function() { - return includeTemplates(); - }) - .then(function() { - includeAssets(function() { - initialize(); - }); - }); - - deferred.resolve(); - }; - - var configure = function() { - return $.when(loadConfigFile('default'), loadConfigFile('user')).done(function(confd, confu) { - var config_default = confd[0]; - var config_user = confu[0]; - - // remove version from user config file - if (config_user !== undefined && config_user !== null) { - delete config_user.version; - } - // merge default config and user config file - config = $.extend({}, config_default, config_user); - - // setup apiConnector - if(config.api.connectorUrl) { - apiConnector = config.api.connectorUrl; - } else { - var connectorUrl = location.origin + location.pathname; - var langConnector = 'connectors/' + config.api.lang + '/filemanager.' + config.api.lang; - - // for url like http://site.com/index.html - if(getExtension(connectorUrl).length > 0) { - connectorUrl = connectorUrl.substring(0, connectorUrl.lastIndexOf('/') + 1); - } - apiConnector = connectorUrl + langConnector; - } - }); - }; - - // performs initial request to server to retrieve initial params - var performInitialRequest = function () { - return buildAjaxRequest('GET', { - mode: 'initiate' - }).done(function (response) { - if (response.data) { - var serverConfig = response.data.attributes.config; - // configuration options retrieved from the server - $.each(serverConfig, function (section, options) { - $.each(options, function (param, value) { - if (value === null) { - return true; - } - if (config[section] === undefined) { - config[section] = []; - } - config[section][param] = value; + fmModel.loadPath(path, applyTreeNode); + }; + + + /*-------------------------------------------------------------------------------------------------------------- + Private methods + These methods can be called only from inside the plugin like: methodName(arg1, arg2, ... argn) + --------------------------------------------------------------------------------------------------------------*/ + + /** + * The "constructor" method that gets called when the object is created + */ + var construct = function() { + var deferred = $.Deferred(); + + deferred + .then(function() { + return configure(); + }) + .then(function() { + return localize(); + }) + .then(function(conf_d, conf_u) { + return performInitialRequest(); + }) + .then(function() { + return includeTemplates(); + }) + .then(function() { + includeAssets(function() { + initialize(); }); }); - - // Overrides client-side capabilities list to improve the compatibility for connectors. - // If a connector doesn't support a new feature, the plugin will mask this feature to avoid error. - if (serverConfig.options && serverConfig.options.capabilities) { - config.options.capabilities = serverConfig.options.capabilities; - } - } - }).fail(function (xhr) { - fm.error('Unable to perform initial request to server.'); - handleAjaxError(xhr); - }).then(function (response) { - if (response.errors) { - return $.Deferred().reject(); - } - }); - }; - - // localize messages based on configuration or URL value - var localize = function() { - langModel = new LangModel(); - - return $.ajax() - .then(function() { - var urlLangCode = _url_.param('langCode'); - if(urlLangCode) { - // try to load lang file based on langCode in query params - return file_exists(langModel.buildLangFileUrl(urlLangCode)) - .done(function() { - langModel.setLang(urlLangCode); - }) - .fail(function() { - setTimeout(function() { - fm.error('Given language file (' + langModel.buildLangFileUrl(urlLangCode) + ') does not exist!'); - }, 500); - }); - } else { - langModel.setLang(config.language.default); - } - }) - .then(function() { - // append query param to prevent caching - var langFileUrl = langModel.buildLangFileUrl(langModel.getLang()) + '?_=' + new Date().getTime(); - - return $.ajax({ - type: 'GET', - url: langFileUrl, - dataType: 'json' - }).done(function(jsonTrans) { - langModel.setTranslations(jsonTrans); - }); - }) - .then(function() { - // trim language code to first 2 chars - var lang = langModel.getLang().substr(0, 2), - baseUrl = fm.settings.baseUrl; - - return $.when( - $.get(baseUrl + '/libs/cldrjs/cldr-dates/' + lang + '/ca-gregorian.json'), - $.get(baseUrl + '/libs/cldrjs/cldr-numbers/' + lang + '/numbers.json'), - $.get(baseUrl + '/libs/cldrjs/cldr-core/supplemental/likelySubtags.json'), - $.get(baseUrl + '/libs/cldrjs/cldr-core/supplemental/timeData.json'), - $.get(baseUrl + '/libs/cldrjs/cldr-core/supplemental/weekData.json') - ).fail(function () { - fm.error('CLDR files for "' + lang + '" language do not exist!'); - }).then(function () { - // Normalize $.get results, we only need the JSON, not the request statuses. - return [].slice.apply(arguments, [0]).map(function (result) { - return result[0]; + + deferred.resolve(); + }; + + var configure = function() { + return $.when(loadConfigFile('default'), loadConfigFile('user')).done(function(confd, confu) { + var config_default = confd[0]; + var config_user = confu[0]; + + // remove version from user config file + if (config_user !== undefined && config_user !== null) { + delete config_user.version; + } + // merge default config and user config file + config = $.extend({}, config_default, config_user); + + // setup apiConnector + if(config.api.connectorUrl) { + apiConnector = config.api.connectorUrl; + } else { + var connectorUrl = location.origin + location.pathname; + var langConnector = 'connectors/' + config.api.lang + '/filemanager.' + config.api.lang; + + // for url like http://site.com/index.html + if(getExtension(connectorUrl).length > 0) { + connectorUrl = connectorUrl.substring(0, connectorUrl.lastIndexOf('/') + 1); + } + apiConnector = connectorUrl + langConnector; + } + }); + }; + + // performs initial request to server to retrieve initial params + var performInitialRequest = function () { + return buildAjaxRequest('GET', { + mode: 'initiate' + }).done(function (response) { + if (response.data) { + var serverConfig = response.data.attributes.config; + // configuration options retrieved from the server + $.each(serverConfig, function (section, options) { + $.each(options, function (param, value) { + if (value === null) { + return true; + } + if (config[section] === undefined) { + config[section] = []; + } + config[section][param] = value; + }); + }); + + // Overrides client-side capabilities list to improve the compatibility for connectors. + // If a connector doesn't support a new feature, the plugin will mask this feature to avoid error. + if (serverConfig.options && serverConfig.options.capabilities) { + config.options.capabilities = serverConfig.options.capabilities; + } + } + }).fail(function (xhr) { + fm.error('Unable to perform initial request to server.'); + handleAjaxError(xhr); + }).then(function (response) { + if (response.errors) { + return $.Deferred().reject(); + } + }); + }; + + // localize messages based on configuration or URL value + var localize = function() { + langModel = new LangModel(); + + return $.ajax() + .then(function() { + var urlLangCode = _url_.param('langCode'); + if(urlLangCode) { + // try to load lang file based on langCode in query params + return file_exists(langModel.buildLangFileUrl(urlLangCode)) + .done(function() { + langModel.setLang(urlLangCode); + }) + .fail(function() { + setTimeout(function() { + fm.error('Given language file (' + langModel.buildLangFileUrl(urlLangCode) + ') does not exist!'); + }, 500); + }); + } else { + langModel.setLang(config.language.default); + } + }) + .then(function() { + // append query param to prevent caching + var langFileUrl = langModel.buildLangFileUrl(langModel.getLang()) + '?_=' + new Date().getTime(); + + return $.ajax({ + type: 'GET', + url: langFileUrl, + dataType: 'json' + }).done(function(jsonTrans) { + langModel.setTranslations(jsonTrans); + }); + }) + .then(function() { + // trim language code to first 2 chars + var lang = langModel.getLang().substr(0, 2), + baseUrl = fm.settings.baseUrl; + + return $.when( + $.get(baseUrl + '/libs/cldrjs/cldr-dates/' + lang + '/ca-gregorian.json'), + $.get(baseUrl + '/libs/cldrjs/cldr-numbers/' + lang + '/numbers.json'), + $.get(baseUrl + '/libs/cldrjs/cldr-core/supplemental/likelySubtags.json'), + $.get(baseUrl + '/libs/cldrjs/cldr-core/supplemental/timeData.json'), + $.get(baseUrl + '/libs/cldrjs/cldr-core/supplemental/weekData.json') + ).fail(function () { + fm.error('CLDR files for "' + lang + '" language do not exist!'); + }).then(function () { + // Normalize $.get results, we only need the JSON, not the request statuses. + return [].slice.apply(arguments, [0]).map(function (result) { + return result[0]; + }); + }).then(Globalize.load).then(function () { + globalize = Globalize(lang); }); - }).then(Globalize.load).then(function () { - globalize = Globalize(lang); }); + }; + + var includeTemplates = function() { + return $.when(loadTemplate('upload-container'), loadTemplate('upload-item')).done(function(uc, ui) { + var tmpl_upload_container = uc[0]; + var tmpl_upload_item = ui[0]; + + $wrapper + .append(tmpl_upload_container) + .append(tmpl_upload_item); }); - }; - - var includeTemplates = function() { - return $.when(loadTemplate('upload-container'), loadTemplate('upload-item')).done(function(uc, ui) { - var tmpl_upload_container = uc[0]; - var tmpl_upload_item = ui[0]; - - $wrapper - .append(tmpl_upload_container) - .append(tmpl_upload_item); - }); - }; - - var includeAssets = function(callback) { - var primary = [], - secondary = []; - - // theme defined in configuration file - primary.push('/themes/' + config.options.theme + '/styles/theme.css'); - - if(config.viewer.image.lazyLoad) { - primary.push('/libs/lazyload/dist/lazyload.min.js'); - } - if(config.customScrollbar.enabled) { - primary.push('/libs/custom-scrollbar-plugin/jquery.mCustomScrollbar.min.css'); - primary.push('/libs/custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js'); - } - - // add callback on loaded assets and inject primary ones - primary.push(callback); - loadAssets(primary); - - // Loading CodeMirror if enabled for online edition - if(config.editor.enabled) { - var editorTheme = config.editor.theme; - if (editorTheme && editorTheme !== 'default') { - secondary.push('/libs/CodeMirror/theme/' + editorTheme + '.css'); + }; + + var includeAssets = function(callback) { + var primary = [], + secondary = []; + + // theme defined in configuration file + primary.push('/themes/' + config.options.theme + '/styles/theme.css'); + + if(config.viewer.image.lazyLoad) { + primary.push('/libs/lazyload/dist/lazyload.min.js'); } - secondary.push('/libs/CodeMirror/lib/codemirror.css'); - secondary.push('/libs/CodeMirror/lib/codemirror.js'); - secondary.push('/libs/CodeMirror/addon/selection/active-line.js'); - secondary.push('/libs/CodeMirror/addon/display/fullscreen.css'); - secondary.push('/libs/CodeMirror/addon/display/fullscreen.js'); - } - - // Load Markdown-it, if enabled. For .md to HTML rendering: - if(config.viewer.markdownRenderer.enabled) { - secondary.push('/src/css/fm-markdown.css'); - secondary.push('/libs/markdown-it/markdown-it.min.js'); - secondary.push('/libs/markdown-it/default.min.css'); - secondary.push('/libs/markdown-it/highlight.min.js'); - secondary.push('/libs/markdown-it/markdown-it-footnote.min.js'); - secondary.push('/libs/markdown-it/markdown-it-replace-link.min.js'); - } - - if(!config.options.browseOnly) { - // Loading jquery file upload library - secondary.push('/src/js/libs-fileupload.js'); - - if(config.upload.multiple) { - secondary.push('/libs/jQuery-File-Upload/css/dropzone.css'); - } - } - - if(secondary.length) { - loadAssets(secondary); - } - }; - - var initialize = function () { - delayStack = new DelayStack(); - - // reads capabilities from config files if exists else apply default settings - capabilities = config.options.capabilities || ['upload', 'select', 'download', 'rename', 'copy', 'move', 'delete', 'extract', 'createFolder']; - - // If the server is in read only mode, set the GUI to browseOnly: - if (config.security.readOnly) { - config.options.browseOnly = true; - } - - // Set default upload parameter - if (!config.upload.paramName) { - config.upload.paramName = 'files'; - } - - // defines sort params - var chunks = []; - if(config.options.fileSorting) { - chunks = config.options.fileSorting.toLowerCase().split('_'); - } - - configSortField = chunks[0] || 'name'; - configSortOrder = chunks[1] || 'asc'; - - // changes files root to restrict the view to a given folder - var exclusiveFolder = _url_.param('exclusiveFolder'); - if(exclusiveFolder) { - fileRoot = '/' + exclusiveFolder + '/'; - fileRoot = normalizePath(fileRoot); - } - - // get folder that should be expanded after filemanager is loaded - var expandedFolder = _url_.param('expandedFolder'); - if(expandedFolder) { - fullexpandedFolder = fileRoot + expandedFolder + '/'; - fullexpandedFolder = normalizePath(fullexpandedFolder); - } - - // Activates knockout.js - fmModel = new FmModel(); - ko.applyBindings(fmModel); - - fmModel.itemsModel.initiateLazyLoad(); - fmModel.filterModel.setName(_url_.param('filter')); - - ko.bindingHandlers.toggleNodeVisibility = { - init: function (element, valueAccessor) { - var node = valueAccessor(); - $(element).toggle(node.isExpanded()); - }, - update: function (element, valueAccessor) { - var node = valueAccessor(); - if(node.isSliding() === false) { - return false; - } - if(node.isExpanded() === false) { - $(element).slideDown(config.filetree.expandSpeed, function() { - node.isSliding(false); - node.isExpanded(true); - }); - } - if(node.isExpanded() === true) { - $(element).slideUp(config.filetree.expandSpeed, function() { - node.isSliding(false); - node.isExpanded(false); - }); - } - } - }; - - ko.bindingHandlers.draggableView = { - init: function(element, valueAccessor, allBindingsAccessor) { - fmModel.ddModel.makeDraggable(valueAccessor(), element); - } - }; - - ko.bindingHandlers.droppableView = { - init: function(element, valueAccessor, allBindingsAccessor) { - fmModel.ddModel.makeDroppable(valueAccessor(), element); - } - }; - - ko.bindingHandlers.draggableTree = { - init: function(element, valueAccessor, allBindingsAccessor) { - fmModel.ddModel.makeDraggable(valueAccessor(), element); - } - }; - - ko.bindingHandlers.droppableTree = { - init: function(element, valueAccessor, allBindingsAccessor) { - fmModel.ddModel.makeDroppable(valueAccessor(), element); - } - }; - - $wrapper.mousewheel(function(e) { - if (!fmModel.ddModel.dragHelper) { - return true; + if(config.customScrollbar.enabled) { + primary.push('/libs/custom-scrollbar-plugin/jquery.mCustomScrollbar.min.css'); + primary.push('/libs/custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js'); } - - var $panes, - $obstacle = null; - - if (config.customScrollbar.enabled) { - $panes = $([$viewItemsWrapper[0], $filetree[0]]); - } else { - $panes = $splitter.children('.splitter-pane'); + + // add callback on loaded assets and inject primary ones + primary.push(callback); + loadAssets(primary); + + // Loading CodeMirror if enabled for online edition + if(config.editor.enabled) { + var editorTheme = config.editor.theme; + if (editorTheme && editorTheme !== 'default') { + secondary.push('/libs/CodeMirror/theme/' + editorTheme + '.css'); + } + secondary.push('/libs/CodeMirror/lib/codemirror.css'); + secondary.push('/libs/CodeMirror/lib/codemirror.js'); + secondary.push('/libs/CodeMirror/addon/selection/active-line.js'); + secondary.push('/libs/CodeMirror/addon/display/fullscreen.css'); + secondary.push('/libs/CodeMirror/addon/display/fullscreen.js'); } - - $panes.each(function(i) { - var $pane = $(this), - top = $pane.offset().top, - left = $pane.offset().left; - - if ((e.offsetY >= top && e.offsetY <= top + $pane.height()) && - (e.offsetX >= left && e.offsetX <= left + $pane.width())) { - $obstacle = $pane; - return false; - } - }); - - // no one appropriate obstacle is overlapped - if ($obstacle === null) { - return false; + + // Load Markdown-it, if enabled. For .md to HTML rendering: + if(config.viewer.markdownRenderer.enabled) { + secondary.push('/src/css/fm-markdown.css'); + secondary.push('/libs/markdown-it/markdown-it.min.js'); + secondary.push('/libs/markdown-it/default.min.css'); + secondary.push('/libs/markdown-it/highlight.min.js'); + secondary.push('/libs/markdown-it/markdown-it-footnote.min.js'); + secondary.push('/libs/markdown-it/markdown-it-replace-link.min.js'); } - - if (config.customScrollbar.enabled) { - var $scrollBar = $obstacle.find('.mCSB_scrollTools_vertical'), - directionSign = (e.deltaY === 1) ? '+' : '-'; - - if ($scrollBar.is(':visible')) { - $obstacle.mCustomScrollbar('scrollTo', [directionSign + '=250', 0], { - scrollInertia: 500, - scrollEasing: 'easeOut', - callbacks: true - }); + + if(!config.options.browseOnly) { + // Loading jquery file upload library + secondary.push('/src/js/libs-fileupload.js'); + + if(config.upload.multiple) { + secondary.push('/libs/jQuery-File-Upload/css/dropzone.css'); } - } else { - if ($obstacle[0].scrollHeight > $obstacle[0].clientHeight) { - var scrollPosition = $obstacle.scrollTop(); - var scrollOffset = scrollPosition - (200 * e.deltaY); - - fmModel.ddModel.isScrolling = true; - scrollOffset = (scrollOffset < 0) ? 0 : scrollOffset; - $obstacle.stop().animate({scrollTop: scrollOffset}, 100, 'linear', function() { - fmModel.ddModel.isScrolling = false; - fmModel.ddModel.isScrolled = true; - }); - } } - }); - - $viewItems.selectable({ - filter: 'li:not(.directory-parent), tbody > tr:not(.directory-parent)', - cancel: '.directory-parent, thead', - disabled: !config.manager.selection.enabled, - 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); - koItem.selected(true); - }, - unselected: function(event, ui) { - var koItem = ko.dataFor(ui.unselected); - koItem.selected(false); + + if(secondary.length) { + loadAssets(secondary); } - }); - - $fileinfo.contextMenu({ - selector: '.view-items', - zIndex: 10, - // wrap options with "build" allows to get item element - build: function ($triggerElement, e) { - var contextMenuItems = { - createFolder: { - name: lg('create_folder'), - className: 'create-folder' - }, - paste: { - name: lg('clipboard_paste'), - className: 'paste', - disabled: function (key, options) { - return fmModel.clipboardModel.isEmpty(); - } + }; + + var initialize = function () { + delayStack = new DelayStack(); + + // reads capabilities from config files if exists else apply default settings + capabilities = config.options.capabilities || ['upload', 'select', 'download', 'rename', 'copy', 'move', 'delete', 'extract', 'createFolder']; + + // If the server is in read only mode, set the GUI to browseOnly: + if (config.security.readOnly) { + config.options.browseOnly = true; + } + + // Set default upload parameter + if (!config.upload.paramName) { + config.upload.paramName = 'files'; + } + + // defines sort params + var chunks = []; + if(config.options.fileSorting) { + chunks = config.options.fileSorting.toLowerCase().split('_'); + } + + configSortField = chunks[0] || 'name'; + configSortOrder = chunks[1] || 'asc'; + + // changes files root to restrict the view to a given folder + var exclusiveFolder = _url_.param('exclusiveFolder'); + if(exclusiveFolder) { + fileRoot = '/' + exclusiveFolder + '/'; + fileRoot = normalizePath(fileRoot); + } + + // get folder that should be expanded after filemanager is loaded + var expandedFolder = _url_.param('expandedFolder'); + if(expandedFolder) { + fullexpandedFolder = fileRoot + expandedFolder + '/'; + fullexpandedFolder = normalizePath(fullexpandedFolder); + } + + // Activates knockout.js + fmModel = new FmModel(); + ko.applyBindings(fmModel); + + fmModel.itemsModel.initiateLazyLoad(); + fmModel.filterModel.setName(_url_.param('filter')); + + ko.bindingHandlers.toggleNodeVisibility = { + init: function (element, valueAccessor) { + var node = valueAccessor(); + $(element).toggle(node.isExpanded()); + }, + update: function (element, valueAccessor) { + var node = valueAccessor(); + if(node.isSliding() === false) { + return false; } - }; - - if (!fmModel.clipboardModel.enabled() || config.options.browseOnly === true) { - delete contextMenuItems.paste; + if(node.isExpanded() === false) { + $(element).slideDown(config.filetree.expandSpeed, function() { + node.isSliding(false); + node.isExpanded(true); + }); + } + if(node.isExpanded() === true) { + $(element).slideUp(config.filetree.expandSpeed, function() { + node.isSliding(false); + node.isExpanded(false); + }); + } + } + }; + + ko.bindingHandlers.draggableView = { + init: function(element, valueAccessor, allBindingsAccessor) { + fmModel.ddModel.makeDraggable(valueAccessor(), element); + } + }; + + ko.bindingHandlers.droppableView = { + init: function(element, valueAccessor, allBindingsAccessor) { + fmModel.ddModel.makeDroppable(valueAccessor(), element); + } + }; + + ko.bindingHandlers.draggableTree = { + init: function(element, valueAccessor, allBindingsAccessor) { + fmModel.ddModel.makeDraggable(valueAccessor(), element); + } + }; + + ko.bindingHandlers.droppableTree = { + init: function(element, valueAccessor, allBindingsAccessor) { + fmModel.ddModel.makeDroppable(valueAccessor(), element); + } + }; + + $wrapper.mousewheel(function(e) { + if (!fmModel.ddModel.dragHelper) { + return true; } - if (!hasCapability('createFolder') || config.options.browseOnly === true) { - delete contextMenuItems.createFolder; + + var $panes, + $obstacle = null; + + if (config.customScrollbar.enabled) { + $panes = $([$viewItemsWrapper[0], $filetree[0]]); + } else { + $panes = $splitter.children('.splitter-pane'); } - // prevent the creation of context menu - if ($.isEmptyObject(contextMenuItems)) { + + $panes.each(function(i) { + var $pane = $(this), + top = $pane.offset().top, + left = $pane.offset().left; + + if ((e.offsetY >= top && e.offsetY <= top + $pane.height()) && + (e.offsetX >= left && e.offsetX <= left + $pane.width())) { + $obstacle = $pane; + return false; + } + }); + + // no one appropriate obstacle is overlapped + if ($obstacle === null) { return false; - } - - return { - appendTo: '.fm-container', - items: contextMenuItems, - reposition: false, - callback: function(itemKey, options) { - switch(itemKey) { - case 'createFolder': - fmModel.headerModel.createFolder(); - break; - - case 'paste': - fmModel.clipboardModel.paste(); - break; - } + } + + if (config.customScrollbar.enabled) { + var $scrollBar = $obstacle.find('.mCSB_scrollTools_vertical'), + directionSign = (e.deltaY === 1) ? '+' : '-'; + + if ($scrollBar.is(':visible')) { + $obstacle.mCustomScrollbar('scrollTo', [directionSign + '=250', 0], { + scrollInertia: 500, + scrollEasing: 'easeOut', + callbacks: true + }); + } + } else { + if ($obstacle[0].scrollHeight > $obstacle[0].clientHeight) { + var scrollPosition = $obstacle.scrollTop(); + var scrollOffset = scrollPosition - (200 * e.deltaY); + + fmModel.ddModel.isScrolling = true; + scrollOffset = (scrollOffset < 0) ? 0 : scrollOffset; + $obstacle.stop().animate({scrollTop: scrollOffset}, 100, 'linear', function() { + fmModel.ddModel.isScrolling = false; + fmModel.ddModel.isScrolled = true; + }); } } - } - }); - - if(config.extras.extra_js) { - for(var i=0; i tr:not(.directory-parent)', + cancel: '.directory-parent, thead', + disabled: !config.manager.selection.enabled, + appendTo: $viewItems, + start: function(event, ui) { + clearSelection(); + fmModel.itemsModel.isSelecting(true); }, - advanced: { - autoExpandHorizontalScroll: true, - updateOnContentResize: true, - updateOnSelectorChange: '.fm-preview-viewer' - } - }); - - $viewItemsWrapper.mCustomScrollbar({ - theme: config.customScrollbar.theme, - scrollButtons: { - enable: config.customScrollbar.button + stop: function(event, ui) { + fmModel.itemsModel.isSelecting(false); }, - advanced: { - autoExpandHorizontalScroll:true, - updateOnContentResize: true, - updateOnSelectorChange: '.grid, .list' + selected: function(event, ui) { + var koItem = ko.dataFor(ui.selected); + koItem.selected(true); }, - callbacks: { - onScrollStart: function() { - if (!fmModel.itemsModel.continiousSelection()) { - this.yStartPosition = this.mcs.top; - this.yStartTime = (new Date()).getTime(); + unselected: function(event, ui) { + var koItem = ko.dataFor(ui.unselected); + koItem.selected(false); + } + }); + + $fileinfo.contextMenu({ + selector: '.view-items', + zIndex: 10, + // wrap options with "build" allows to get item element + build: function ($triggerElement, e) { + var contextMenuItems = { + createFolder: { + name: lg('create_folder'), + className: 'create-folder' + }, + paste: { + name: lg('clipboard_paste'), + className: 'paste', + disabled: function (key, options) { + return fmModel.clipboardModel.isEmpty(); + } } - fmModel.ddModel.isScrolling = true; + }; + + if (!fmModel.clipboardModel.enabled() || config.options.browseOnly === true) { + delete contextMenuItems.paste; + } + if (!hasCapability('createFolder') || config.options.browseOnly === true) { + delete contextMenuItems.createFolder; + } + // prevent the creation of context menu + if ($.isEmptyObject(contextMenuItems)) { + return false; + } + + return { + appendTo: '.fm-container', + items: contextMenuItems, + reposition: false, + callback: function(itemKey, options) { + switch(itemKey) { + case 'createFolder': + fmModel.headerModel.createFolder(); + break; + + case 'paste': + fmModel.clipboardModel.paste(); + break; + } + } + } + } + }); + + if(config.extras.extra_js) { + for(var i=0; i 400) { + callbacks: { + onScrollStart: function() { + fmModel.ddModel.isScrolling = true; + }, + onScroll: function() { + fmModel.ddModel.isScrolling = false; + } + }, + axis: 'yx' + }); + + $previewWrapper.mCustomScrollbar({ + theme: config.customScrollbar.theme, + scrollButtons: { + enable: config.customScrollbar.button + }, + advanced: { + autoExpandHorizontalScroll: true, + updateOnContentResize: true, + updateOnSelectorChange: '.fm-preview-viewer' + } + }); + + $viewItemsWrapper.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(); } - - // set flag if selection lasso is active - if (fmModel.itemsModel.isSelecting()) { - fmModel.itemsModel.continiousSelection(true); + fmModel.ddModel.isScrolling = true; + }, + onScroll: function() { + fmModel.ddModel.isScrolling = false; + fmModel.ddModel.isScrolled = true; + }, + 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); + } + + if (fmModel.itemsModel.lazyLoad) { + fmModel.itemsModel.lazyLoad.handleScroll(); // use throttle } - - var yIncrement = Math.abs(this.mcs.top) - Math.abs(this.yStartPosition); - $viewItems.selectable('repositionCssHelper', yIncrement, 0); - } - - if (fmModel.itemsModel.lazyLoad) { - fmModel.itemsModel.lazyLoad.handleScroll(); // use throttle } - } - }, - axis: 'y', - alwaysShowScrollbar: 0 - }); - } - - // add useragent string to html element for IE 10/11 detection - var doc = document.documentElement; - doc.setAttribute('data-useragent', navigator.userAgent); - - if(config.options.logger) { - var timeEnd = new Date().getTime(); - var time = timeEnd - timeStart; - console.log('Total execution time : ' + time + ' ms'); - } - - var $loading = $container.find('.fm-loading-wrap'); - // remove loading screen div - $loading.fadeOut(800, function() { - fm.setDimensions(); - }); - fm.setDimensions(); - }; - - /** - * Language model - * - * @constructor - */ - var LangModel = function() { - var currentLang = null, - translationsHash = {}, - translationsPath = fm.settings.baseUrl + '/languages/'; - - this.buildLangFileUrl = function(code) { - return translationsPath + code + '.json'; - }; - - this.setLang = function(code) { - currentLang = code; - }; - - this.getLang = function() { - return currentLang; - }; - - this.setTranslations = function(json) { - translationsHash = json; - }; - - this.getTranslations = function() { - return translationsHash; - }; - - this.translate = function(key) { - return translationsHash[key]; - }; - }; - - /** - * DelayStack - * Delays execution of functions that is passed as argument - * - * @constructor - */ - var DelayStack = function() { - var hash = {}, - delay_stack = this; - - this.push = function(name, callback, ms) { - delay_stack.removeTimer(name); - hash[name] = setTimeout(callback, ms); - }; - - this.getTimer = function(name) { - return hash[name]; - }; - - this.removeTimer = function(name) { - if (hash[name]) { - clearTimeout(hash[name]); - delete hash[name]; - } - }; - }; - - /** - * Knockout general model - * - * @constructor - */ - var FmModel = function() { - var model = this; - this.config = ko.observable(config); - this.loadingView = ko.observable(true); - this.previewFile = ko.observable(false); - this.viewMode = ko.observable(config.manager.defaultViewMode); - this.currentPath = ko.observable(fileRoot); - this.browseOnly = ko.observable(config.options.browseOnly); - this.previewModel = ko.observable(null); - this.currentLang = langModel.getLang(); - this.lg = langModel.getTranslations(); - - this.previewFile.subscribe(function (enabled) { - if (!enabled) { - // close editor upon disabling preview - model.previewModel.closeEditor(); - - // update content of descriptive panel - if (model.itemsModel.descriptivePanel.rdo().id === model.previewModel.rdo().id) { - model.itemsModel.descriptivePanel.render(model.previewModel.viewer.content()); - } - } - }); - - this.isCapable = function(capability) { - return hasCapability(capability); - }; - - this.loadPath = function (targetPath, applyTreeNode) { - var targetNode, - folderLoader = new FolderAjaxLoader(targetPath); - - if (applyTreeNode) { - targetNode = fmModel.treeModel.findByParam('id', targetPath); - } - if (targetNode) { - folderLoader.setPreloader(fmModel.treeModel.getPreloader(targetNode)) - } - - folderLoader - .setPreloader(model.itemsModel.getPreloader()) - .setDataHandler(function (resourceObjects, targetPath) { - if (targetNode) { - fmModel.treeModel.addNodes(resourceObjects, targetNode, true); - } - model.itemsModel.addItems(resourceObjects, targetPath, true); - model.searchModel.clearInput(); - }) - .load(function () { - return readFolder(targetPath); + }, + axis: 'y', + alwaysShowScrollbar: 0 }); - }; - - this.addElements = function (resourceObjects, targetPath, reset) { - // handle tree nodes - var targetNode = model.treeModel.findByParam('id', targetPath); - if (targetNode) { - model.treeModel.addNodes(resourceObjects, targetNode, reset); - } - - // handle view objects - if (model.currentPath() === targetPath) { - model.itemsModel.addItems(resourceObjects, targetPath, reset); - } - }; - - this.removeElement = function (resourceObject) { - // handle tree nodes - var treeNode = model.treeModel.findByParam('id', resourceObject.id); - if (treeNode) { - treeNode.remove(); - } - - // handle view objects - var viewItem = model.itemsModel.findByParam('id', resourceObject.id); - if (viewItem) { - viewItem.remove(); - } - }; - - // fetch selected view items OR tree nodes - this.fetchSelectedItems = function(instanceName) { - var selectedNodes, selectedItems; - - if (instanceName === ItemObject.name) { - return model.itemsModel.getSelected(); } - if (instanceName === TreeNodeModel.name) { - return model.treeModel.getSelected(); + + // add useragent string to html element for IE 10/11 detection + var doc = document.documentElement; + doc.setAttribute('data-useragent', navigator.userAgent); + + if(config.options.logger) { + var timeEnd = new Date().getTime(); + var time = timeEnd - timeStart; + console.log('Total execution time : ' + time + ' ms'); } - if (!instanceName) { - selectedNodes = model.treeModel.getSelected(); - selectedItems = model.itemsModel.getSelected(); - - return (selectedItems.length > 0) ? selectedItems : selectedNodes; - } - throw new Error('Unknown item type.'); - }; - - // fetch resource objects out of the selected items - this.fetchSelectedObjects = function(item) { - var objects = []; - $.each(model.fetchSelectedItems(item.constructor.name), function(i, itemObject) { - objects.push(itemObject.rdo); + + var $loading = $container.find('.fm-loading-wrap'); + // remove loading screen div + $loading.fadeOut(800, function() { + fm.setDimensions(); }); - return objects; + fm.setDimensions(); }; - - // check whether view item can be opened based on the event and configuration options - function isItemOpenable(event) { - // selecting with Ctrl key - if(config.manager.selection.enabled && config.manager.selection.useCtrlKey && event.ctrlKey === true) { - return false; - } - - // single clicked while expected dblclick - if(config.manager.dblClickOpen && event.type === 'click') { - return false; - } - - return true; - } - + /** - * PanelLoader Interface - * + * Language model + * * @constructor */ - var PanelLoader = function() { - this.beforeLoad = function(path) {}; - this.afterLoad = function(path, response) {}; - }; - + var LangModel = function() { + var currentLang = null, + translationsHash = {}, + translationsPath = fm.settings.baseUrl + '/languages/'; + + this.buildLangFileUrl = function(code) { + return translationsPath + code + '.json'; + }; + + this.setLang = function(code) { + currentLang = code; + }; + + this.getLang = function() { + return currentLang; + }; + + this.setTranslations = function(json) { + translationsHash = json; + }; + + this.getTranslations = function() { + return translationsHash; + }; + + this.translate = function(key) { + return translationsHash[key]; + }; + }; + /** - * Preview model - * + * DelayStack + * Delays execution of functions that is passed as argument + * * @constructor */ - var PreviewModel = function() { - var preview_model = this, - clipboard = null; - - this.rdo = ko.observable({}); - // computed resource data object - this.cdo = ko.observable({}); - - this.viewer = { - type: ko.observable('default'), - isEditable: ko.observable(false), - url: ko.observable(null), - pureUrl: ko.observable(null), - options: ko.observable({}), - content: ko.observable(null), - codeMirror: ko.observable(null) - }; - - this.renderer = new RenderModel(); - this.editor = new EditorModel(); - - this.rdo.subscribe(function (resourceObject) { - preview_model.cdo({ - isFolder: (resourceObject.type === 'folder'), - sizeFormatted: formatBytes(resourceObject.attributes.size), - createdFormatted: formatTimestamp(resourceObject.attributes.created), - modifiedFormatted: formatTimestamp(resourceObject.attributes.modified), - extension: (resourceObject.type === 'file') ? getExtension(resourceObject.id) : null, - dimensions: resourceObject.attributes.width ? resourceObject.attributes.width + 'x' + resourceObject.attributes.height : null - }); - }); - - this.editor.content.subscribe(function (content) { - if (preview_model.editor.isInteractive()) { - // instantly render changes of editor content - preview_model.renderer.render(content); - } - }); - - this.applyObject = function(resourceObject) { - if (clipboard) { - clipboard.destroy(); - } - - model.previewFile(false); - - var filename = resourceObject.attributes.name, - editorObject = { - interactive: false - }, - viewerObject = { - type: 'default', - url: null, - options: {} - }; - - preview_model.rdo(resourceObject); - - if(isImageFile(filename)) { - viewerObject.type = 'image'; - viewerObject.url = createImageUrl(resourceObject, false, true); - } - if(isAudioFile(filename) && config.viewer.audio.enabled === true) { - viewerObject.type = 'audio'; - viewerObject.url = createPreviewUrl(resourceObject, true); - } - if(isVideoFile(filename) && config.viewer.video.enabled === true) { - viewerObject.type = 'video'; - viewerObject.url = createPreviewUrl(resourceObject, true); - viewerObject.options = { - width: config.viewer.video.playerWidth, - height: config.viewer.video.playerHeight - }; - } - // if(isOnlyOfficeFile(filename) && config.viewer.onlyoffice.enabled === true) { - // viewerObject.type = 'onlyoffice'; - // var connectorUrl = config.viewer.onlyoffice.connectorUrl || fm.settings.baseUrl + '/connectors/php/onlyoffice/editor.php'; - // viewerObject.url = connectorUrl + '?path=' + encodeURIComponent(resourceObject.attributes.path); - // viewerObject.options = { - // width: config.viewer.onlyoffice.editorWidth, - // height: config.viewer.onlyoffice.editorHeight - // }; - // } - if(isOpenDocFile(filename) && config.viewer.opendoc.enabled === true) { - viewerObject.type = 'opendoc'; - viewerObject.url = fm.settings.baseUrl + '/libs/ViewerJS/index.html#' + createPreviewUrl(resourceObject, true); - viewerObject.options = { - width: config.viewer.opendoc.readerWidth, - height: config.viewer.opendoc.readerHeight - }; - } - if(isGoogleDocsFile(filename) && config.viewer.google.enabled === true) { - viewerObject.type = 'google'; - viewerObject.url = 'https://docs.google.com/viewer?url=' + encodeURIComponent(createPreviewUrl(resourceObject, false)) + '&embedded=true'; - viewerObject.options = { - width: config.viewer.google.readerWidth, - height: config.viewer.google.readerHeight - }; - } - if (isIFrameFile(filename) && config.viewer.iframe.enabled === true) { - viewerObject.type = 'iframe'; - viewerObject.url = createPreviewUrl(resourceObject, true); - viewerObject.options = { - width: config.viewer.iframe.readerWidth, - height: config.viewer.iframe.readerHeight - }; - } - if ((isCodeMirrorFile(filename) && config.viewer.codeMirrorRenderer.enabled === true) || - (isMarkdownFile(filename) && config.viewer.markdownRenderer.enabled === true) - ) { - viewerObject.type = 'renderer'; - viewerObject.options = { - is_writable: resourceObject.attributes.writable - }; - preview_model.renderer.setRenderer(resourceObject); - editorObject.interactive = preview_model.renderer.renderer().interactive; - } - - preview_model.viewer.type(viewerObject.type); - preview_model.viewer.url(viewerObject.url); - preview_model.viewer.options(viewerObject.options); - preview_model.viewer.pureUrl(createCopyUrl(resourceObject)); - preview_model.viewer.isEditable(isEditableFile(filename) && config.editor.enabled === true); - preview_model.editor.isInteractive(editorObject.interactive); - - if (viewerObject.type === 'renderer' || preview_model.viewer.isEditable()) { - previewItem(resourceObject).then(function(content) { - preview_model.viewer.content(content); - model.previewFile(true); - }); - } else { - model.previewFile(true); - } + var DelayStack = function() { + var hash = {}, + delay_stack = this; + + this.push = function(name, callback, ms) { + delay_stack.removeTimer(name); + hash[name] = setTimeout(callback, ms); }; - - this.afterRender = function() { - preview_model.renderer.render(preview_model.viewer.content()); - - var copyBtnEl = $previewWrapper.find('.btn-copy-url')[0]; - clipboard = new Clipboard(copyBtnEl); - - clipboard.on('success', function(e) { - fm.success(lg('copied')); - }); + + this.getTimer = function(name) { + return hash[name]; }; - - this.initiateEditor = function(elements) { - var textarea = $previewWrapper.find('.fm-cm-editor-content')[0]; - preview_model.editor.createInstance(preview_model.cdo().extension, textarea, { - readOnly: false, - styleActiveLine: true - }); - }; - - // fires specific action by clicking toolbar buttons in detail view - this.bindToolbar = function(action) { - if (isObjectCapable(preview_model.rdo(), action)) { - performAction(action, {}, preview_model.rdo()); - } - }; - - this.previewIconClass = ko.pureComputed(function() { - var cssClass = [], - extraClass = ['ico']; - if(preview_model.viewer.type() === 'default' || !preview_model.viewer.url()) { - cssClass.push('grid-icon'); - if(this.cdo().isFolder === true) { - cssClass.push('ico_folder'); - extraClass.push('folder'); - if(!this.rdo().attributes.readable) { - extraClass.push('lock'); - } - } else { - cssClass.push('ico_file'); - if(this.rdo().attributes.readable) { - extraClass.push('ext', this.cdo().extension); - } else { - extraClass.push('file', 'lock'); - } - } - cssClass.push(extraClass.join('_')); + + this.removeTimer = function(name) { + if (hash[name]) { + clearTimeout(hash[name]); + delete hash[name]; } - return cssClass.join(' '); - }, this); - - this.closePreview = function() { - model.previewFile(false); }; - - this.editFile = function() { - var content = preview_model.viewer.content(); - preview_model.renderer.render(content); - preview_model.editor.render(content); - }; - - this.saveFile = function() { - saveItem(preview_model.rdo()); - }; - - this.closeEditor = function() { - preview_model.editor.enabled(false); - // re-render viewer content - preview_model.renderer.render(preview_model.viewer.content()); - }; - - this.buttonVisibility = function(action) { - switch(action) { - case 'select': - return (isObjectCapable(preview_model.rdo(), action) && hasContext()); - case 'move': - case 'rename': - case 'delete': - case 'download': - return (isObjectCapable(preview_model.rdo(), action)); - } - }; - }; - - var TreeModel = function() { - var tree_model = this; - this.selectedNode = ko.observable(null); - - var rootNode = new TreeNodeModel({attributes: {}}); - rootNode.id = fileRoot; - rootNode.level = ko.observable(-1); - this.rootNode = rootNode; - - function expandFolderDefault(parentNode) { - if (fullexpandedFolder !== null) { - if(!parentNode) { - parentNode = tree_model.rootNode; - } - - // looking for node that starts with specified path - var node = tree_model.findByFilter(function (node) { - return (fullexpandedFolder.indexOf(node.id) === 0); - }, parentNode); - - if (node) { - config.filetree.expandSpeed = 10; - tree_model.loadDataNode(node, false, true); - } else { - fullexpandedFolder = null; - config.filetree.expandSpeed = 200; - tree_model.setItemsFromNode(parentNode); - } - } - } - - this.mapNodes = function(filter, contextNode) { - if (!contextNode) { - contextNode = tree_model.rootNode; - } - // don't apply callback function to the filetree root node - if (!contextNode.isRoot()) { - filter.call(this, contextNode); + }; + + /** + * Knockout general model + * + * @constructor + */ + var FmModel = function() { + var model = this; + this.config = ko.observable(config); + this.loadingView = ko.observable(true); + this.previewFile = ko.observable(false); + this.viewMode = ko.observable(config.manager.defaultViewMode); + this.currentPath = ko.observable(fileRoot); + this.browseOnly = ko.observable(config.options.browseOnly); + this.previewModel = ko.observable(null); + this.currentLang = langModel.getLang(); + this.lg = langModel.getTranslations(); + + this.previewFile.subscribe(function (enabled) { + if (!enabled) { + // close editor upon disabling preview + model.previewModel.closeEditor(); + + // update content of descriptive panel + if (model.itemsModel.descriptivePanel.rdo().id === model.previewModel.rdo().id) { + model.itemsModel.descriptivePanel.render(model.previewModel.viewer.content()); + } } - var nodes = contextNode.children(); - if (!nodes || nodes.length === 0) { - return null; + }); + + this.isCapable = function(capability) { + return hasCapability(capability); + }; + + this.loadPath = function (targetPath, applyTreeNode) { + var targetNode, + folderLoader = new FolderAjaxLoader(targetPath); + + if (applyTreeNode) { + targetNode = fmModel.treeModel.findByParam('id', targetPath); } - for (var i = 0, l = nodes.length; i < l; i++) { - filter.call(this, nodes[i]); - tree_model.findByFilter(filter, nodes[i]); + if (targetNode) { + folderLoader.setPreloader(fmModel.treeModel.getPreloader(targetNode)) } - }; - - this.findByParam = function(key, value, contextNode) { - if(!contextNode) { - contextNode = tree_model.rootNode; - if(contextNode[key] === value) { - return contextNode; - } - } - var nodes = contextNode.children(); - if(!nodes || nodes.length === 0) { - return null; - } - for (var i = 0, l = nodes.length; i < l; i++) { - if (nodes[i][key] === value) { - return nodes[i]; - } - var result = tree_model.findByParam(key, value, nodes[i]); - if(result) return result; - } - return null; - }; - - this.findByFilter = function(filter, contextNode) { - if(!contextNode) { - contextNode = tree_model.rootNode; - if(filter(contextNode)) { - return contextNode; - } - } - var nodes = contextNode.children(); - if(!nodes || nodes.length === 0) { - return null; - } - for (var i = 0, l = nodes.length; i < l; i++) { - if(filter(nodes[i])) { - return nodes[i]; - } - var result = tree_model.findByFilter(filter, nodes[i]); - if(result) return result; - } - return null; - }; - - this.getSelected = function() { - var selectedItems = []; - if (tree_model.selectedNode()) { - selectedItems.push(tree_model.selectedNode()); - } - return selectedItems; - }; - - this.loadDataNode = function(targetNode, populateItems, refresh) { - var targetPath = targetNode.id; - var folderLoader = new FolderAjaxLoader(targetPath); - + folderLoader - .setPreloader(tree_model.getPreloader(targetNode)) + .setPreloader(model.itemsModel.getPreloader()) .setDataHandler(function (resourceObjects, targetPath) { - tree_model.addNodes(resourceObjects, targetNode, refresh); + if (targetNode) { + fmModel.treeModel.addNodes(resourceObjects, targetNode, true); + } + model.itemsModel.addItems(resourceObjects, targetPath, true); + model.searchModel.clearInput(); + }) + .load(function () { + return readFolder(targetPath); }); - - if (populateItems) { - folderLoader - .setPreloader(model.itemsModel.getPreloader()) - .setDataHandler(function (resourceObjects, targetPath) { - model.itemsModel.addItems(resourceObjects, targetPath, refresh); - model.searchModel.clearInput(); - }); - } - - folderLoader.load(function() { - return readFolder(targetPath); - }); - }; - - this.getPreloader = function(targetNode) { - var preloader = function() {}; - preloader.prototype = Object.create(PanelLoader); - - preloader.prototype.beforeLoad = function(path) { - if(!targetNode.isRoot()) { - targetNode.isLoaded(false); - } - }; - - preloader.prototype.afterLoad = function(path, response) { - if(!targetNode.isRoot()) { - targetNode.isLoaded(true); - tree_model.expandNode(targetNode); - } - expandFolderDefault(targetNode); - }; - - return new preloader(); - }; - - this.createNode = function(resourceObject) { - var node = new TreeNodeModel(resourceObject); - fmModel.filterModel.filterItem(node); - return node; - }; - - this.createNodes = function(resourceObjects) { - var nodes = []; - $.each(resourceObjects, function(i, resourceObject) { - nodes.push(tree_model.createNode(resourceObject)); - }); - return nodes; }; - - this.appendNodes = function(targetNode, newNodes) { - if(!$.isArray(newNodes)) { - newNodes = [newNodes]; - } - if (!targetNode) { - targetNode = tree_model.rootNode; - } - // list only folders in tree - if(config.filetree.foldersOnly) { - newNodes = $.grep(newNodes, function(node) { - return (node.cdo.isFolder); - }); - } - $.each(newNodes, function(i, node) { - node.parentNode(targetNode); - }); - var allNodes = targetNode.children().concat(newNodes); - targetNode.children(sortItems(allNodes)); - }; - - this.addNodes = function(resourceObjects, targetNode, reset) { - if(!$.isArray(resourceObjects)) { - resourceObjects = [resourceObjects]; + + this.addElements = function (resourceObjects, targetPath, reset) { + // handle tree nodes + var targetNode = model.treeModel.findByParam('id', targetPath); + if (targetNode) { + model.treeModel.addNodes(resourceObjects, targetNode, reset); } - - if(targetNode) { - var newNodes = tree_model.createNodes(resourceObjects); - if (reset) { - targetNode.children([]); - } - tree_model.appendNodes(targetNode, newNodes); + + // handle view objects + if (model.currentPath() === targetPath) { + model.itemsModel.addItems(resourceObjects, targetPath, reset); } - }; - - this.expandNode = function(node) { - if(node.isExpanded() === false && node.isLoaded() === true) { - node.isSliding(true); - return true; - } - return false; - }; - - this.collapseNode = function(node) { - if(node.isExpanded() === true) { - node.isSliding(true); - return true; - } - 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) { - cNode.level(node.level() + 1); - cNode.isFirstNode(index === 0); - cNode.isLastNode(index === (childrenLength - 1)); - }); - }; - - this.setItemsFromNode = function(node) { - var dataObjects = []; - $.each(node.children(), function(i, cnode) { - dataObjects.push(cnode.rdo); - }); - model.itemsModel.addItems(dataObjects, node.id, true); - }; - - this.nodeRendered = function(elements, node) { - // attach context menu - $(elements[1]).contextMenu({ - selector: '.file, .directory', - zIndex: 100, - // wrap options with "build" allows to get item element - build: function ($triggerElement, e) { - node.selected(true); - - return { - appendTo: '.fm-container', - items: getContextMenuItems(node.rdo), - callback: function(itemKey, options) { - performAction(itemKey, options, node.rdo, model.fetchSelectedObjects(node)); - } - } - } - }); - }; - - this.actualizeNodeObject = function(node, oldFolder, newFolder) { - var search = new RegExp('^' + oldFolder); - var oldPath = node.rdo.id; - var newPath = oldPath.replace(search, newFolder); - node.id = newPath; - node.rdo.id = newPath; - node.rdo.attributes.path = node.rdo.attributes.path.replace(new RegExp(oldPath + '$'), newPath); - - if(node.children().length) { - $.each(node.children(), function(index, cNode) { - tree_model.actualizeNodeObject(cNode, oldFolder, newFolder); - }); - } - }; - }; - - var TreeNodeModel = function(resourceObject) { - var tree_node = this; - this.id = resourceObject.id; - this.rdo = resourceObject; - this.cdo = { // computed data object - isFolder: (resourceObject.type === 'folder'), - extension: (resourceObject.type === 'file') ? getExtension(resourceObject.id) : null, - dimensions: resourceObject.attributes.width ? resourceObject.attributes.width + 'x' + resourceObject.attributes.height : null, - cssItemClass: (resourceObject.type === 'folder') ? 'directory' : 'file', - hiddenByType: false, - hiddenBySearch: false }; - - this.visible = ko.observable(true); - this.nodeTitle = ko.observable(resourceObject.attributes.name); - this.children = ko.observableArray([]); - this.parentNode = ko.observable(null); - this.isSliding = ko.observable(false); - this.isLoading = ko.observable(false); - this.isLoaded = ko.observable(false); - this.isExpanded = ko.observable(false); - this.selected = ko.observable(false); - this.dragHovered = ko.observable(false); - // arrangable properties - this.level = ko.observable(0); - this.isFirstNode = ko.observable(false); - this.isLastNode = ko.observable(false); - - this.nodeTitle.subscribe(function (value) { - tree_node.rdo.attributes.name = value; - }); - - this.children.subscribe(function (value) { - model.treeModel.arrangeNode(tree_node); - }); - - this.isLoaded.subscribe(function (value) { - tree_node.isLoading(!value); - }); - - this.selected.subscribe(function (value) { - if (value) { - if (model.treeModel.selectedNode() !== null) { - model.treeModel.selectedNode().selected(false); - } - model.treeModel.selectedNode(tree_node); - model.itemsModel.unselectItems(); - } else { - model.treeModel.selectedNode(null); - } - }); - - this.switchNode = function(node) { - if(!node.cdo.isFolder) { - return false; - } - if(!node.rdo.attributes.readable) { - fm.error(lg('NOT_ALLOWED_SYSTEM')); - return false; + + this.removeElement = function (resourceObject) { + // handle tree nodes + var treeNode = model.treeModel.findByParam('id', resourceObject.id); + if (treeNode) { + treeNode.remove(); } - if(!node.isLoaded()) { - tree_node.openNode(node, false); - } else { - model.treeModel.toggleNode(node); - } - }; - - this.mouseDown = function(node, e) { - node.selected(true); - }; - - this.nodeClick = function(node, e) { - if(!config.manager.dblClickOpen) { - tree_node.openNode(node, true); + + // handle view objects + var viewItem = model.itemsModel.findByParam('id', resourceObject.id); + if (viewItem) { + viewItem.remove(); } }; - - this.nodeDblClick = function(node, e) { - if(config.manager.dblClickOpen) { - tree_node.openNode(node, true); + + // fetch selected view items OR tree nodes + this.fetchSelectedItems = function(instanceName) { + var selectedNodes, selectedItems; + + if (instanceName === ItemObject.name) { + return model.itemsModel.getSelected(); } - }; - - this.openNode = function(node, populateItems, e) { - if(node.rdo.type === 'file') { - getDetailView(node.rdo); + if (instanceName === TreeNodeModel.name) { + return model.treeModel.getSelected(); } - if(node.rdo.type === 'folder') { - if(!node.isLoaded() || (node.isExpanded() && config.filetree.reloadOnClick)) { - model.treeModel.loadDataNode(node, populateItems, true); - } else { - model.treeModel.toggleNode(node); - - if (populateItems) { - model.treeModel.setItemsFromNode(node); - } - } + if (!instanceName) { + selectedNodes = model.treeModel.getSelected(); + selectedItems = model.itemsModel.getSelected(); + + return (selectedItems.length > 0) ? selectedItems : selectedNodes; } + throw new Error('Unknown item type.'); }; - - this.remove = function() { - tree_node.parentNode().children.remove(tree_node); - }; - - this.isRoot = function() { - return tree_node.level() === model.treeModel.rootNode.level(); + + // fetch resource objects out of the selected items + this.fetchSelectedObjects = function(item) { + var objects = []; + $.each(model.fetchSelectedItems(item.constructor.name), function(i, itemObject) { + objects.push(itemObject.rdo); + }); + return objects; }; - - this.title = ko.pureComputed(function() { - return (config.options.showTitleAttr) ? this.rdo.id : null; - }, this); - - this.itemClass = ko.pureComputed(function() { - var cssClass = []; - if (this.selected() && config.manager.selection.enabled) { - cssClass.push('ui-selected'); + + // check whether view item can be opened based on the event and configuration options + function isItemOpenable(event) { + // selecting with Ctrl key + if(config.manager.selection.enabled && config.manager.selection.useCtrlKey && event.ctrlKey === true) { + return false; } - if (this.dragHovered()) { - cssClass.push(model.ddModel.hoveredCssClass); + + // single clicked while expected dblclick + if(config.manager.dblClickOpen && event.type === 'click') { + return false; } - return cssClass.join(' '); - }, this); - - this.iconClass = ko.pureComputed(function() { - var cssClass, - extraClass = ['ico']; - if(this.cdo.isFolder === true) { - cssClass = 'ico_folder'; - if(this.isLoading() === true) { - extraClass.push('loading'); - } else { - extraClass.push('folder'); - if(!this.rdo.attributes.readable) { - extraClass.push('lock'); - } else if(this.isExpanded() || !this.isExpanded() && this.isSliding()) { - extraClass.push('open'); - } - } - } else { - cssClass = 'ico_file'; - if(this.rdo.attributes.readable) { - extraClass.push('ext', this.cdo.extension); - } else { - extraClass.push('file', 'lock'); + + return true; + } + + /** + * PanelLoader Interface + * + * @constructor + */ + var PanelLoader = function() { + this.beforeLoad = function(path) {}; + this.afterLoad = function(path, response) {}; + }; + + /** + * Preview model + * + * @constructor + */ + var PreviewModel = function() { + var preview_model = this, + clipboard = null; + + this.rdo = ko.observable({}); + // computed resource data object + this.cdo = ko.observable({}); + + this.viewer = { + type: ko.observable('default'), + isEditable: ko.observable(false), + url: ko.observable(null), + pureUrl: ko.observable(null), + options: ko.observable({}), + content: ko.observable(null), + codeMirror: ko.observable(null) + }; + + this.renderer = new RenderModel(); + this.editor = new EditorModel(); + + this.rdo.subscribe(function (resourceObject) { + preview_model.cdo({ + isFolder: (resourceObject.type === 'folder'), + sizeFormatted: formatBytes(resourceObject.attributes.size), + createdFormatted: formatTimestamp(resourceObject.attributes.created), + modifiedFormatted: formatTimestamp(resourceObject.attributes.modified), + extension: (resourceObject.type === 'file') ? getExtension(resourceObject.id) : null, + dimensions: resourceObject.attributes.width ? resourceObject.attributes.width + 'x' + resourceObject.attributes.height : null + }); + }); + + this.editor.content.subscribe(function (content) { + if (preview_model.editor.isInteractive()) { + // instantly render changes of editor content + preview_model.renderer.render(content); } - } - return cssClass + ' ' + extraClass.join('_'); - }, this); - - this.switcherClass = ko.pureComputed(function() { - var cssClass = []; - if (config.filetree.showLine) { - if (this.level() === 0 && this.isFirstNode() && this.isLastNode()) { - cssClass.push('root'); - } else if (this.level() === 0 && this.isFirstNode()) { - cssClass.push('roots'); - } else if (this.isLastNode()) { - cssClass.push('bottom'); - } else { - cssClass.push('center'); + }); + + this.applyObject = function(resourceObject) { + if (clipboard) { + clipboard.destroy(); } - } else { - cssClass.push('noline'); - } - if (this.cdo.isFolder) { - var isOpen = (this.isExpanded() || !this.isExpanded() && this.isSliding()); - cssClass.push(isOpen ? 'open' : 'close'); - } else { - cssClass.push('docu'); - } - return cssClass.join('_'); - }, this); - - this.clusterClass = ko.pureComputed(function() { - return (config.filetree.showLine && !this.isLastNode()) ? 'line' : ''; - }, this); - }; - - var ItemsModel = function() { - var items_model = this; - this.objects = ko.observableArray([]); - this.parentItem = ko.observable(null); - this.objectsSize = ko.observable(0); - this.objectsNumber = ko.observable(0); - this.selectedNumber = ko.observable(0); - this.listSortField = ko.observable(configSortField); - this.listSortOrder = ko.observable(configSortOrder); - this.isSelecting = ko.observable(false); - this.continiousSelection = ko.observable(false); - this.descriptivePanel = new RenderModel(); - this.lazyLoad = null; - - this.isSelecting.subscribe(function(state) { - if(!state) { - // means selection lasso has been dropped - items_model.continiousSelection(false); - } - }); - - this.createItem = function(resourceObject) { - var item = new ItemObject(resourceObject); - fmModel.filterModel.filterItem(item); - return item; - }; - - this.createItems = function(resourceObjects) { - var items = []; - $.each(resourceObjects, function(i, resourceObject) { - items.push(items_model.createItem(resourceObject)); - }); - return items; - }; - - this.appendItems = function(items) { - if(!$.isArray(items)) { - items = [items]; - } - - var allItems = items_model.objects().concat(items); - items_model.objects(sortItems(allItems)); + + model.previewFile(false); + + var filename = resourceObject.attributes.name, + editorObject = { + interactive: false + }, + viewerObject = { + type: 'default', + url: null, + options: {} + }; + + preview_model.rdo(resourceObject); + + if(isImageFile(filename)) { + viewerObject.type = 'image'; + viewerObject.url = createImageUrl(resourceObject, false, true); + } + if(isAudioFile(filename) && config.viewer.audio.enabled === true) { + viewerObject.type = 'audio'; + viewerObject.url = createPreviewUrl(resourceObject, true); + } + if(isVideoFile(filename) && config.viewer.video.enabled === true) { + viewerObject.type = 'video'; + viewerObject.url = createPreviewUrl(resourceObject, true); + viewerObject.options = { + width: config.viewer.video.playerWidth, + height: config.viewer.video.playerHeight + }; + } + // if(isOnlyOfficeFile(filename) && config.viewer.onlyoffice.enabled === true) { + // viewerObject.type = 'onlyoffice'; + // var connectorUrl = config.viewer.onlyoffice.connectorUrl || fm.settings.baseUrl + '/connectors/php/onlyoffice/editor.php'; + // viewerObject.url = connectorUrl + '?path=' + encodeURIComponent(resourceObject.attributes.path); + // viewerObject.options = { + // width: config.viewer.onlyoffice.editorWidth, + // height: config.viewer.onlyoffice.editorHeight + // }; + // } + if(isOpenDocFile(filename) && config.viewer.opendoc.enabled === true) { + viewerObject.type = 'opendoc'; + viewerObject.url = fm.settings.baseUrl + '/libs/ViewerJS/index.html#' + createPreviewUrl(resourceObject, true); + viewerObject.options = { + width: config.viewer.opendoc.readerWidth, + height: config.viewer.opendoc.readerHeight + }; + } + if(isGoogleDocsFile(filename) && config.viewer.google.enabled === true) { + viewerObject.type = 'google'; + viewerObject.url = 'https://docs.google.com/viewer?url=' + encodeURIComponent(createPreviewUrl(resourceObject, false)) + '&embedded=true'; + viewerObject.options = { + width: config.viewer.google.readerWidth, + height: config.viewer.google.readerHeight + }; + } + if (isIFrameFile(filename) && config.viewer.iframe.enabled === true) { + viewerObject.type = 'iframe'; + viewerObject.url = createPreviewUrl(resourceObject, true); + viewerObject.options = { + width: config.viewer.iframe.readerWidth, + height: config.viewer.iframe.readerHeight + }; + } + if ((isCodeMirrorFile(filename) && config.viewer.codeMirrorRenderer.enabled === true) || + (isMarkdownFile(filename) && config.viewer.markdownRenderer.enabled === true) + ) { + viewerObject.type = 'renderer'; + viewerObject.options = { + is_writable: resourceObject.attributes.writable + }; + preview_model.renderer.setRenderer(resourceObject); + editorObject.interactive = preview_model.renderer.renderer().interactive; + } + + preview_model.viewer.type(viewerObject.type); + preview_model.viewer.url(viewerObject.url); + preview_model.viewer.options(viewerObject.options); + preview_model.viewer.pureUrl(createCopyUrl(resourceObject)); + preview_model.viewer.isEditable(isEditableFile(filename) && config.editor.enabled === true); + preview_model.editor.isInteractive(editorObject.interactive); + + if (viewerObject.type === 'renderer' || preview_model.viewer.isEditable()) { + previewItem(resourceObject).then(function(content) { + preview_model.viewer.content(content); + model.previewFile(true); + }); + } else { + model.previewFile(true); + } + }; + + this.afterRender = function() { + preview_model.renderer.render(preview_model.viewer.content()); + + var copyBtnEl = $previewWrapper.find('.btn-copy-url')[0]; + clipboard = new Clipboard(copyBtnEl); + + clipboard.on('success', function(e) { + fm.success(lg('copied')); + }); + }; + + this.initiateEditor = function(elements) { + var textarea = $previewWrapper.find('.fm-cm-editor-content')[0]; + preview_model.editor.createInstance(preview_model.cdo().extension, textarea, { + readOnly: false, + styleActiveLine: true + }); + }; + + // fires specific action by clicking toolbar buttons in detail view + this.bindToolbar = function(action) { + if (isObjectCapable(preview_model.rdo(), action)) { + performAction(action, {}, preview_model.rdo()); + } + }; + + this.previewIconClass = ko.pureComputed(function() { + var cssClass = [], + extraClass = ['ico']; + if(preview_model.viewer.type() === 'default' || !preview_model.viewer.url()) { + cssClass.push('grid-icon'); + if(this.cdo().isFolder === true) { + cssClass.push('ico_folder'); + extraClass.push('folder'); + if(!this.rdo().attributes.readable) { + extraClass.push('lock'); + } + } else { + cssClass.push('ico_file'); + if(this.rdo().attributes.readable) { + extraClass.push('ext', this.cdo().extension); + } else { + extraClass.push('file', 'lock'); + } + } + cssClass.push(extraClass.join('_')); + } + return cssClass.join(' '); + }, this); + + this.closePreview = function() { + model.previewFile(false); + }; + + this.editFile = function() { + var content = preview_model.viewer.content(); + preview_model.renderer.render(content); + preview_model.editor.render(content); + }; + + this.saveFile = function() { + saveItem(preview_model.rdo()); + }; + + this.closeEditor = function() { + preview_model.editor.enabled(false); + // re-render viewer content + preview_model.renderer.render(preview_model.viewer.content()); + }; + + this.buttonVisibility = function(action) { + switch(action) { + case 'select': + return (isObjectCapable(preview_model.rdo(), action) && hasContext()); + case 'move': + case 'rename': + case 'delete': + case 'download': + return (isObjectCapable(preview_model.rdo(), action)); + } + }; }; - - this.addItems = function(resourceObjects, targetPath, reset) { - if(!$.isArray(resourceObjects)) { - resourceObjects = [resourceObjects]; - } - - var items = items_model.createItems(resourceObjects); - - if (reset) { - model.currentPath(targetPath); - model.breadcrumbsModel.splitCurrent(); - - items_model.setDescriptivePanel(resourceObjects); - items_model.setItemsList(items); - items_model.addParentItem(); - } else { - items_model.appendItems(items); + + var TreeModel = function() { + var tree_model = this; + this.selectedNode = ko.observable(null); + + var rootNode = new TreeNodeModel({attributes: {}}); + rootNode.id = fileRoot; + rootNode.level = ko.observable(-1); + this.rootNode = rootNode; + + function expandFolderDefault(parentNode) { + if (fullexpandedFolder !== null) { + if(!parentNode) { + parentNode = tree_model.rootNode; + } + + // looking for node that starts with specified path + var node = tree_model.findByFilter(function (node) { + return (fullexpandedFolder.indexOf(node.id) === 0); + }, parentNode); + + if (node) { + config.filetree.expandSpeed = 10; + tree_model.loadDataNode(node, false, true); + } else { + fullexpandedFolder = null; + config.filetree.expandSpeed = 200; + tree_model.setItemsFromNode(parentNode); + } + } } - }; - - this.loadDataList = function(targetPath) { - var folderLoader = new FolderAjaxLoader(targetPath); - - folderLoader - .setPreloader(items_model.getPreloader()) - .setDataHandler(function (resourceObjects, targetPath) { - items_model.addItems(resourceObjects, targetPath, true); - model.searchModel.clearInput(); - }) - .load(function() { + + this.mapNodes = function(filter, contextNode) { + if (!contextNode) { + contextNode = tree_model.rootNode; + } + // don't apply callback function to the filetree root node + if (!contextNode.isRoot()) { + filter.call(this, contextNode); + } + var nodes = contextNode.children(); + if (!nodes || nodes.length === 0) { + return null; + } + for (var i = 0, l = nodes.length; i < l; i++) { + filter.call(this, nodes[i]); + tree_model.findByFilter(filter, nodes[i]); + } + }; + + this.findByParam = function(key, value, contextNode) { + if(!contextNode) { + contextNode = tree_model.rootNode; + if(contextNode[key] === value) { + return contextNode; + } + } + var nodes = contextNode.children(); + if(!nodes || nodes.length === 0) { + return null; + } + for (var i = 0, l = nodes.length; i < l; i++) { + if (nodes[i][key] === value) { + return nodes[i]; + } + var result = tree_model.findByParam(key, value, nodes[i]); + if(result) return result; + } + return null; + }; + + this.findByFilter = function(filter, contextNode) { + if(!contextNode) { + contextNode = tree_model.rootNode; + if(filter(contextNode)) { + return contextNode; + } + } + var nodes = contextNode.children(); + if(!nodes || nodes.length === 0) { + return null; + } + for (var i = 0, l = nodes.length; i < l; i++) { + if(filter(nodes[i])) { + return nodes[i]; + } + var result = tree_model.findByFilter(filter, nodes[i]); + if(result) return result; + } + return null; + }; + + this.getSelected = function() { + var selectedItems = []; + if (tree_model.selectedNode()) { + selectedItems.push(tree_model.selectedNode()); + } + return selectedItems; + }; + + this.loadDataNode = function(targetNode, populateItems, refresh) { + var targetPath = targetNode.id; + var folderLoader = new FolderAjaxLoader(targetPath); + + folderLoader + .setPreloader(tree_model.getPreloader(targetNode)) + .setDataHandler(function (resourceObjects, targetPath) { + tree_model.addNodes(resourceObjects, targetNode, refresh); + }); + + if (populateItems) { + folderLoader + .setPreloader(model.itemsModel.getPreloader()) + .setDataHandler(function (resourceObjects, targetPath) { + model.itemsModel.addItems(resourceObjects, targetPath, refresh); + model.searchModel.clearInput(); + }); + } + + folderLoader.load(function() { return readFolder(targetPath); }); - }; - - this.setItemsList = function(items) { - // clear parent item - items_model.parentItem(null); - - items = sortItems(items); - items_model.objects(items); - }; - - this.addParentItem = function() { - // parent item is displayed for non-root folders - if(isFile(model.currentPath()) || model.currentPath() === fileRoot) { - return; - } - - var parentPath = getParentDirname(model.currentPath()); - var parentItem = { - id: parentPath, - rdo: { - id: parentPath, - type: 'parent', - attributes: { - readable: true, - writable: true + }; + + this.getPreloader = function(targetNode) { + var preloader = function() {}; + preloader.prototype = Object.create(PanelLoader); + + preloader.prototype.beforeLoad = function(path) { + if(!targetNode.isRoot()) { + targetNode.isLoaded(false); } - }, - dragHovered: ko.observable(false) + }; + + preloader.prototype.afterLoad = function(path, response) { + if(!targetNode.isRoot()) { + targetNode.isLoaded(true); + tree_model.expandNode(targetNode); + } + expandFolderDefault(targetNode); + }; + + return new preloader(); }; - - parentItem.open = function(item, e) { - if(isItemOpenable(e)) { - items_model.loadDataList(parentItem.id); + + this.createNode = function(resourceObject) { + var node = new TreeNodeModel(resourceObject); + fmModel.filterModel.filterItem(node); + return node; + }; + + this.createNodes = function(resourceObjects) { + var nodes = []; + $.each(resourceObjects, function(i, resourceObject) { + nodes.push(tree_model.createNode(resourceObject)); + }); + return nodes; + }; + + this.appendNodes = function(targetNode, newNodes) { + if(!$.isArray(newNodes)) { + newNodes = [newNodes]; } + if (!targetNode) { + targetNode = tree_model.rootNode; + } + // list only folders in tree + if(config.filetree.foldersOnly) { + newNodes = $.grep(newNodes, function(node) { + return (node.cdo.isFolder); + }); + } + $.each(newNodes, function(i, node) { + node.parentNode(targetNode); + }); + var allNodes = targetNode.children().concat(newNodes); + targetNode.children(sortItems(allNodes)); }; - - parentItem.itemClass = ko.pureComputed(function() { - var cssClass = []; - if (parentItem.dragHovered()) { - cssClass.push(model.ddModel.hoveredCssClass); + + this.addNodes = function(resourceObjects, targetNode, reset) { + if(!$.isArray(resourceObjects)) { + resourceObjects = [resourceObjects]; } - return cssClass.join(' '); - }); - - items_model.parentItem(parentItem); - }; - - this.setDescriptivePanel = function(dataObjects) { - // clear previously rendered content - items_model.descriptivePanel.content(null); - - $.each(dataObjects, function (i, resourceObject) { - if (config.manager.renderer.position && typeof config.manager.renderer.indexFile === 'string' && - resourceObject.attributes.name.toLowerCase() === config.manager.renderer.indexFile.toLowerCase() - ) { - items_model.descriptivePanel.setRenderer(resourceObject); - // load and render index file content - previewItem(items_model.descriptivePanel.rdo()).then(function(content) { - items_model.descriptivePanel.render(content); + + if(targetNode) { + var newNodes = tree_model.createNodes(resourceObjects); + if (reset) { + targetNode.children([]); + } + tree_model.appendNodes(targetNode, newNodes); + } + }; + + this.expandNode = function(node) { + if(node.isExpanded() === false && node.isLoaded() === true) { + node.isSliding(true); + return true; + } + return false; + }; + + this.collapseNode = function(node) { + if(node.isExpanded() === true) { + node.isSliding(true); + return true; + } + 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) { + cNode.level(node.level() + 1); + cNode.isFirstNode(index === 0); + cNode.isLastNode(index === (childrenLength - 1)); + }); + }; + + this.setItemsFromNode = function(node) { + var dataObjects = []; + $.each(node.children(), function(i, cnode) { + dataObjects.push(cnode.rdo); + }); + model.itemsModel.addItems(dataObjects, node.id, true); + }; + + this.nodeRendered = function(elements, node) { + // attach context menu + $(elements[1]).contextMenu({ + selector: '.file, .directory', + zIndex: 100, + // wrap options with "build" allows to get item element + build: function ($triggerElement, e) { + node.selected(true); + + return { + appendTo: '.fm-container', + items: getContextMenuItems(node.rdo), + callback: function(itemKey, options) { + performAction(itemKey, options, node.rdo, model.fetchSelectedObjects(node)); + } + } + } + }); + }; + + this.actualizeNodeObject = function(node, oldFolder, newFolder) { + var search = new RegExp('^' + oldFolder); + var oldPath = node.rdo.id; + var newPath = oldPath.replace(search, newFolder); + node.id = newPath; + node.rdo.id = newPath; + node.rdo.attributes.path = node.rdo.attributes.path.replace(new RegExp(oldPath + '$'), newPath); + + if(node.children().length) { + $.each(node.children(), function(index, cNode) { + tree_model.actualizeNodeObject(cNode, oldFolder, newFolder); }); } - }); + }; }; - - this.findByParam = function(key, value) { - return ko.utils.arrayFirst(items_model.objects(), function(object) { - return object[key] === value; - }); - }; - - this.findByFilter = function(filter, allMatches) { - var firstMatch = !(allMatches || false); - - var resultItems = [], - items = items_model.objects(); - - if(!items || items.length === 0) { - return firstMatch ? null : resultItems; - } - for (var i = 0, l = items.length; i < l; i++) { - if(filter(items[i])) { - if(firstMatch) { - return items[i]; - } - resultItems.push(items[i]); - } - } - return firstMatch ? null : resultItems; - }; - - this.sortObjects = function() { - var sortedList = sortItems(items_model.objects()); - items_model.objects(sortedList); - }; - - this.getSelected = function() { - var selectedItems = items_model.findByFilter(function (item) { - return item.selected(); - }, true) || []; - - items_model.selectedNumber(selectedItems.length); - return selectedItems; - }; - - this.unselectItems = function(ctrlKey) { - var appendSelection = (config.manager.selection.enabled && config.manager.selection.useCtrlKey && ctrlKey === true); - if(!appendSelection) { - // drop selection from selected items - $.each(items_model.getSelected(), function(i, itemObject) { - itemObject.selected(false); - }); - } - }; - - this.initiateLazyLoad = function() { - // not configured or already initiated - if (config.viewer.image.lazyLoad !== true || items_model.lazyLoad) { - return; - } - - items_model.lazyLoad = new LazyLoad({ - container: $fileinfo[0], // work only for default scrollbar - callback_load: function (element) { - fm.console('LOADED', element.getAttribute('data-original')); - }, - callback_set: function (element) { - fm.console('SET', element.getAttribute('data-original')); - }, - callback_processed: function (elementsLeft) { - fm.console('PROCESSED', elementsLeft + ' images left'); + + var TreeNodeModel = function(resourceObject) { + var tree_node = this; + this.id = resourceObject.id; + this.rdo = resourceObject; + this.cdo = { // computed data object + isFolder: (resourceObject.type === 'folder'), + extension: (resourceObject.type === 'file') ? getExtension(resourceObject.id) : null, + dimensions: resourceObject.attributes.width ? resourceObject.attributes.width + 'x' + resourceObject.attributes.height : null, + cssItemClass: (resourceObject.type === 'folder') ? 'directory' : 'file', + hiddenByType: false, + hiddenBySearch: false + }; + + this.visible = ko.observable(true); + this.nodeTitle = ko.observable(resourceObject.attributes.name); + this.children = ko.observableArray([]); + this.parentNode = ko.observable(null); + this.isSliding = ko.observable(false); + this.isLoading = ko.observable(false); + this.isLoaded = ko.observable(false); + this.isExpanded = ko.observable(false); + this.selected = ko.observable(false); + this.dragHovered = ko.observable(false); + // arrangable properties + this.level = ko.observable(0); + this.isFirstNode = ko.observable(false); + this.isLastNode = ko.observable(false); + + this.nodeTitle.subscribe(function (value) { + tree_node.rdo.attributes.name = value; + }); + + this.children.subscribe(function (value) { + model.treeModel.arrangeNode(tree_node); + }); + + this.isLoaded.subscribe(function (value) { + tree_node.isLoading(!value); + }); + + this.selected.subscribe(function (value) { + if (value) { + if (model.treeModel.selectedNode() !== null) { + model.treeModel.selectedNode().selected(false); + } + model.treeModel.selectedNode(tree_node); + model.itemsModel.unselectItems(); + } else { + model.treeModel.selectedNode(null); } }); - }; - - this.getPreloader = function() { - var preloader = function() {}; - preloader.prototype = Object.create(PanelLoader); - - preloader.prototype.beforeLoad = function(path) { - model.loadingView(true); + + this.switchNode = function(node) { + if(!node.cdo.isFolder) { + return false; + } + if(!node.rdo.attributes.readable) { + fm.error(lg('NOT_ALLOWED_SYSTEM')); + return false; + } + if(!node.isLoaded()) { + tree_node.openNode(node, false); + } else { + model.treeModel.toggleNode(node); + } }; - - preloader.prototype.afterLoad = function(path, response) { - model.loadingView(false); - - if (items_model.lazyLoad) { - items_model.lazyLoad.update(); + + this.mouseDown = function(node, e) { + node.selected(true); + }; + + this.nodeClick = function(node, e) { + if(!config.manager.dblClickOpen) { + tree_node.openNode(node, true); } }; - - return new preloader(); - }; - - this.objects.subscribe(function(items) { - var totalNumber = 0, - totalSize = 0; - - $.each(items, function(i, item) { - totalNumber++; - if(item.rdo.type === 'file') { - totalSize += Number(item.rdo.attributes.size); - } - }); - // updates folder summary info - items_model.objectsNumber(totalNumber); - items_model.objectsSize(formatBytes(totalSize)); - - // update - if (items_model.lazyLoad) { - setTimeout(function() { - items_model.lazyLoad.update(); - }, 50); - } - - // context menu - $viewItems.contextMenu({ - selector: '.file, .directory', - zIndex: 100, - // wrap options with "build" allows to get item element - build: function ($triggerElement, e) { - var koItem = ko.dataFor($triggerElement[0]); - if(!koItem.selected()) { - model.itemsModel.unselectItems(false); - koItem.selected(true); - } - - return { - appendTo: '.fm-container', - items: getContextMenuItems(koItem.rdo), - callback: function(itemKey, options) { - performAction(itemKey, options, koItem.rdo, model.fetchSelectedObjects(koItem)); - } - } - } - }); - }); - }; - - var ItemObject = function(resourceObject) { - var item_object = this, - previewWidth = config.viewer.image.thumbMaxWidth; - - if(resourceObject.attributes.width && resourceObject.attributes.width < previewWidth) { - previewWidth = resourceObject.attributes.width; - } - - this.id = resourceObject.id; // for search purpose - this.rdo = resourceObject; // original resource data object - this.cdo = { // computed data object - isFolder: (resourceObject.type === 'folder'), - sizeFormatted: formatBytes(resourceObject.attributes.size), - createdFormatted: formatTimestamp(resourceObject.attributes.created), - modifiedFormatted: formatTimestamp(resourceObject.attributes.modified), - extension: (resourceObject.type === 'file') ? getExtension(resourceObject.id) : null, - dimensions: resourceObject.attributes.width ? resourceObject.attributes.width + 'x' + resourceObject.attributes.height : null, - cssItemClass: (resourceObject.type === 'folder') ? 'directory' : 'file', - imageUrl: createImageUrl(resourceObject, true, true), - previewWidth: previewWidth, - hiddenByType: false, - hiddenBySearch: false - }; - this.visible = ko.observable(true); - this.selected = ko.observable(false); - this.dragHovered = ko.observable(false); - this.lazyPreview = (config.viewer.image.lazyLoad && this.cdo.imageUrl); - - this.selected.subscribe(function (value) { - if (value && model.treeModel.selectedNode() !== null) { - model.treeModel.selectedNode().selected(false); - } - }); - - this.title = ko.pureComputed(function() { - return (config.options.showTitleAttr) ? this.rdo.id : null; - }, this); - - this.itemClass = ko.pureComputed(function() { - var cssClass = []; - if (this.selected() && config.manager.selection.enabled) { - cssClass.push('ui-selected'); - } - if (this.dragHovered()) { - cssClass.push(model.ddModel.hoveredCssClass); - } - return this.cdo.cssItemClass + ' ' + cssClass.join(' '); - }, this); - - this.listIconClass = ko.pureComputed(function() { - var cssClass, - extraClass = ['ico']; - if (this.cdo.isFolder === true) { - cssClass = 'ico_folder'; - extraClass.push('folder'); - if (!this.rdo.attributes.readable) { - extraClass.push('lock'); + + this.nodeDblClick = function(node, e) { + if(config.manager.dblClickOpen) { + tree_node.openNode(node, true); } - } else { - cssClass = 'ico_file'; - if (this.rdo.attributes.readable) { - extraClass.push('ext', this.cdo.extension); - } else { - extraClass.push('file', 'lock'); + }; + + this.openNode = function(node, populateItems, e) { + if(node.rdo.type === 'file') { + getDetailView(node.rdo); } - } - return cssClass + ' ' + extraClass.join('_'); - }, this); - - this.gridIconClass = ko.pureComputed(function() { - var cssClass = [], - extraClass = ['ico']; - if (!this.cdo.imageUrl) { - cssClass.push('grid-icon'); - if (this.cdo.isFolder === true) { - cssClass.push('ico_folder'); - extraClass.push('folder'); - if (!this.rdo.attributes.readable) { - extraClass.push('lock'); + if(node.rdo.type === 'folder') { + if(!node.isLoaded() || (node.isExpanded() && config.filetree.reloadOnClick)) { + model.treeModel.loadDataNode(node, populateItems, true); + } else { + model.treeModel.toggleNode(node); + + if (populateItems) { + model.treeModel.setItemsFromNode(node); + } + } + } + }; + + this.remove = function() { + tree_node.parentNode().children.remove(tree_node); + }; + + this.isRoot = function() { + return tree_node.level() === model.treeModel.rootNode.level(); + }; + + this.title = ko.pureComputed(function() { + return (config.options.showTitleAttr) ? this.rdo.id : null; + }, this); + + this.itemClass = ko.pureComputed(function() { + var cssClass = []; + if (this.selected() && config.manager.selection.enabled) { + cssClass.push('ui-selected'); + } + if (this.dragHovered()) { + cssClass.push(model.ddModel.hoveredCssClass); + } + return cssClass.join(' '); + }, this); + + this.iconClass = ko.pureComputed(function() { + var cssClass, + extraClass = ['ico']; + if(this.cdo.isFolder === true) { + cssClass = 'ico_folder'; + if(this.isLoading() === true) { + extraClass.push('loading'); + } else { + extraClass.push('folder'); + if(!this.rdo.attributes.readable) { + extraClass.push('lock'); + } else if(this.isExpanded() || !this.isExpanded() && this.isSliding()) { + extraClass.push('open'); + } } } else { - cssClass.push('ico_file'); - if (this.rdo.attributes.readable) { + cssClass = 'ico_file'; + if(this.rdo.attributes.readable) { extraClass.push('ext', this.cdo.extension); } else { extraClass.push('file', 'lock'); } } - cssClass.push(extraClass.join('_')); - } - return cssClass.join(' '); - }, this); - - this.mouseDown = function(item, e) { - // case: previously selected items are dragged instead of a newly one - // unselect if currently clicked item is not the one of selected items - if (!item.selected()) { - model.itemsModel.unselectItems(e.ctrlKey); - } - - model.selectionModel.unselect = item.selected(); - item.selected(true); - }; - - this.open = function(item, e) { - if (model.selectionModel.unselect) { - // case: click + ctrlKey on selected item - if (e.ctrlKey) { - item.selected(false); - } - // drop selection - if (!e.ctrlKey && config.manager.dblClickOpen) { - model.itemsModel.unselectItems(e.ctrlKey); - item.selected(true); + return cssClass + ' ' + extraClass.join('_'); + }, this); + + this.switcherClass = ko.pureComputed(function() { + var cssClass = []; + if (config.filetree.showLine) { + if (this.level() === 0 && this.isFirstNode() && this.isLastNode()) { + cssClass.push('root'); + } else if (this.level() === 0 && this.isFirstNode()) { + cssClass.push('roots'); + } else if (this.isLastNode()) { + cssClass.push('bottom'); + } else { + cssClass.push('center'); + } + } else { + cssClass.push('noline'); } - } - - if(isItemOpenable(e)) { - if(config.options.quickSelect && item.rdo.type === 'file' && isObjectCapable(item.rdo, 'select')) { - selectItem(item.rdo); + if (this.cdo.isFolder) { + var isOpen = (this.isExpanded() || !this.isExpanded() && this.isSliding()); + cssClass.push(isOpen ? 'open' : 'close'); } else { - getDetailView(item.rdo); + cssClass.push('docu'); } - } - }; - - this.remove = function() { - model.itemsModel.objects.remove(this); - }; - }; - - var FolderAjaxLoader = function(path) { - var folder_loader = this, - handlers = [], - /** @type {PanelLoader[]} */ - preloaders = []; - - /** - * @param {PanelLoader} preloader - * @returns {FolderAjaxLoader} - */ - this.setPreloader = function(preloader) { - preloaders.push(preloader); - return folder_loader; + return cssClass.join('_'); + }, this); + + this.clusterClass = ko.pureComputed(function() { + return (config.filetree.showLine && !this.isLastNode()) ? 'line' : ''; + }, this); }; - - /** - * - * @param {function} callback - * @returns {FolderAjaxLoader} - */ - this.setDataHandler = function(callback) { - handlers.push(callback); - return folder_loader; - }; - - this.load = function(folderLoader) { - preloaders.forEach(function (preloader, index, array) { - preloader.beforeLoad(path); - }); - - folderLoader().then(function(response) { - if(response.data) { - $.each(handlers, function(i, handler) { - handler(response.data, path); + + var ItemsModel = function() { + var items_model = this; + this.objects = ko.observableArray([]); + this.parentItem = ko.observable(null); + this.objectsSize = ko.observable(0); + this.objectsNumber = ko.observable(0); + this.selectedNumber = ko.observable(0); + this.listSortField = ko.observable(configSortField); + this.listSortOrder = ko.observable(configSortOrder); + this.isSelecting = ko.observable(false); + this.continiousSelection = ko.observable(false); + this.descriptivePanel = new RenderModel(); + this.lazyLoad = null; + + this.isSelecting.subscribe(function(state) { + if(!state) { + // means selection lasso has been dropped + items_model.continiousSelection(false); + } + }); + + this.createItem = function(resourceObject) { + var item = new ItemObject(resourceObject); + fmModel.filterModel.filterItem(item); + return item; + }; + + this.createItems = function(resourceObjects) { + var items = []; + $.each(resourceObjects, function(i, resourceObject) { + items.push(items_model.createItem(resourceObject)); + }); + return items; + }; + + this.appendItems = function(items) { + if(!$.isArray(items)) { + items = [items]; + } + + var allItems = items_model.objects().concat(items); + items_model.objects(sortItems(allItems)); + }; + + this.addItems = function(resourceObjects, targetPath, reset) { + if(!$.isArray(resourceObjects)) { + resourceObjects = [resourceObjects]; + } + + var items = items_model.createItems(resourceObjects); + + if (reset) { + model.currentPath(targetPath); + model.breadcrumbsModel.splitCurrent(); + + items_model.setDescriptivePanel(resourceObjects); + items_model.setItemsList(items); + items_model.addParentItem(); + } else { + items_model.appendItems(items); + } + }; + + this.loadDataList = function(targetPath) { + var folderLoader = new FolderAjaxLoader(targetPath); + + folderLoader + .setPreloader(items_model.getPreloader()) + .setDataHandler(function (resourceObjects, targetPath) { + items_model.addItems(resourceObjects, targetPath, true); + model.searchModel.clearInput(); + }) + .load(function() { + return readFolder(targetPath); }); - - $.each(preloaders, function(i, preloader) { - preloader.afterLoad(path, response); + }; + + this.setItemsList = function(items) { + // clear parent item + items_model.parentItem(null); + + items = sortItems(items); + items_model.objects(items); + }; + + this.addParentItem = function() { + // parent item is displayed for non-root folders + if(isFile(model.currentPath()) || model.currentPath() === fileRoot) { + return; + } + + var parentPath = getParentDirname(model.currentPath()); + var parentItem = { + id: parentPath, + rdo: { + id: parentPath, + type: 'parent', + attributes: { + readable: true, + writable: true + } + }, + dragHovered: ko.observable(false) + }; + + parentItem.open = function(item, e) { + if(isItemOpenable(e)) { + items_model.loadDataList(parentItem.id); + } + }; + + parentItem.itemClass = ko.pureComputed(function() { + var cssClass = []; + if (parentItem.dragHovered()) { + cssClass.push(model.ddModel.hoveredCssClass); + } + return cssClass.join(' '); + }); + + items_model.parentItem(parentItem); + }; + + this.setDescriptivePanel = function(dataObjects) { + // clear previously rendered content + items_model.descriptivePanel.content(null); + + $.each(dataObjects, function (i, resourceObject) { + if (config.manager.renderer.position && typeof config.manager.renderer.indexFile === 'string' && + resourceObject.attributes.name.toLowerCase() === config.manager.renderer.indexFile.toLowerCase() + ) { + items_model.descriptivePanel.setRenderer(resourceObject); + // load and render index file content + previewItem(items_model.descriptivePanel.rdo()).then(function(content) { + items_model.descriptivePanel.render(content); + }); + } + }); + }; + + this.findByParam = function(key, value) { + return ko.utils.arrayFirst(items_model.objects(), function(object) { + return object[key] === value; + }); + }; + + this.findByFilter = function(filter, allMatches) { + var firstMatch = !(allMatches || false); + + var resultItems = [], + items = items_model.objects(); + + if(!items || items.length === 0) { + return firstMatch ? null : resultItems; + } + for (var i = 0, l = items.length; i < l; i++) { + if(filter(items[i])) { + if(firstMatch) { + return items[i]; + } + resultItems.push(items[i]); + } + } + return firstMatch ? null : resultItems; + }; + + this.sortObjects = function() { + var sortedList = sortItems(items_model.objects()); + items_model.objects(sortedList); + }; + + this.getSelected = function() { + var selectedItems = items_model.findByFilter(function (item) { + return item.selected(); + }, true) || []; + + items_model.selectedNumber(selectedItems.length); + return selectedItems; + }; + + this.unselectItems = function(ctrlKey) { + var appendSelection = (config.manager.selection.enabled && config.manager.selection.useCtrlKey && ctrlKey === true); + if(!appendSelection) { + // drop selection from selected items + $.each(items_model.getSelected(), function(i, itemObject) { + itemObject.selected(false); }); } + }; + + this.initiateLazyLoad = function() { + // not configured or already initiated + if (config.viewer.image.lazyLoad !== true || items_model.lazyLoad) { + return; + } + + items_model.lazyLoad = new LazyLoad({ + container: $fileinfo[0], // work only for default scrollbar + callback_load: function (element) { + fm.console('LOADED', element.getAttribute('data-original')); + }, + callback_set: function (element) { + fm.console('SET', element.getAttribute('data-original')); + }, + callback_processed: function (elementsLeft) { + fm.console('PROCESSED', elementsLeft + ' images left'); + } + }); + }; + + this.getPreloader = function() { + var preloader = function() {}; + preloader.prototype = Object.create(PanelLoader); + + preloader.prototype.beforeLoad = function(path) { + model.loadingView(true); + }; + + preloader.prototype.afterLoad = function(path, response) { + model.loadingView(false); + + if (items_model.lazyLoad) { + items_model.lazyLoad.update(); + } + }; + + return new preloader(); + }; + + this.objects.subscribe(function(items) { + var totalNumber = 0, + totalSize = 0; + + $.each(items, function(i, item) { + totalNumber++; + if(item.rdo.type === 'file') { + totalSize += Number(item.rdo.attributes.size); + } + }); + // updates folder summary info + items_model.objectsNumber(totalNumber); + items_model.objectsSize(formatBytes(totalSize)); + + // update + if (items_model.lazyLoad) { + setTimeout(function() { + items_model.lazyLoad.update(); + }, 50); + } + + // context menu + $viewItems.contextMenu({ + selector: '.file, .directory', + zIndex: 100, + // wrap options with "build" allows to get item element + build: function ($triggerElement, e) { + var koItem = ko.dataFor($triggerElement[0]); + if(!koItem.selected()) { + model.itemsModel.unselectItems(false); + koItem.selected(true); + } + + return { + appendTo: '.fm-container', + items: getContextMenuItems(koItem.rdo), + callback: function(itemKey, options) { + performAction(itemKey, options, koItem.rdo, model.fetchSelectedObjects(koItem)); + } + } + } + }); }); }; - }; - - var TableViewModel = function() { - var SortableHeader = function(name) { - var thead = this; - this.column = ko.observable(name); - this.order = ko.observable(model.itemsModel.listSortOrder()); - - this.sortClass = ko.pureComputed(function() { - var cssClass; - if(model.itemsModel.listSortField() === thead.column()) { - cssClass = 'sorted sorted-' + this.order(); - } - return cssClass; - }, this); - - this.sort = function() { - var isAscending = thead.order() === 'asc'; - var isSameColumn = model.itemsModel.listSortField() === thead.column(); - thead.order(isSameColumn ? (isAscending ? 'desc' : 'asc') : model.itemsModel.listSortOrder()); - model.itemsModel.listSortField(thead.column()); - model.itemsModel.listSortOrder(thead.order()); - model.itemsModel.sortObjects(); - }; - }; - - this.thName = new SortableHeader('name'); - this.thType = new SortableHeader('type'); - this.thSize = new SortableHeader('size'); - this.thDimensions = new SortableHeader('dimensions'); - this.thModified = new SortableHeader('modified'); - }; - - var HeaderModel = function() { - var header_model = this; - - this.closeButton = ko.observable(false); - this.langSwitcher = $.isArray(config.language.available) && config.language.available.length > 0; - - this.closeButtonOnClick = function() { - fm.console('CLOSE button is clicked'); - }; - - this.navHome = function() { - model.previewFile(false); - model.itemsModel.loadDataList(fileRoot); - }; - - this.navLevelUp = function() { - var parentFolder = model.previewFile() - ? getDirname(model.previewModel.rdo().id) - : getParentDirname(model.currentPath()); - - if(model.previewFile()) { - model.previewFile(false); - } - - if(parentFolder !== model.currentPath()) { - model.itemsModel.loadDataList(parentFolder); + + var ItemObject = function(resourceObject) { + var item_object = this, + previewWidth = config.viewer.image.thumbMaxWidth; + + if(resourceObject.attributes.width && resourceObject.attributes.width < previewWidth) { + previewWidth = resourceObject.attributes.width; } - }; - - this.navRefresh = function() { - if(model.previewFile()) { - model.previewFile(false); - model.previewFile(true); - } else { - model.itemsModel.loadDataList(model.currentPath()); - } - }; - - this.displayGrid = function() { - model.viewMode('grid'); - model.previewFile(false); - - if (model.itemsModel.lazyLoad) { - model.itemsModel.lazyLoad.update(); - } - }; - - this.displayList = function() { - model.viewMode('list'); - model.previewFile(false); - }; - - this.switchLang = function(e) { - var langNew = e.target.value, - langCurrent = langModel.getLang(); - - if (langNew && langNew.toLowerCase() !== langCurrent.toLowerCase()) { - var newUrl, url = window.location.toString(), - regExp = new RegExp('(langCode=)' + langCurrent); - - if (regExp.test(url)) { - newUrl = url.replace(regExp, '$1' + langNew); - } else { - newUrl = url + ($.isEmptyObject(_url_.param()) ? '?' : '#') + 'langCode=' + langNew; - } - window.location.href = newUrl; - } - }; - - this.createFolder = function() { - if(!hasCapability('createFolder')) { - fm.error(lg('NOT_ALLOWED')); - return false; - } - - function makeFolder(e, ui) { - var folderName = ui.getInputValue(); - if(!folderName) { - fm.error(lg('no_foldername')); - return; - } - - buildAjaxRequest('GET', { - mode: 'addfolder', - path: fmModel.currentPath(), - name: folderName - }).done(function(response) { - if (response.data) { - fmModel.addElements(response.data, fmModel.currentPath()); - - ui.closeDialog(); - if (config.options.showConfirmation) { - fm.success(lg('successful_added_folder')); + + this.id = resourceObject.id; // for search purpose + this.rdo = resourceObject; // original resource data object + this.cdo = { // computed data object + isFolder: (resourceObject.type === 'folder'), + sizeFormatted: formatBytes(resourceObject.attributes.size), + createdFormatted: formatTimestamp(resourceObject.attributes.created), + modifiedFormatted: formatTimestamp(resourceObject.attributes.modified), + extension: (resourceObject.type === 'file') ? getExtension(resourceObject.id) : null, + dimensions: resourceObject.attributes.width ? resourceObject.attributes.width + 'x' + resourceObject.attributes.height : null, + cssItemClass: (resourceObject.type === 'folder') ? 'directory' : 'file', + imageUrl: createImageUrl(resourceObject, true, true), + previewWidth: previewWidth, + hiddenByType: false, + hiddenBySearch: false + }; + this.visible = ko.observable(true); + this.selected = ko.observable(false); + this.dragHovered = ko.observable(false); + this.lazyPreview = (config.viewer.image.lazyLoad && this.cdo.imageUrl); + + this.selected.subscribe(function (value) { + if (value && model.treeModel.selectedNode() !== null) { + model.treeModel.selectedNode().selected(false); + } + }); + + this.title = ko.pureComputed(function() { + return (config.options.showTitleAttr) ? this.rdo.id : null; + }, this); + + this.itemClass = ko.pureComputed(function() { + var cssClass = []; + if (this.selected() && config.manager.selection.enabled) { + cssClass.push('ui-selected'); + } + if (this.dragHovered()) { + cssClass.push(model.ddModel.hoveredCssClass); + } + return this.cdo.cssItemClass + ' ' + cssClass.join(' '); + }, this); + + this.listIconClass = ko.pureComputed(function() { + var cssClass, + extraClass = ['ico']; + if (this.cdo.isFolder === true) { + cssClass = 'ico_folder'; + extraClass.push('folder'); + if (!this.rdo.attributes.readable) { + extraClass.push('lock'); + } + } else { + cssClass = 'ico_file'; + if (this.rdo.attributes.readable) { + extraClass.push('ext', this.cdo.extension); + } else { + extraClass.push('file', 'lock'); + } + } + return cssClass + ' ' + extraClass.join('_'); + }, this); + + this.gridIconClass = ko.pureComputed(function() { + var cssClass = [], + extraClass = ['ico']; + if (!this.cdo.imageUrl) { + cssClass.push('grid-icon'); + if (this.cdo.isFolder === true) { + cssClass.push('ico_folder'); + extraClass.push('folder'); + if (!this.rdo.attributes.readable) { + extraClass.push('lock'); + } + } else { + cssClass.push('ico_file'); + if (this.rdo.attributes.readable) { + extraClass.push('ext', this.cdo.extension); + } else { + extraClass.push('file', 'lock'); } } - }).fail(handleAjaxError); - } - - fm.prompt({ - message: lg('prompt_foldername'), - value: lg('default_foldername'), - okBtn: { - label: lg('create_folder'), - autoClose: false, - click: makeFolder - }, - cancelBtn: { - label: lg('cancel') - } - }); - }; - }; - - var SummaryModel = function() { - this.files = ko.observable(null); - this.folders = ko.observable(null); - this.size = ko.observable(null); - this.enabled = ko.observable(false); - - this.doSummarize = function() { - summarizeItems(); - }; - }; - - var FilterModel = function() { - var filter_model = this; - this.name = ko.observable(null); - - this.setName = function(filterName) { - if (filterName && - config.filter && - $.isArray(config.filter[filterName]) - ) { - filter_model.name(filterName); - } - }; - - // return extensions which are match a filter name - this.getExtensions = function() { - if (filter_model.name()) { - return config.filter[filter_model.name()]; - } - return null; - }; - - // check whether file item should be filtered out of the output based on it's extension - this.filterItem = function(itemObject) { - var extensions = filter_model.getExtensions(), - visibility = !itemObject.cdo.hiddenBySearch; - - // set default visibility, required for "all files" filter - itemObject.cdo.hiddenByType = false; - - if (itemObject.rdo.type === 'file' && $.isArray(extensions)) { - var ext = getExtension(itemObject.id), - matchByType = extensions.indexOf(ext) !== -1; - - visibility = visibility && matchByType; - itemObject.cdo.hiddenByType = !matchByType; - } - itemObject.visible(visibility); - }; - - this.filter = function(filterName) { - filter_model.setName(filterName); - - $.each(model.itemsModel.objects(), function(i, itemObject) { - filter_model.filterItem(itemObject); - }); - - model.treeModel.mapNodes(function (node) { - filter_model.filterItem(node); - }); - - if (model.itemsModel.lazyLoad) { - model.itemsModel.lazyLoad.update(); - } - }; - - this.reset = function() { - filter_model.name(null); - filter_model.filter(null); - }; - }; - - var SearchModel = function() { - var search_model = this, - previousValue = '', - searchOnTyping = !!config.search.typingDelay; - - this.value = ko.observable(''); - this.isRendered = ko.observable(false); - - this.value.subscribe(function(oldValue) { - previousValue = oldValue; - }, null, 'beforeChange'); - - this.inputKeyUp = function(data, e) { - var keyCode = e.which || e.keyCode, - // https://stackoverflow.com/a/19347349/7095038 - invalidKeyCodes = [16,17,18,27,37,38,39,40]; - - if (searchOnTyping) { - // validate input - if (invalidKeyCodes.indexOf(keyCode) > -1) { - return; + cssClass.push(extraClass.join('_')); } - // explicit assign value, required for search-on-typing - search_model.value(e.target.value); - } - - // search-on-typing or Enter key pressed - if (searchOnTyping || keyCode === 13) { - performSearch(); - } - }; - - this.seekItems = function(data, e) { - performSearch(); + return cssClass.join(' '); + }, this); + + this.mouseDown = function(item, e) { + // case: previously selected items are dragged instead of a newly one + // unselect if currently clicked item is not the one of selected items + if (!item.selected()) { + model.itemsModel.unselectItems(e.ctrlKey); + } + + model.selectionModel.unselect = item.selected(); + item.selected(true); + }; + + this.open = function(item, e) { + if (model.selectionModel.unselect) { + // case: click + ctrlKey on selected item + if (e.ctrlKey) { + item.selected(false); + } + // drop selection + if (!e.ctrlKey && config.manager.dblClickOpen) { + model.itemsModel.unselectItems(e.ctrlKey); + item.selected(true); + } + } + + if(isItemOpenable(e)) { + if(config.options.quickSelect && item.rdo.type === 'file' && isObjectCapable(item.rdo, 'select')) { + selectItem(item.rdo); + } else { + getDetailView(item.rdo); + } + } + }; + + this.remove = function() { + model.itemsModel.objects.remove(this); + }; }; - - this.reset = function (data, e) { - restoreItems(); + + var FolderAjaxLoader = function(path) { + var folder_loader = this, + handlers = [], + /** @type {PanelLoader[]} */ + preloaders = []; + + /** + * @param {PanelLoader} preloader + * @returns {FolderAjaxLoader} + */ + this.setPreloader = function(preloader) { + preloaders.push(preloader); + return folder_loader; + }; + + /** + * + * @param {function} callback + * @returns {FolderAjaxLoader} + */ + this.setDataHandler = function(callback) { + handlers.push(callback); + return folder_loader; + }; + + this.load = function(folderLoader) { + preloaders.forEach(function (preloader, index, array) { + preloader.beforeLoad(path); + }); + + folderLoader().then(function(response) { + if(response.data) { + $.each(handlers, function(i, handler) { + handler(response.data, path); + }); + + $.each(preloaders, function(i, preloader) { + preloader.afterLoad(path, response); + }); + } + }); + }; }; - - this.clearInput = function () { - // reset search string - previousValue = ''; - search_model.value(''); - search_model.isRendered(false); - delayStack.removeTimer('search'); + + var TableViewModel = function() { + var SortableHeader = function(name) { + var thead = this; + this.column = ko.observable(name); + this.order = ko.observable(model.itemsModel.listSortOrder()); + + this.sortClass = ko.pureComputed(function() { + var cssClass; + if(model.itemsModel.listSortField() === thead.column()) { + cssClass = 'sorted sorted-' + this.order(); + } + return cssClass; + }, this); + + this.sort = function() { + var isAscending = thead.order() === 'asc'; + var isSameColumn = model.itemsModel.listSortField() === thead.column(); + thead.order(isSameColumn ? (isAscending ? 'desc' : 'asc') : model.itemsModel.listSortOrder()); + model.itemsModel.listSortField(thead.column()); + model.itemsModel.listSortOrder(thead.order()); + model.itemsModel.sortObjects(); + }; + }; + + this.thName = new SortableHeader('name'); + this.thType = new SortableHeader('type'); + this.thSize = new SortableHeader('size'); + this.thDimensions = new SortableHeader('dimensions'); + this.thModified = new SortableHeader('modified'); }; - - function performSearch() { - if (searchOnTyping) { - // create delayed timer - delayStack.push('search', function() { - searchItems(); - }, config.search.typingDelay); - } else { - searchItems(); - } - } - - function searchItems() { - var searchString = search_model.value(), - subject = config.search.caseSensitive ? searchString : searchString.toLowerCase(); - - if (searchString === '') { - if (searchString !== previousValue) { - restoreItems(); - } else { - fm.warning(lg('search_string_empty')); - } - return; - } - - if (config.search.recursive) { - // recursive search with server-side request - var targetPath = model.currentPath(); - var folderLoader = new FolderAjaxLoader(targetPath); - - folderLoader - .setPreloader(model.itemsModel.getPreloader()) - .setDataHandler(function (dataObject, targetPath) { - var resourceObjects = []; - - if (config.search.caseSensitive) { - $.each(dataObject, function (i, resourceObject) { - if (resourceObject.attributes.name.indexOf(subject) === 0) { - resourceObjects.push(resourceObject); - } - }); - } else { - resourceObjects = dataObject; + + var HeaderModel = function() { + var header_model = this; + + this.closeButton = ko.observable(false); + this.langSwitcher = $.isArray(config.language.available) && config.language.available.length > 0; + + this.closeButtonOnClick = function() { + fm.console('CLOSE button is clicked'); + }; + + this.navHome = function() { + model.previewFile(false); + model.itemsModel.loadDataList(fileRoot); + }; + + this.navLevelUp = function() { + var parentFolder = model.previewFile() + ? getDirname(model.previewModel.rdo().id) + : getParentDirname(model.currentPath()); + + if(model.previewFile()) { + model.previewFile(false); + } + + if(parentFolder !== model.currentPath()) { + model.itemsModel.loadDataList(parentFolder); + } + }; + + this.navRefresh = function() { + if(model.previewFile()) { + model.previewFile(false); + model.previewFile(true); + } else { + model.itemsModel.loadDataList(model.currentPath()); + } + }; + + this.displayGrid = function() { + model.viewMode('grid'); + model.previewFile(false); + + if (model.itemsModel.lazyLoad) { + model.itemsModel.lazyLoad.update(); + } + }; + + this.displayList = function() { + model.viewMode('list'); + model.previewFile(false); + }; + + this.switchLang = function(e) { + var langNew = e.target.value, + langCurrent = langModel.getLang(); + + if (langNew && langNew.toLowerCase() !== langCurrent.toLowerCase()) { + var newUrl, url = window.location.toString(), + regExp = new RegExp('(langCode=)' + langCurrent); + + if (regExp.test(url)) { + newUrl = url.replace(regExp, '$1' + langNew); + } else { + newUrl = url + ($.isEmptyObject(_url_.param()) ? '?' : '#') + 'langCode=' + langNew; + } + window.location.href = newUrl; + } + }; + + this.createFolder = function() { + if(!hasCapability('createFolder')) { + fm.error(lg('NOT_ALLOWED')); + return false; + } + + function makeFolder(e, ui) { + var folderName = ui.getInputValue(); + if(!folderName) { + fm.error(lg('no_foldername')); + return; + } + + buildAjaxRequest('GET', { + mode: 'addfolder', + path: fmModel.currentPath(), + name: folderName + }).done(function(response) { + if (response.data) { + fmModel.addElements(response.data, fmModel.currentPath()); + + ui.closeDialog(); + if (config.options.showConfirmation) { + fm.success(lg('successful_added_folder')); + } } - - var items = model.itemsModel.createItems(resourceObjects); - model.itemsModel.setItemsList(items); - search_model.isRendered(true); - }) - .load(function () { - return seekFolder(targetPath, searchString); - }); - } else { - // client-side search in the currently open folder - $.each(model.itemsModel.objects(), function (i, itemObject) { - var filename = itemObject.rdo.attributes.name; - if (!config.search.caseSensitive) { - filename = filename.toLowerCase(); + }).fail(handleAjaxError); + } + + fm.prompt({ + message: lg('prompt_foldername'), + value: lg('default_foldername'), + okBtn: { + label: lg('create_folder'), + autoClose: false, + click: makeFolder + }, + cancelBtn: { + label: lg('cancel') } - - var matchByName = (filename.indexOf(subject) === 0); - var visibility = !itemObject.cdo.hiddenByType; - visibility = visibility && matchByName; - - itemObject.cdo.hiddenBySearch = !matchByName; - itemObject.visible(visibility); }); - search_model.isRendered(true); - } - } - - function restoreItems() { - search_model.clearInput(); - - // restore original content of the current folder - if (config.search.recursive) { - model.itemsModel.loadDataList(model.currentPath()); - } else { - $.each(model.itemsModel.objects(), function (i, itemObject) { - itemObject.cdo.hiddenBySearch = false; - itemObject.visible(!itemObject.cdo.hiddenByType); + }; + }; + + var SummaryModel = function() { + this.files = ko.observable(null); + this.folders = ko.observable(null); + this.size = ko.observable(null); + this.enabled = ko.observable(false); + + this.doSummarize = function() { + summarizeItems(); + }; + }; + + var FilterModel = function() { + var filter_model = this; + this.name = ko.observable(null); + + this.setName = function(filterName) { + if (filterName && + config.filter && + $.isArray(config.filter[filterName]) + ) { + filter_model.name(filterName); + } + }; + + // return extensions which are match a filter name + this.getExtensions = function() { + if (filter_model.name()) { + return config.filter[filter_model.name()]; + } + return null; + }; + + // check whether file item should be filtered out of the output based on it's extension + this.filterItem = function(itemObject) { + var extensions = filter_model.getExtensions(), + visibility = !itemObject.cdo.hiddenBySearch; + + // set default visibility, required for "all files" filter + itemObject.cdo.hiddenByType = false; + + if (itemObject.rdo.type === 'file' && $.isArray(extensions)) { + var ext = getExtension(itemObject.id), + matchByType = extensions.indexOf(ext) !== -1; + + visibility = visibility && matchByType; + itemObject.cdo.hiddenByType = !matchByType; + } + itemObject.visible(visibility); + }; + + this.filter = function(filterName) { + filter_model.setName(filterName); + + $.each(model.itemsModel.objects(), function(i, itemObject) { + filter_model.filterItem(itemObject); }); + + model.treeModel.mapNodes(function (node) { + filter_model.filterItem(node); + }); + + if (model.itemsModel.lazyLoad) { + model.itemsModel.lazyLoad.update(); + } + }; + + this.reset = function() { + filter_model.name(null); + filter_model.filter(null); + }; + }; + + var SearchModel = function() { + var search_model = this, + previousValue = '', + searchOnTyping = !!config.search.typingDelay; + + this.value = ko.observable(''); + this.isRendered = ko.observable(false); + + this.value.subscribe(function(oldValue) { + previousValue = oldValue; + }, null, 'beforeChange'); + + this.inputKeyUp = function(data, e) { + var keyCode = e.which || e.keyCode, + // https://stackoverflow.com/a/19347349/7095038 + invalidKeyCodes = [16,17,18,27,37,38,39,40]; + + if (searchOnTyping) { + // validate input + if (invalidKeyCodes.indexOf(keyCode) > -1) { + return; + } + // explicit assign value, required for search-on-typing + search_model.value(e.target.value); + } + + // search-on-typing or Enter key pressed + if (searchOnTyping || keyCode === 13) { + performSearch(); + } + }; + + this.seekItems = function(data, e) { + performSearch(); + }; + + this.reset = function (data, e) { + restoreItems(); + }; + + this.clearInput = function () { + // reset search string + previousValue = ''; + search_model.value(''); + search_model.isRendered(false); + delayStack.removeTimer('search'); + }; + + function performSearch() { + if (searchOnTyping) { + // create delayed timer + delayStack.push('search', function() { + searchItems(); + }, config.search.typingDelay); + } else { + searchItems(); + } } - } - }; - - var ClipboardModel = function() { - var cbMode = null, - cbObjects = [], - clipboard_model = this, - active = hasCapability('copy') && hasCapability('move'); - - this.itemsNum = ko.observable(0); - this.enabled = ko.observable(model.config().clipboard.enabled && active); - - this.copy = function() { - if (!clipboard_model.hasCapability('copy')) { - return; - } - cbMode = 'copy'; - cbObjects = model.fetchSelectedItems(); - clipboard_model.itemsNum(cbObjects.length); - }; - - this.cut = function() { - if (!clipboard_model.hasCapability('cut')) { - return; - } - cbMode = 'cut'; - cbObjects = model.fetchSelectedItems(); - clipboard_model.itemsNum(cbObjects.length); - }; - - this.paste = function() { - var targetPath = model.currentPath(); - - if (!clipboard_model.hasCapability('paste') || clipboard_model.isEmpty()) { - return; - } - if (cbMode === null || cbObjects.length === 0) { - fm.warning(lg('clipboard_empty')); - return; - } - - processMultipleActions(cbObjects, function (i, itemObject) { - if (cbMode === 'cut') { - return moveItem(itemObject, targetPath); + + function searchItems() { + var searchString = search_model.value(), + subject = config.search.caseSensitive ? searchString : searchString.toLowerCase(); + + if (searchString === '') { + if (searchString !== previousValue) { + restoreItems(); + } else { + fm.warning(lg('search_string_empty')); + } + return; } - if (cbMode === 'copy') { - return copyItem(itemObject, targetPath); + + if (config.search.recursive) { + // recursive search with server-side request + var targetPath = model.currentPath(); + var folderLoader = new FolderAjaxLoader(targetPath); + + folderLoader + .setPreloader(model.itemsModel.getPreloader()) + .setDataHandler(function (dataObject, targetPath) { + var resourceObjects = []; + + if (config.search.caseSensitive) { + $.each(dataObject, function (i, resourceObject) { + if (resourceObject.attributes.name.indexOf(subject) === 0) { + resourceObjects.push(resourceObject); + } + }); + } else { + resourceObjects = dataObject; + } + + var items = model.itemsModel.createItems(resourceObjects); + model.itemsModel.setItemsList(items); + search_model.isRendered(true); + }) + .load(function () { + return seekFolder(targetPath, searchString); + }); + } else { + // client-side search in the currently open folder + $.each(model.itemsModel.objects(), function (i, itemObject) { + var filename = itemObject.rdo.attributes.name; + if (!config.search.caseSensitive) { + filename = filename.toLowerCase(); + } + + var matchByName = (filename.indexOf(subject) === 0); + var visibility = !itemObject.cdo.hiddenByType; + visibility = visibility && matchByName; + + itemObject.cdo.hiddenBySearch = !matchByName; + itemObject.visible(visibility); + }); + search_model.isRendered(true); } - }, clearClipboard); - }; - - this.clear = function() { - if (!clipboard_model.hasCapability('clear') || clipboard_model.isEmpty()) { - return; } - clearClipboard(); - fm.success(lg('clipboard_cleared')); - }; - - this.isEmpty = function() { - return cbObjects.length === 0; - }; - - this.hasCapability = function(capability) { - if (!clipboard_model.enabled) { - return false; - } - - switch(capability) { - case 'copy': - return hasCapability('copy'); - case 'cut': - return hasCapability('move'); - default: - return true; - } - }; - - function clearClipboard() { - cbObjects = []; - cbMode = null; - clipboard_model.itemsNum(0); - } - }; - - var BreadcrumbsModel = function() { - var bc_model = this; - - this.items = ko.observableArray([]); - - this.clean = function() { - bc_model.items([]); - // push root node - bc_model.add(fileRoot, ''); - }; - - this.add = function(path, label) { - bc_model.items.push(new BcItem(path, label)); - }; - - this.splitPath = function(targetPath) { - var path = fileRoot, - chunks = targetPath.replace(new RegExp('^' + fileRoot), '').split('/'); - - // reset breadcrumbs - bc_model.clean(); - - while (chunks.length > 0) { - var chunk = chunks.shift(); - if (chunk) { - path += chunk + '/'; - bc_model.add(path, chunk); + + function restoreItems() { + search_model.clearInput(); + + // restore original content of the current folder + if (config.search.recursive) { + model.itemsModel.loadDataList(model.currentPath()); + } else { + $.each(model.itemsModel.objects(), function (i, itemObject) { + itemObject.cdo.hiddenBySearch = false; + itemObject.visible(!itemObject.cdo.hiddenByType); + }); } } }; - - this.splitCurrent = function() { - bc_model.splitPath(model.currentPath()); - }; - - this.getLabel = ko.pureComputed(function() { - var label = model.searchModel.isRendered() ? lg('search_results') : lg('current_folder'); - return label + ': '; - }, this); - - var BcItem = function(path, label) { - var bc_item = this; - this.path = path; - this.label = label; - this.isRoot = (path === fileRoot); - this.active = (path === model.currentPath()); - - this.itemClass = function() { - var cssClass = ['nav-item']; - if(bc_item.isRoot) { - cssClass.push('root'); + + var ClipboardModel = function() { + var cbMode = null, + cbObjects = [], + clipboard_model = this, + active = hasCapability('copy') && hasCapability('move'); + + this.itemsNum = ko.observable(0); + this.enabled = ko.observable(model.config().clipboard.enabled && active); + + this.copy = function() { + if (!clipboard_model.hasCapability('copy')) { + return; } - if(bc_item.active) { - cssClass.push('active'); + cbMode = 'copy'; + cbObjects = model.fetchSelectedItems(); + clipboard_model.itemsNum(cbObjects.length); + }; + + this.cut = function() { + if (!clipboard_model.hasCapability('cut')) { + return; } - return cssClass.join(' '); - }; - - this.goto = function(item, e) { - if (!item.active) { - model.itemsModel.loadDataList(item.path); - } + cbMode = 'cut'; + cbObjects = model.fetchSelectedItems(); + clipboard_model.itemsNum(cbObjects.length); }; - }; - }; - - var RenderModel = function() { - var $containerElement, - render_model = this; - - function getRendererInstance(filename) { - if (isMarkdownFile(filename)) { - return new MarkdownRenderer(); - } - if (isCodeMirrorFile(filename)) { - return new CodeMirrorRenderer(); - } - } - - this.rdo = ko.observable({}); - this.content = ko.observable(null); - this.renderer = ko.observable(null); - - this.render = function(data) { - if (render_model.renderer()) { - render_model.renderer().processContent(data); + + this.paste = function() { + var targetPath = model.currentPath(); + + if (!clipboard_model.hasCapability('paste') || clipboard_model.isEmpty()) { + return; + } + if (cbMode === null || cbObjects.length === 0) { + fm.warning(lg('clipboard_empty')); + return; + } + + processMultipleActions(cbObjects, function (i, itemObject) { + if (cbMode === 'cut') { + return moveItem(itemObject, targetPath); + } + if (cbMode === 'copy') { + return copyItem(itemObject, targetPath); + } + }, clearClipboard); + }; + + this.clear = function() { + if (!clipboard_model.hasCapability('clear') || clipboard_model.isEmpty()) { + return; + } + clearClipboard(); + fm.success(lg('clipboard_cleared')); + }; + + this.isEmpty = function() { + return cbObjects.length === 0; + }; + + this.hasCapability = function(capability) { + if (!clipboard_model.enabled) { + return false; + } + + switch(capability) { + case 'copy': + return hasCapability('copy'); + case 'cut': + return hasCapability('move'); + default: + return true; + } + }; + + function clearClipboard() { + cbObjects = []; + cbMode = null; + clipboard_model.itemsNum(0); } }; - - this.setRenderer = function(resourceObject) { - render_model.rdo(resourceObject); - render_model.renderer(getRendererInstance(resourceObject.attributes.name)); - }; - - this.setContainer = function(templateElements) { - $.each(templateElements, function () { - if ($(this).hasClass('fm-renderer-container')) { - $containerElement = $(this); - return false; + + var BreadcrumbsModel = function() { + var bc_model = this; + + this.items = ko.observableArray([]); + + this.clean = function() { + bc_model.items([]); + // push root node + bc_model.add(fileRoot, ''); + }; + + this.add = function(path, label) { + bc_model.items.push(new BcItem(path, label)); + }; + + this.splitPath = function(targetPath) { + var path = fileRoot, + chunks = targetPath.replace(new RegExp('^' + fileRoot), '').split('/'); + + // reset breadcrumbs + bc_model.clean(); + + while (chunks.length > 0) { + var chunk = chunks.shift(); + if (chunk) { + path += chunk + '/'; + bc_model.add(path, chunk); + } } - }); - - render_model.renderer().processDomElements($containerElement); - }; - - var CodeMirrorRenderer = function() { - this.name = 'codeMirror'; - this.interactive = false; - - var instance = new EditorModel(); - - this.processContent = function(data) { - instance.render(data); - render_model.content(data); - }; - - this.processDomElements = function($container) { - if (!instance.instance) { - var textarea = $container.find('.fm-cm-renderer-content')[0], - extension = getExtension(render_model.rdo().id); - - instance.createInstance(extension, textarea, { - readOnly: 'nocursor', - styleActiveLine: false, - lineNumbers: false - }); - } - }; - }; - - var MarkdownRenderer = function() { - this.name = 'markdown'; - this.interactive = true; - - var instance = window.markdownit({ - // Basic options: - html: true, - linkify: true, - typographer: true, - - // Custom highlight function to apply CSS class `highlight`: - highlight: function (str, lang) { - if (lang && hljs.getLanguage(lang)) { - try { - return '
' +
-                                    hljs.highlight(lang, str, true).value +
-                                    '
'; - } catch (__) {} + }; + + this.splitCurrent = function() { + bc_model.splitPath(model.currentPath()); + }; + + this.getLabel = ko.pureComputed(function() { + var label = model.searchModel.isRendered() ? lg('search_results') : lg('current_folder'); + return label + ': '; + }, this); + + var BcItem = function(path, label) { + var bc_item = this; + this.path = path; + this.label = label; + this.isRoot = (path === fileRoot); + this.active = (path === model.currentPath()); + + this.itemClass = function() { + var cssClass = ['nav-item']; + if(bc_item.isRoot) { + cssClass.push('root'); } - return '
' + instance.utils.escapeHtml(str) + '
'; - }, - - // custom link function to enable and file d/ls: - replaceLink: function (link, env) { - - // do not change if link as http:// or ftp:// or mailto: etc. - if (link.search('://') !== -1 || startsWith(link, 'mailto:')) { - return link; + if(bc_item.active) { + cssClass.push('active'); } - - // define path depending on absolute / relative link type - var basePath = (startsWith(link, '/')) ? fileRoot : getDirname(render_model.rdo().id); - var path = basePath + ltrim(link, '/'); - - if (isMarkdownFile(path)) { - // to open file in preview mode upon click - return path; - } else { - var queryParams = extendRequestParams('GET', { - mode: 'readfile', - path: path - }); - return buildConnectorUrl(queryParams); + return cssClass.join(' '); + }; + + this.goto = function(item, e) { + if (!item.active) { + model.itemsModel.loadDataList(item.path); } + }; + }; + }; + + var RenderModel = function() { + var $containerElement, + render_model = this; + + function getRendererInstance(filename) { + if (isMarkdownFile(filename)) { + return new MarkdownRenderer(); + } + if (isCodeMirrorFile(filename)) { + return new CodeMirrorRenderer(); + } + } + + this.rdo = ko.observable({}); + this.content = ko.observable(null); + this.renderer = ko.observable(null); + + this.render = function(data) { + if (render_model.renderer()) { + render_model.renderer().processContent(data); } - }).use(window.markdownitReplaceLink); - - this.processContent = function(data) { - var result = instance.render(data); - render_model.content(result); - setLinksBehavior(); }; - - this.processDomElements = function($container) {}; - - function setLinksBehavior() { - // add onClick events to local .md file links (to perform AJAX previews) - $containerElement.find('a').each(function() { - var href = $(this).attr('href'), - editor = fmModel.previewModel.editor; - - if (editor.enabled() && editor.isInteractive()) { - // prevent user from losing unsaved changes in preview mode - // in case of clicking on a link that jumps off the page - $(this).off('click'); - $(this).on('click', function () { - return false; // prevent onClick event + + this.setRenderer = function(resourceObject) { + render_model.rdo(resourceObject); + render_model.renderer(getRendererInstance(resourceObject.attributes.name)); + }; + + this.setContainer = function(templateElements) { + $.each(templateElements, function () { + if ($(this).hasClass('fm-renderer-container')) { + $containerElement = $(this); + return false; + } + }); + + render_model.renderer().processDomElements($containerElement); + }; + + var CodeMirrorRenderer = function() { + this.name = 'codeMirror'; + this.interactive = false; + + var instance = new EditorModel(); + + this.processContent = function(data) { + instance.render(data); + render_model.content(data); + }; + + this.processDomElements = function($container) { + if (!instance.instance) { + var textarea = $container.find('.fm-cm-renderer-content')[0], + extension = getExtension(render_model.rdo().id); + + instance.createInstance(extension, textarea, { + readOnly: 'nocursor', + styleActiveLine: false, + lineNumbers: false }); - } else { - if (href.search('://') !== -1 || startsWith(href, 'mailto:')) { - return; // do nothing + } + }; + }; + + var MarkdownRenderer = function() { + this.name = 'markdown'; + this.interactive = true; + + var instance = window.markdownit({ + // Basic options: + html: true, + linkify: true, + typographer: true, + + // Custom highlight function to apply CSS class `highlight`: + highlight: function (str, lang) { + if (lang && hljs.getLanguage(lang)) { + try { + return '
' +
+                                        hljs.highlight(lang, str, true).value +
+                                        '
'; + } catch (__) {} } - - if (isMarkdownFile(href)) { - // open file in preview mode for clicked link - $(this).on('click', function (e) { - getItemInfo(href).then(function(response) { - if(response.data) { - getDetailView(response.data); - } - }); - - return false; // prevent onClick event + return '
' + instance.utils.escapeHtml(str) + '
'; + }, + + // custom link function to enable and file d/ls: + replaceLink: function (link, env) { + + // do not change if link as http:// or ftp:// or mailto: etc. + if (link.search('://') !== -1 || startsWith(link, 'mailto:')) { + return link; + } + + // define path depending on absolute / relative link type + var basePath = (startsWith(link, '/')) ? fileRoot : getDirname(render_model.rdo().id); + var path = basePath + ltrim(link, '/'); + + if (isMarkdownFile(path)) { + // to open file in preview mode upon click + return path; + } else { + var queryParams = extendRequestParams('GET', { + mode: 'readfile', + path: path }); + return buildConnectorUrl(queryParams); } } - }); - } - }; - }; - - var EditorModel = function() { - var editor_model = this, - delayedContent = null; - - this.instance = null; - this.enabled = ko.observable(false); - this.content = ko.observable(null); - this.mode = ko.observable(null); - this.isInteractive = ko.observable(false); - - this.mode.subscribe(function(mode) { - if (mode) { - editor_model.instance.setOption('mode', mode); - if (delayedContent) { - drawContent(delayedContent); - delayedContent = null; + }).use(window.markdownitReplaceLink); + + this.processContent = function(data) { + var result = instance.render(data); + render_model.content(result); + setLinksBehavior(); + }; + + this.processDomElements = function($container) {}; + + function setLinksBehavior() { + // add onClick events to local .md file links (to perform AJAX previews) + $containerElement.find('a').each(function() { + var href = $(this).attr('href'), + editor = fmModel.previewModel.editor; + + if (editor.enabled() && editor.isInteractive()) { + // prevent user from losing unsaved changes in preview mode + // in case of clicking on a link that jumps off the page + $(this).off('click'); + $(this).on('click', function () { + return false; // prevent onClick event + }); + } else { + if (href.search('://') !== -1 || startsWith(href, 'mailto:')) { + return; // do nothing + } + + if (isMarkdownFile(href)) { + // open file in preview mode for clicked link + $(this).on('click', function (e) { + getItemInfo(href).then(function(response) { + if(response.data) { + getDetailView(response.data); + } + }); + + return false; // prevent onClick event + }); + } + } + }); } - } - }); - - this.render = function(content) { - if (editor_model.mode()) { - drawContent(content); - } else { - delayedContent = content; - } + }; }; - - this.createInstance = function(extension, element, options) { - var cm, - defaults = { - readOnly: 'nocursor', - styleActiveLine: false, - viewportMargin: Infinity, - lineNumbers: config.editor.lineNumbers, - lineWrapping: config.editor.lineWrapping, - theme: config.editor.theme, - matchBrackets: config.editor.matchBrackets, - extraKeys: { - 'F11': function (cm) { - cm.setOption('fullScreen', !cm.getOption('fullScreen')); - }, - 'Esc': function (cm) { - if (cm.getOption('fullScreen')) cm.setOption('fullScreen', false); - } + + var EditorModel = function() { + var editor_model = this, + delayedContent = null; + + this.instance = null; + this.enabled = ko.observable(false); + this.content = ko.observable(null); + this.mode = ko.observable(null); + this.isInteractive = ko.observable(false); + + this.mode.subscribe(function(mode) { + if (mode) { + editor_model.instance.setOption('mode', mode); + if (delayedContent) { + drawContent(delayedContent); + delayedContent = null; } - }; - - cm = CodeMirror.fromTextArea(element, $.extend({}, defaults, options)); - - cm.on('changes', function(cm, change) { - editor_model.content(cm.getValue()); + } }); - - editor_model.instance = cm; - - includeAssets(extension); - }; - - function drawContent(content) { - editor_model.enabled(true); - editor_model.instance.setValue(content); - // to make sure DOM is ready to render content - setTimeout(function() { - editor_model.instance.refresh(); - }, 0); - } - - function includeAssets(extension) { - var assets = [], - currentMode = 'default'; - - // highlight code according to extension file - if (config.editor.codeHighlight) { - if (extension === 'js') { - assets.push('/libs/CodeMirror/mode/javascript/javascript.js'); - currentMode = 'javascript'; - } - if (extension === 'css') { - assets.push('/libs/CodeMirror/mode/css/css.js'); - currentMode = 'css'; - } - if (extension === 'html') { - assets.push('/libs/CodeMirror/mode/xml/xml.js'); - currentMode = 'text/html'; - } - if (extension === 'xml') { - assets.push('/libs/CodeMirror/mode/xml/xml.js'); - currentMode = 'application/xml'; - } - if (extension === 'php') { - assets.push('/libs/CodeMirror/mode/htmlmixed/htmlmixed.js'); - assets.push('/libs/CodeMirror/mode/xml/xml.js'); - assets.push('/libs/CodeMirror/mode/javascript/javascript.js'); - assets.push('/libs/CodeMirror/mode/css/css.js'); - assets.push('/libs/CodeMirror/mode/clike/clike.js'); - assets.push('/libs/CodeMirror/mode/php/php.js'); - currentMode = 'application/x-httpd-php'; - } - if (extension === 'java') { - assets.push('/libs/CodeMirror/mode/clike/clike.js'); - currentMode = 'text/x-java'; - } - if (extension === 'sql') { - assets.push('/libs/CodeMirror/mode/sql/sql.js'); - currentMode = 'text/x-mysql'; - } - if (extension === 'md') { - assets.push('/libs/CodeMirror/addon/mode/overlay.js'); - assets.push('/libs/CodeMirror/mode/xml/xml.js'); - assets.push('/libs/CodeMirror/mode/markdown/markdown.js'); - assets.push('/libs/CodeMirror/mode/gfm/gfm.js'); - assets.push('/libs/CodeMirror/mode/javascript/javascript.js'); - assets.push('/libs/CodeMirror/mode/css/css.js'); - assets.push('/libs/CodeMirror/mode/htmlmixed/htmlmixed.js'); - assets.push('/libs/CodeMirror/mode/clike/clike.js'); - assets.push('/libs/CodeMirror/mode/shell/shell.js'); - assets.push('/libs/CodeMirror/mode/meta.js'); - currentMode = 'gfm'; - } - if (extension === 'sh') { - assets.push('/libs/CodeMirror/addon/mode/overlay.js'); - assets.push('/libs/CodeMirror/mode/markdown/markdown.js'); - assets.push('/libs/CodeMirror/mode/gfm/gfm.js'); - assets.push('/libs/CodeMirror/mode/javascript/javascript.js'); - assets.push('/libs/CodeMirror/mode/css/css.js'); - assets.push('/libs/CodeMirror/mode/htmlmixed/htmlmixed.js'); - assets.push('/libs/CodeMirror/mode/clike/clike.js'); - assets.push('/libs/CodeMirror/mode/meta.js'); - assets.push('/libs/CodeMirror/mode/shell/shell.js'); - currentMode = 'shell'; + + this.render = function(content) { + if (editor_model.mode()) { + drawContent(content); + } else { + delayedContent = content; + } + }; + + this.createInstance = function(extension, element, options) { + var cm, + defaults = { + readOnly: 'nocursor', + styleActiveLine: false, + viewportMargin: Infinity, + lineNumbers: config.editor.lineNumbers, + lineWrapping: config.editor.lineWrapping, + theme: config.editor.theme, + matchBrackets: config.editor.matchBrackets, + extraKeys: { + 'F11': function (cm) { + cm.setOption('fullScreen', !cm.getOption('fullScreen')); + }, + 'Esc': function (cm) { + if (cm.getOption('fullScreen')) cm.setOption('fullScreen', false); + } + } + }; + + cm = CodeMirror.fromTextArea(element, $.extend({}, defaults, options)); + + cm.on('changes', function(cm, change) { + editor_model.content(cm.getValue()); + }); + + editor_model.instance = cm; + + includeAssets(extension); + }; + + function drawContent(content) { + editor_model.enabled(true); + editor_model.instance.setValue(content); + // to make sure DOM is ready to render content + setTimeout(function() { + editor_model.instance.refresh(); + }, 0); + } + + function includeAssets(extension) { + var assets = [], + currentMode = 'default'; + + // highlight code according to extension file + if (config.editor.codeHighlight) { + if (extension === 'js') { + assets.push('/libs/CodeMirror/mode/javascript/javascript.js'); + currentMode = 'javascript'; + } + if (extension === 'css') { + assets.push('/libs/CodeMirror/mode/css/css.js'); + currentMode = 'css'; + } + if (extension === 'html') { + assets.push('/libs/CodeMirror/mode/xml/xml.js'); + currentMode = 'text/html'; + } + if (extension === 'xml') { + assets.push('/libs/CodeMirror/mode/xml/xml.js'); + currentMode = 'application/xml'; + } + if (extension === 'php') { + assets.push('/libs/CodeMirror/mode/htmlmixed/htmlmixed.js'); + assets.push('/libs/CodeMirror/mode/xml/xml.js'); + assets.push('/libs/CodeMirror/mode/javascript/javascript.js'); + assets.push('/libs/CodeMirror/mode/css/css.js'); + assets.push('/libs/CodeMirror/mode/clike/clike.js'); + assets.push('/libs/CodeMirror/mode/php/php.js'); + currentMode = 'application/x-httpd-php'; + } + if (extension === 'java') { + assets.push('/libs/CodeMirror/mode/clike/clike.js'); + currentMode = 'text/x-java'; + } + if (extension === 'sql') { + assets.push('/libs/CodeMirror/mode/sql/sql.js'); + currentMode = 'text/x-mysql'; + } + if (extension === 'md') { + assets.push('/libs/CodeMirror/addon/mode/overlay.js'); + assets.push('/libs/CodeMirror/mode/xml/xml.js'); + assets.push('/libs/CodeMirror/mode/markdown/markdown.js'); + assets.push('/libs/CodeMirror/mode/gfm/gfm.js'); + assets.push('/libs/CodeMirror/mode/javascript/javascript.js'); + assets.push('/libs/CodeMirror/mode/css/css.js'); + assets.push('/libs/CodeMirror/mode/htmlmixed/htmlmixed.js'); + assets.push('/libs/CodeMirror/mode/clike/clike.js'); + assets.push('/libs/CodeMirror/mode/shell/shell.js'); + assets.push('/libs/CodeMirror/mode/meta.js'); + currentMode = 'gfm'; + } + if (extension === 'sh') { + assets.push('/libs/CodeMirror/addon/mode/overlay.js'); + assets.push('/libs/CodeMirror/mode/markdown/markdown.js'); + assets.push('/libs/CodeMirror/mode/gfm/gfm.js'); + assets.push('/libs/CodeMirror/mode/javascript/javascript.js'); + assets.push('/libs/CodeMirror/mode/css/css.js'); + assets.push('/libs/CodeMirror/mode/htmlmixed/htmlmixed.js'); + assets.push('/libs/CodeMirror/mode/clike/clike.js'); + assets.push('/libs/CodeMirror/mode/meta.js'); + assets.push('/libs/CodeMirror/mode/shell/shell.js'); + currentMode = 'shell'; + } } - } - - if(assets.length) { - assets.push(function() { - // after all required assets are loaded + + if(assets.length) { + assets.push(function() { + // after all required assets are loaded + editor_model.mode(currentMode); + }); + loadAssets(assets); + } else { editor_model.mode(currentMode); - }); - loadAssets(assets); - } else { - editor_model.mode(currentMode); - } - } - }; - - var DragAndDropModel = function() { - var drag_model = this, - restrictedCssClass = 'drop-restricted', - $dragHelperTemplate = $('#drag-helper-template'); - - this.items = []; - this.hoveredItem = null; - this.dragHelper = null; - this.isScrolling = false; - this.isScrolled = false; - this.hoveredCssClass = 'drop-hover'; - - this.makeDraggable = function(item, element) { - if(item.rdo.type === 'file' || item.rdo.type === 'folder') { - $(element).draggable({ - distance: 3, - cursor: 'pointer', - cursorAt: { - left: Math.floor($dragHelperTemplate.width() / 2), - bottom: 15 - }, - scroll: false, - appendTo: $wrapper, - containment: $container, - refreshPositions: false, - helper: function () { - var $cloned, - iconClass; - - if (model.fetchSelectedItems(item.constructor.name).length > 1) { - iconClass = 'ico_multiple'; - } else { - iconClass = (item.rdo.type === 'folder') - ? 'ico_folder' - : 'ico_file ico_ext_' + getExtension(item.rdo.id); - } - - $cloned = $dragHelperTemplate.children('.drag-helper').clone(); - $cloned.find('.clip').addClass(iconClass); - - drag_model.dragHelper = $cloned; - return $cloned; - }, - start: function(event, ui) { - drag_model.items = model.fetchSelectedItems(item.constructor.name); - }, - drag: function(event, ui) { - $(this).draggable('option', 'refreshPositions', drag_model.isScrolling || drag_model.isScrolled); - drag_model.isScrolled = false; - }, - stop: function(event, ui) { - drag_model.items = []; - drag_model.dragHelper = null; - } - }); + } } }; - - this.makeDroppable = function(targetItem, element) { - if(targetItem.rdo.type === 'folder' || targetItem.rdo.type === 'parent') { - $(element).droppable({ - tolerance: 'pointer', - enableExtendedEvents: targetItem instanceof ItemObject, - accept: function ($draggable) { - var dragItem = ko.dataFor($draggable[0]), - type = dragItem ? dragItem.rdo.type : null; - - return (type === 'file' || type === 'folder'); - }, - over: function (event, ui) { - // prevent "over" event fire before "out" event - // http://stackoverflow.com/a/28457286/7095038 - setTimeout(function () { + + var DragAndDropModel = function() { + var drag_model = this, + restrictedCssClass = 'drop-restricted', + $dragHelperTemplate = $('#drag-helper-template'); + + this.items = []; + this.hoveredItem = null; + this.dragHelper = null; + this.isScrolling = false; + this.isScrolled = false; + this.hoveredCssClass = 'drop-hover'; + + this.makeDraggable = function(item, element) { + if(item.rdo.type === 'file' || item.rdo.type === 'folder') { + $(element).draggable({ + distance: 3, + cursor: 'pointer', + cursorAt: { + left: Math.floor($dragHelperTemplate.width() / 2), + bottom: 15 + }, + scroll: false, + appendTo: $wrapper, + containment: $container, + refreshPositions: false, + helper: function () { + var $cloned, + iconClass; + + if (model.fetchSelectedItems(item.constructor.name).length > 1) { + iconClass = 'ico_multiple'; + } else { + iconClass = (item.rdo.type === 'folder') + ? 'ico_folder' + : 'ico_file ico_ext_' + getExtension(item.rdo.id); + } + + $cloned = $dragHelperTemplate.children('.drag-helper').clone(); + $cloned.find('.clip').addClass(iconClass); + + drag_model.dragHelper = $cloned; + return $cloned; + }, + start: function(event, ui) { + drag_model.items = model.fetchSelectedItems(item.constructor.name); + }, + drag: function(event, ui) { + $(this).draggable('option', 'refreshPositions', drag_model.isScrolling || drag_model.isScrolled); + drag_model.isScrolled = false; + }, + stop: function(event, ui) { + drag_model.items = []; + drag_model.dragHelper = null; + } + }); + } + }; + + this.makeDroppable = function(targetItem, element) { + if(targetItem.rdo.type === 'folder' || targetItem.rdo.type === 'parent') { + $(element).droppable({ + tolerance: 'pointer', + enableExtendedEvents: targetItem instanceof ItemObject, + accept: function ($draggable) { + var dragItem = ko.dataFor($draggable[0]), + type = dragItem ? dragItem.rdo.type : null; + + return (type === 'file' || type === 'folder'); + }, + over: function (event, ui) { + // prevent "over" event fire before "out" event + // http://stackoverflow.com/a/28457286/7095038 + setTimeout(function () { + markHovered(null); + markRestricted(ui.helper, false); + + if (!isDropAllowed(targetItem)) { + markRestricted(ui.helper, true); + } + markHovered(targetItem); + }, 0); + }, + out: function (event, ui) { markHovered(null); markRestricted(ui.helper, false); - + }, + drop: function (event, ui) { + markHovered(null); + if (!isDropAllowed(targetItem)) { - markRestricted(ui.helper, true); + return false; } - markHovered(targetItem); - }, 0); - }, - out: function (event, ui) { - markHovered(null); - markRestricted(ui.helper, false); - }, - drop: function (event, ui) { - markHovered(null); - - if (!isDropAllowed(targetItem)) { - return false; + + processMultipleActions(drag_model.items, function (i, itemObject) { + return moveItem(itemObject.rdo, targetItem.id); + }); + } + }); + } + }; + + // check whether draggable items can be accepted by target item + function isDropAllowed(targetItem) { + var matches = $.grep(drag_model.items, function(itemObject, i) { + if (targetItem.rdo.type === 'folder' || targetItem.rdo.type === 'parent') { + // drop folder inside descending folders (filetree) + if (startsWith(targetItem.rdo.id, itemObject.rdo.id)) { + return true; + } + // drop items inside the same folder (filetree) + if (targetItem.rdo.id === getClosestNode(itemObject.rdo.id)) { + return true; } - - processMultipleActions(drag_model.items, function (i, itemObject) { - return moveItem(itemObject.rdo, targetItem.id); - }); } + // drop item to itself + return (itemObject.id === targetItem.id); }); + // prevent on moving (to) protect folder or to the one of selected items + return (targetItem.rdo.attributes.writable && matches.length === 0); + } + + // mark item as hovered if it accepts draggable item + function markHovered(item) { + if (drag_model.hoveredItem !== null) { + drag_model.hoveredItem.dragHovered(false); + } + drag_model.hoveredItem = item; + if (item) { + item.dragHovered(true); + } + } + + // mark helper as restricted if target item doesn't accept draggable item + function markRestricted($helper, flag) { + if (flag) { + $helper.addClass(restrictedCssClass); + } else { + $helper.removeClass(restrictedCssClass); + } } }; - - // check whether draggable items can be accepted by target item - function isDropAllowed(targetItem) { - var matches = $.grep(drag_model.items, function(itemObject, i) { - if (targetItem.rdo.type === 'folder' || targetItem.rdo.type === 'parent') { - // drop folder inside descending folders (filetree) - if (startsWith(targetItem.rdo.id, itemObject.rdo.id)) { - return true; + + var SelectionModel = function() { + this.unselect = false; + }; + + this.treeModel = new TreeModel(); + this.itemsModel = new ItemsModel(); + this.tableViewModel = new TableViewModel(); + this.previewModel = new PreviewModel(); + this.headerModel = new HeaderModel(); + this.summaryModel = new SummaryModel(); + this.filterModel = new FilterModel(); + this.searchModel = new SearchModel(); + this.clipboardModel = new ClipboardModel(); + this.breadcrumbsModel = new BreadcrumbsModel(); + this.ddModel = new DragAndDropModel(); + this.selectionModel = new SelectionModel(); + }; + + + /*--------------------------------------------------------- + Helper functions + ---------------------------------------------------------*/ + + // Wrapper for translate method + var lg = function(key) { + return langModel.translate(key); + }; + + var sortItems = function(items) { + var sortOrder = (fmModel.viewMode() === 'list') ? fmModel.itemsModel.listSortOrder() : configSortOrder; + var sortParams = { + natural: true, + order: sortOrder === 'asc' ? 1 : -1, + cases: false + }; + + items.sort(function(a, b) { + var sortReturnNumber, + aa = getSortSubject(a), + bb = getSortSubject(b); + + if (aa === bb) { + sortReturnNumber = 0; + } else { + if (aa === undefined || bb === undefined) { + sortReturnNumber = 0; + } else { + if(!sortParams.natural || (!isNaN(aa) && !isNaN(bb))) { + sortReturnNumber = aa < bb ? -1 : (aa > bb ? 1 : 0); + } else { + sortReturnNumber = naturalCompare(aa, bb); } - // drop items inside the same folder (filetree) - if (targetItem.rdo.id === getClosestNode(itemObject.rdo.id)) { - return true; + } + } + // lastly assign asc/desc + sortReturnNumber *= sortParams.order; + return sortReturnNumber; + }); + + /** + * Get the string/number to be sorted by checking the array value with the criterium. + * @item KO or treeNode object + */ + function getSortSubject(item) { + var sortBy, + sortField = configSortField; + + if(fmModel.viewMode() === 'list') { + sortField = fmModel.itemsModel.listSortField(); + } + + switch(sortField) { + case 'type': + sortBy = item.cdo.extension || ''; + break; + case 'size': + sortBy = item.rdo.attributes.size; + break; + case 'modified': + sortBy = item.rdo.attributes.modified; + break; + case 'dimensions': + sortBy = item.cdo.dimensions || ''; + break; + default: + sortBy = item.rdo.attributes.name; + } + + // strings should be ordered in lowercase (unless specified) + if (typeof sortBy === 'string') { + if (!sortParams.cases) { + sortBy = sortBy.toLowerCase(); + } + // spaces/newlines + sortBy = sortBy.replace(/\s+/g, ' '); + } + return sortBy; + } + + /** + * Compare strings using natural sort order + * http://web.archive.org/web/20130826203933/http://my.opera.com/GreyWyvern/blog/show.dml/1671288 + */ + function naturalCompare(a, b) { + var aa = chunkify(a.toString()), + bb = chunkify(b.toString()); + for (var x = 0; aa[x] && bb[x]; x++) { + if (aa[x] !== bb[x]) { + var c = Number(aa[x]), + d = Number(bb[x]); + if (c == aa[x] && d == bb[x]) { + return c - d; + } else { + return aa[x] > bb[x] ? 1 : -1; } } - // drop item to itself - return (itemObject.id === targetItem.id); - }); - // prevent on moving (to) protect folder or to the one of selected items - return (targetItem.rdo.attributes.writable && matches.length === 0); + } + return aa.length - bb.length; } - - // mark item as hovered if it accepts draggable item - function markHovered(item) { - if (drag_model.hoveredItem !== null) { - drag_model.hoveredItem.dragHovered(false); + + /** + * Split a string into an array by type: numeral or string + */ + function chunkify(t) { + var tz = [], x = 0, y = -1, n = 0, i, j; + while (i = (j = t.charAt(x++)).charCodeAt(0)) { + var m = (i == 46 || (i >=48 && i <= 57)); + if (m !== n) { + tz[++y] = ''; + n = m; + } + tz[y] += j; } - drag_model.hoveredItem = item; - if (item) { - item.dragHovered(true); + 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); } } - - // mark helper as restricted if target item doesn't accept draggable item - function markRestricted($helper, flag) { - if (flag) { - $helper.addClass(restrictedCssClass); + 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 { - $helper.removeClass(restrictedCssClass); + items.push(folderItems[k]); } } + + return items; }; - - var SelectionModel = function() { - this.unselect = false; - }; - - this.treeModel = new TreeModel(); - this.itemsModel = new ItemsModel(); - this.tableViewModel = new TableViewModel(); - this.previewModel = new PreviewModel(); - this.headerModel = new HeaderModel(); - this.summaryModel = new SummaryModel(); - this.filterModel = new FilterModel(); - this.searchModel = new SearchModel(); - this.clipboardModel = new ClipboardModel(); - this.breadcrumbsModel = new BreadcrumbsModel(); - this.ddModel = new DragAndDropModel(); - this.selectionModel = new SelectionModel(); - }; - - - /*--------------------------------------------------------- - Helper functions - ---------------------------------------------------------*/ - - // Wrapper for translate method - var lg = function(key) { - return langModel.translate(key); - }; - - var sortItems = function(items) { - var sortOrder = (fmModel.viewMode() === 'list') ? fmModel.itemsModel.listSortOrder() : configSortOrder; - var sortParams = { - natural: true, - order: sortOrder === 'asc' ? 1 : -1, - cases: false - }; - - items.sort(function(a, b) { - var sortReturnNumber, - aa = getSortSubject(a), - bb = getSortSubject(b); - - if (aa === bb) { - sortReturnNumber = 0; - } else { - if (aa === undefined || bb === undefined) { - sortReturnNumber = 0; - } else { - if(!sortParams.natural || (!isNaN(aa) && !isNaN(bb))) { - sortReturnNumber = aa < bb ? -1 : (aa > bb ? 1 : 0); - } else { - sortReturnNumber = naturalCompare(aa, bb); - } - } - } - // lastly assign asc/desc - sortReturnNumber *= sortParams.order; - return sortReturnNumber; - }); - - /** - * Get the string/number to be sorted by checking the array value with the criterium. - * @item KO or treeNode object - */ - function getSortSubject(item) { - var sortBy, - sortField = configSortField; - - if(fmModel.viewMode() === 'list') { - sortField = fmModel.itemsModel.listSortField(); - } - - switch(sortField) { - case 'type': - sortBy = item.cdo.extension || ''; - break; - case 'size': - sortBy = item.rdo.attributes.size; - break; - case 'modified': - sortBy = item.rdo.attributes.modified; - break; - case 'dimensions': - sortBy = item.cdo.dimensions || ''; - break; - default: - sortBy = item.rdo.attributes.name; - } - - // strings should be ordered in lowercase (unless specified) - if (typeof sortBy === 'string') { - if (!sortParams.cases) { - sortBy = sortBy.toLowerCase(); - } - // spaces/newlines - sortBy = sortBy.replace(/\s+/g, ' '); - } - return sortBy; - } - - /** - * Compare strings using natural sort order - * http://web.archive.org/web/20130826203933/http://my.opera.com/GreyWyvern/blog/show.dml/1671288 - */ - function naturalCompare(a, b) { - var aa = chunkify(a.toString()), - bb = chunkify(b.toString()); - for (var x = 0; aa[x] && bb[x]; x++) { - if (aa[x] !== bb[x]) { - var c = Number(aa[x]), - d = Number(bb[x]); - if (c == aa[x] && d == bb[x]) { - return c - d; - } else { - return aa[x] > bb[x] ? 1 : -1; - } - } - } - return aa.length - bb.length; - } - - /** - * Split a string into an array by type: numeral or string - */ - function chunkify(t) { - var tz = [], x = 0, y = -1, n = 0, i, j; - while (i = (j = t.charAt(x++)).charCodeAt(0)) { - var m = (i == 46 || (i >=48 && i <= 57)); - if (m !== n) { - tz[++y] = ''; - n = m; - } - tz[y] += j; - } - 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; - }; - - // Test if a given url exists - var file_exists = function(url) { - return $.ajax({ - type: 'HEAD', - url: url - }); - }; - - // Retrieves config settings from config files - var loadConfigFile = function (type) { - var url = null; - type = (typeof type === 'undefined') ? 'user' : type; - - if(type === 'user') { - if(_url_.param('config')) { - url = fm.settings.baseUrl + '/config/' + _url_.param('config'); - } else { - url = fm.settings.configUrl ? fm.settings.configUrl : fm.settings.baseUrl + '/config/filemanager.config.json'; // if configUrl is defined - } - } else { - url = fm.settings.baseUrl + '/config/filemanager.config.default.json'; - } - - return $.ajax({ - type: 'GET', - url: url, - dataType: 'json', - cache: false, - error: function(response) { - fm.error('Given config file (' + url + ') does not exist!'); - } - }); - }; - - // Loads a given js/css files dynamically into header - var loadAssets = function(assets) { - for (var i = 0, l = assets.length; i < l; i++) { - if(typeof assets[i] === 'string') { - assets[i] = fm.settings.baseUrl + assets[i]; - } + + // Test if a given url exists + var file_exists = function(url) { + return $.ajax({ + type: 'HEAD', + url: url + }); + }; + + // Retrieves config settings from config files + var loadConfigFile = function (type) { + var url = null; + type = (typeof type === 'undefined') ? 'user' : type; + + if(type === 'user') { + if(_url_.param('config')) { + url = fm.settings.baseUrl + '/config/' + _url_.param('config'); + } else { + url = fm.settings.configUrl ? fm.settings.configUrl : fm.settings.baseUrl + '/config/filemanager.config.json'; // if configUrl is defined + } + } else { + url = fm.settings.baseUrl + '/config/filemanager.config.default.json'; + } + + return $.ajax({ + type: 'GET', + url: url, + dataType: 'json', + cache: false, + error: function(response) { + fm.error('Given config file (' + url + ') does not exist!'); + } + }); + }; + + // Loads a given js/css files dynamically into header + var loadAssets = function(assets) { + for (var i = 0, l = assets.length; i < l; i++) { + if(typeof assets[i] === 'string') { + assets[i] = fm.settings.baseUrl + assets[i]; + } + } + + toast.apply(this, assets); + }; + + // Loads a given js template file into header if not already included + var loadTemplate = function(id, data) { + return $.ajax({ + type: 'GET', + url: fm.settings.baseUrl + '/src/templates/' + id + '.html', + error: handleAjaxError + }); + }; + + // Converts bytes to KB, MB, or GB as needed for display + var formatBytes = function(bytes, round) { + if(!bytes) return ''; + round = round || false; + var n = parseFloat(bytes); + var d = parseFloat(round ? 1000 : 1024); + var c = 0; + var u = [lg('unit_bytes'), lg('unit_kb'), lg('unit_mb'), lg('unit_gb')]; + + while(true) { + if(n < d) { + n = Math.round(n * 100) / 100; + return n + ' ' + u[c]; + } else { + n /= d; + c += 1; + } + } + }; + + // Converts UNIX timestamp to formatted datetime string + var formatTimestamp = function (datetime) { + var isString = typeof datetime === "string"; + var isInteger = typeof datetime === "number" && Math.floor(datetime) === datetime; + + // invalid argument + if (!(isString || isInteger)) return ''; + + // value looks like seconds, while Date() accepts milliseconds + if (isInteger && datetime < 10000000000) { + datetime = datetime * 1000 + } + + var date = new Date(datetime); + + // invalid Date() object, display datetime without formatting + if (!(date instanceof Date) || isNaN(date)) { + return datetime; + } + + // Timezone support requires "iana-tz-data" package: + // https://github.com/globalizejs/globalize/blob/master/README.md#3-iana-time-zone-data + return globalize.formatDate(date, config.formatter.datetime); + }; + + // Format server-side response single error object + var formatServerError = function(errorObject) { + var message; + // look for message in case an error CODE is provided + if (langModel.getLang() && lg(errorObject.title)) { + message = lg(errorObject.title); + $.each(errorObject.meta.arguments, function(i, argument) { + message = message.replace('%s', argument); + }); + } else { + message = errorObject.title; + } + return message; + }; + + // Handle JSON server errors. + var handleJsonErrors = function(errors) { + fm.console('ERROR JSON', errors); + + $.each(errors, function (i, errorObject) { + fm.error(formatServerError(errorObject)); + + if (errorObject.meta.redirect) { + window.location.href = errorObject.meta.redirect; + } + }); + }; + + // Handle ajax request errors. + var handleAjaxError = function(xhr) { + var errorMessage; + + if ($.isPlainObject(xhr) && xhr.responseText) { + var isJSON = (xhr.getResponseHeader('content-type') === 'application/json'); + + // on "readfile" API request (dataType === 'text') + if (!xhr.responseJSON && isJSON) { + xhr.responseJSON = $.parseJSON(xhr.responseText); + } + + if ($.isPlainObject(xhr.responseJSON) && xhr.responseJSON.errors) { + handleJsonErrors(xhr.responseJSON.errors); + } else { + errorMessage = lg('ERROR_SERVER') + ' ' + xhr.responseText; + } + } else { + // $.Deferred().reject() case e.g. + errorMessage = xhr; + } + + if (errorMessage) { + fm.console('ERROR TEXT', errorMessage); + fm.error(errorMessage); + } + }; + + // Check if capability is allowed + function hasCapability(capability) { + return capabilities.indexOf(capability) > -1; } - - toast.apply(this, assets); - }; - - // Loads a given js template file into header if not already included - var loadTemplate = function(id, data) { - return $.ajax({ - type: 'GET', - url: fm.settings.baseUrl + '/src/templates/' + id + '.html', - error: handleAjaxError - }); - }; - - // Converts bytes to KB, MB, or GB as needed for display - var formatBytes = function(bytes, round) { - if(!bytes) return ''; - round = round || false; - var n = parseFloat(bytes); - var d = parseFloat(round ? 1000 : 1024); - var c = 0; - var u = [lg('unit_bytes'), lg('unit_kb'), lg('unit_mb'), lg('unit_gb')]; - - while(true) { - if(n < d) { - n = Math.round(n * 100) / 100; - return n + ' ' + u[c]; - } else { - n /= d; - c += 1; - } - } - }; - - // Converts UNIX timestamp to formatted datetime string - var formatTimestamp = function (datetime) { - var isString = typeof datetime === "string"; - var isInteger = typeof datetime === "number" && Math.floor(datetime) === datetime; - - // invalid argument - if (!(isString || isInteger)) return ''; - - // value looks like seconds, while Date() accepts milliseconds - if (isInteger && datetime < 10000000000) { - datetime = datetime * 1000 + + // Test if resource object has capability + function isObjectCapable(resourceObject, capability) { + if (!hasCapability(capability)) return false; + if (capability === 'select' && resourceObject.type === 'folder') return false; + if (capability === 'extract') { + var extension = getExtension(resourceObject.attributes.name); + return (resourceObject.type === 'file' && extension === 'zip'); + } + if (capability === 'download' && resourceObject.type === 'folder') { + return (config.options.allowFolderDownload === true); + } + if (typeof(resourceObject.attributes.capabilities) !== 'undefined') { + return $.inArray(capability, resourceObject.attributes.capabilities) > -1 + } + return true; } - - var date = new Date(datetime); - - // invalid Date() object, display datetime without formatting - if (!(date instanceof Date) || isNaN(date)) { - return datetime; + + // http://stackoverflow.com/questions/3390930/any-way-to-make-jquery-inarray-case-insensitive + (function($){ + $.extend({ + // Case insensative $.inArray (http://api.jquery.com/jquery.inarray/) + // $.inArrayInsensitive(value, array [, fromIndex]) + // value (type: String) + // The value to search for + // array (type: Array) + // An array through which to search. + // fromIndex (type: Number) + // The index of the array at which to begin the search. + // The default is 0, which will search the whole array. + inArrayInsensitive: function(elem, arr, i){ + // not looking for a string anyways, use default method + if (typeof elem !== 'string'){ + return $.inArray.apply(this, arguments); + } + // confirm array is populated + if (arr){ + var len = arr.length; + i = i ? (i < 0 ? Math.max(0, len + i) : i) : 0; + elem = elem.toLowerCase(); + for (; i < len; i++){ + if (i in arr && arr[i].toLowerCase() == elem){ + return i; + } + } + } + // stick with inArray/indexOf and return -1 on no match + return -1; + } + }); + })(jQuery); + + // Test if file is authorized, based on extension only + var isAuthorizedFile = function(filename) { + var ext = getExtension(filename); + + if (config.security.extensions.ignoreCase) { + if(config.security.extensions.policy === 'ALLOW_LIST') { + if($.inArrayInsensitive(ext, config.security.extensions.restrictions) !== -1) return true; + } + if(config.security.extensions.policy === 'DISALLOW_LIST') { + if($.inArrayInsensitive(ext, config.security.extensions.restrictions) === -1) return true; + } + } else { + if(config.security.extensions.policy === 'ALLOW_LIST') { + if($.inArray(ext, config.security.extensions.restrictions) !== -1) return true; + } + if(config.security.extensions.policy === 'DISALLOW_LIST') { + if($.inArray(ext, config.security.extensions.restrictions) === -1) return true; + } + } + + return false; + }; + + // Test if path is dir + var isFile = function(path) { + return path.charAt(path.length - 1) !== '/'; + }; + + // Replace all leading or trailing chars with an empty string + var trim = function(string, char) { + var regExp = new RegExp('^' + char + '+|' + char + '+$', 'g'); + return string.replace(regExp, ''); + }; + + // Replace all leading chars with an empty string + var ltrim = function(string, char) { + var regExp = new RegExp('^' + char + '+', 'g'); + return string.replace(regExp, ''); + }; + + // Replace all trailing chars with an empty string + var rtrim = function(string, char) { + var regExp = new RegExp(char + '+$', 'g'); + return string.replace(regExp, ''); + }; + + var startsWith = function(string, searchString, position) { + position = position || 0; + return string.substr(position, searchString.length) === searchString; + }; + + var encodePath = function(path) { + var parts = []; + $.each(path.split('/'), function(i, part) { + parts.push(encodeURIComponent(part)); + }); + 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) { + return ''; + } + return filename.split('.').pop().toLowerCase(); + }; + + // return filename without extension + var getFilename = function(filename) { + if(filename.lastIndexOf('.') !== -1) { + return filename.substring(0, filename.lastIndexOf('.')); + } else { + return filename; + } + }; + + // return filename without extension + var getPureFilename = function(filename) { + if(filename.lastIndexOf('.') !== -1) { + filename = filename.substring(0, filename.lastIndexOf('.')); + } + + if(filename.lastIndexOf('/' !== -1)){ + filename = filename.split('/').pop(); + } + + return filename; + }; + + // return path without filename + // "/dir/to/" --> "/dir/to/" + // "/dir/to/file.txt" --> "/dir/to/" + var getDirname = function(path) { + if(path.lastIndexOf('/') !== path.length - 1) { + return path.substr(0, path.lastIndexOf('/') + 1); + } else { + return path; + } + }; + + // return parent folder for path, if folder is passed it should ends with '/' + // "/dir/to/" --> "/dir/" + // "/dir/to/file.txt" --> "/dir/" + var getParentDirname = function(path) { + return path.split('/').reverse().slice(2).reverse().join('/') + '/'; + }; + + // return closest node for path + // "/dir/to/" --> "/dir/" + // "/dir/to/file.txt" --> "/dir/to/" + var getClosestNode = function(path) { + return path.substring(0, path.slice(0, -1).lastIndexOf('/')) + '/'; + }; + + // Test if is editable file + var isEditableFile = function(filename) { + return ($.inArray(getExtension(filename), config.editor.extensions) !== -1); + }; + + // Test if is image file + var isImageFile = function(filename) { + return ($.inArray(getExtension(filename), config.viewer.image.extensions) !== -1); + }; + + // Test if file is supported web video file + var isVideoFile = function(filename) { + return ($.inArray(getExtension(filename), config.viewer.video.extensions) !== -1); + }; + + // Test if file is supported web audio file + var isAudioFile = function(filename) { + return ($.inArray(getExtension(filename), config.viewer.audio.extensions) !== -1); + }; + + // Test if file is supported by Only Office viewer + var isOnlyOfficeFile = function(filename) { + return ($.inArray(getExtension(filename), config.viewer.onlyoffice.extensions) !== -1); + }; + + // Test if file is openable in iframe + var isIFrameFile = function(filename) { + return ($.inArray(getExtension(filename), config.viewer.iframe.extensions) !== -1); + }; + + // Test if file is opendoc file + // Supported file types: http://viewerjs.org/examples/ + var isOpenDocFile = function(filename) { + return ($.inArray(getExtension(filename), config.viewer.opendoc.extensions) !== -1); + }; + + // Test if file is supported by Google Docs viewer + // Supported file types: http://stackoverflow.com/q/24325363/1789808 + var isGoogleDocsFile = function(filename) { + return ($.inArray(getExtension(filename), config.viewer.google.extensions) !== -1); + }; + + // Test if file is supported by CodeMirror renderer + var isCodeMirrorFile = function(filename) { + return ($.inArray(getExtension(filename), config.viewer.codeMirrorRenderer.extensions) !== -1); + }; + + // Test if file is supported by Markdown-it renderer, which renders .md files to HTML + var isMarkdownFile = function(filename) { + return ($.inArray(getExtension(filename), config.viewer.markdownRenderer.extensions) !== -1); + }; + + var extendRequestParams = function(method, parameters) { + var methodParams, + configParams = config.api.requestParams; + + method = method.toUpperCase(); + + if ($.isPlainObject(configParams)) { + methodParams = configParams[method]; + + if ($.isPlainObject(methodParams) && !$.isEmptyObject(methodParams)) { + var extendParams = $.extend({}, configParams['MIXED'] || {}, methodParams); + + // append params to serialized form + if (method === 'POST' && $.isArray(parameters)) { + $.each(extendParams, function(key, value) { + parameters.push({ + name: key, + value: value + }); + }); + } else { + parameters = $.extend({}, parameters, extendParams); + } + } + } + + parameters = fm.settings.callbacks.beforeSetRequestParams(method, parameters); + return parameters; + }; + + var buildAjaxRequest = function(method, parameters, dataType) { + dataType = (typeof dataType === 'undefined') ? 'json' : dataType; + + if (fm.settings.callbacks.beforeSendRequest(method, parameters) === false) { + return $.Deferred().reject(lg('NOT_ALLOWED')); + } + + return $.ajax({ + type: method, + cache: false, + url: buildConnectorUrl(), + dataType: dataType, + data: extendRequestParams(method, parameters) + }); + }; + + var getFilteredFileExtensions = function() { + if (_url_.param('filter')) { + if (config.filter[_url_.param('filter')] !== undefined) { + var shownExtensions = config.filter[_url_.param('filter')]; + } } - - // Timezone support requires "iana-tz-data" package: - // https://github.com/globalizejs/globalize/blob/master/README.md#3-iana-time-zone-data - return globalize.formatDate(date, config.formatter.datetime); - }; - - // Format server-side response single error object - var formatServerError = function(errorObject) { - var message; - // look for message in case an error CODE is provided - if (langModel.getLang() && lg(errorObject.title)) { - message = lg(errorObject.title); - $.each(errorObject.meta.arguments, function(i, argument) { - message = message.replace('%s', argument); + return shownExtensions; + }; + + var buildConnectorUrl = function(params) { + var defaults = { + time: new Date().getTime() + }; + var queryParams = $.extend({}, params || {}, defaults); + return apiConnector + '?' + $.param(queryParams); + }; + + // Build url to preview files + var createPreviewUrl = function(resourceObject, encode) { + var previewUrl, + objectPath = resourceObject.attributes.path; + + if (config.viewer.absolutePath && objectPath) { + if (encode) { + objectPath = encodePath(objectPath); + } + previewUrl = buildAbsolutePath(objectPath, false); + } else { + var queryParams = extendRequestParams('GET', { + mode: 'readfile', + path: resourceObject.id + }); + previewUrl = buildConnectorUrl(queryParams); + } + + previewUrl = fm.settings.callbacks.beforeCreatePreviewUrl(resourceObject, previewUrl); + return previewUrl; + }; + + // Build url to display image or its thumbnail + var createImageUrl = function (resourceObject, thumbnail, disableCache) { + var imageUrl; + if (isImageFile(resourceObject.id) && + resourceObject.attributes.readable && ( + (thumbnail && config.viewer.image.showThumbs) || + (!thumbnail && config.viewer.image.enabled === true) + )) { + if (config.viewer.absolutePath && !thumbnail && resourceObject.attributes.path) { + imageUrl = buildAbsolutePath(encodePath(resourceObject.attributes.path), disableCache); + } else { + var queryParams = {path: resourceObject.id}; + if (getExtension(resourceObject.id) === 'svg') { + queryParams.mode = 'readfile'; + } else { + queryParams.mode = 'getimage'; + if (thumbnail) { + queryParams.thumbnail = 'true'; + } + } + queryParams = extendRequestParams('GET', queryParams); + imageUrl = buildConnectorUrl(queryParams); + } + imageUrl = fm.settings.callbacks.beforeCreateImageUrl(resourceObject, imageUrl); + } + return imageUrl; + }; + + var buildAbsolutePath = function(path, disableCache) { + var url = (typeof config.viewer.previewUrl === 'string') ? config.viewer.previewUrl : location.origin; + url = trim(url, '/') + path; + // add timestamp-based query parameter to disable browser caching + if (disableCache) { + url += '?time=' + (new Date().getTime()); + } + return url; + }; + + var createCopyUrl = function(resourceObject) { + function encodeCopyUrl(path) { + return (config.clipboard.encodeCopyUrl) ? encodePath(path) : path; + } + + if(config.viewer.absolutePath && resourceObject.attributes.path) { + var path = encodeCopyUrl(resourceObject.attributes.path); + return buildAbsolutePath(path, false); + } else { + var path = encodeCopyUrl(resourceObject.id), + mode = (resourceObject.type === 'folder') ? 'readfolder' : 'readfile'; + return apiConnector + '?path=' + path + '&mode=' + mode; + } + }; + + // Returns container for filetree or fileinfo section based on scrollbar plugin state + var getSectionContainer = function($section) { + // if scrollbar plugin is enabled + if (config.customScrollbar.enabled) { + return $section.find('.mCSB_container'); + } else { + return $section; + } + }; + + // Handle multiple actions in loop with deferred object + var processMultipleActions = function(items, callbackFunction, finishCallback) { + var ProcessingLog = function() { + this.total = 0; + this.succeed = 0; + this.failure = 0; + this.processed = 0; + + this.getProgress = function() { + return Math.round((this.processed / this.total) * 100); + }; + + this.getProgressSucceed = function() { + return Math.round((this.succeed / this.total) * 100); + }; + + this.getProgressFailure = function() { + return Math.round((this.failure / this.total) * 100); + }; + + this.getMessage = function() { + return lg('successful_processed').replace('%s', this.succeed).replace('%s', this.total); + }; + + this.succeeded = function() { + this.succeed++; + this.processed++; + }; + + this.failed = function() { + this.failure++; + this.processed++; + }; + + this.isProcessed = function() { + return this.processed === this.total; + }; + }; + + var log, + process = new ProcessingLog(), + deferred = $.Deferred().resolve(); + + process.total = items.length; + if (process.total > 1) { + log = fm.write(process.getMessage(), { + delay: 0, + logMessageTemplate: function(message) { + var progress = process.getProgress(), + animateCss = process.isProcessed() ? 'striped' : 'striped animated'; + + return '
' + message + '
' + + '
' + + '
' + process.getProgress() + '%
' + + '
' + + '
' + + '
' + + '
' + + '
'; + } + }); + log.stick(true); + } + + $.each(items, function(i, item) { + deferred = deferred.then(function() { + return callbackFunction(i, item); + }).then(function(result) { + if(result && result.data) { + process.succeeded(); + } else { + process.failed(); + } + if (log) { + log.setMessage(process.getMessage()); + } + }); + }); + + deferred.then(function() { + if (log && process.isProcessed()) { + log.stick(false); + setTimeout(function() { + log.remove(); + }, 6000); + } }); - } else { - message = errorObject.title; - } - return message; - }; - - // Handle JSON server errors. - var handleJsonErrors = function(errors) { - fm.console('ERROR JSON', errors); - - $.each(errors, function (i, errorObject) { - fm.error(formatServerError(errorObject)); - - if (errorObject.meta.redirect) { - window.location.href = errorObject.meta.redirect; - } - }); - }; - - // Handle ajax request errors. - var handleAjaxError = function(xhr) { - var errorMessage; - - if ($.isPlainObject(xhr) && xhr.responseText) { - var isJSON = (xhr.getResponseHeader('content-type') === 'application/json'); - - // on "readfile" API request (dataType === 'text') - if (!xhr.responseJSON && isJSON) { - xhr.responseJSON = $.parseJSON(xhr.responseText); + + deferred.then(function() { + if (typeof finishCallback === 'function') { + finishCallback(); + } + }); + }; + + // Clears browser window selection + var clearSelection = function() { + if(document.selection && document.selection.empty) { + document.selection.empty(); + } else if(window.getSelection) { + var sel = window.getSelection(); + sel.removeAllRanges(); } - - if ($.isPlainObject(xhr.responseJSON) && xhr.responseJSON.errors) { - handleJsonErrors(xhr.responseJSON.errors); - } else { - errorMessage = lg('ERROR_SERVER') + ' ' + xhr.responseText; - } - } else { - // $.Deferred().reject() case e.g. - errorMessage = xhr; - } - - if (errorMessage) { - fm.console('ERROR TEXT', errorMessage); - fm.error(errorMessage); - } - }; - - // Check if capability is allowed - function hasCapability(capability) { - return capabilities.indexOf(capability) > -1; - } - - // Test if resource object has capability - function isObjectCapable(resourceObject, capability) { - if (!hasCapability(capability)) return false; - if (capability === 'select' && resourceObject.type === 'folder') return false; - if (capability === 'extract') { - var extension = getExtension(resourceObject.attributes.name); - return (resourceObject.type === 'file' && extension === 'zip'); - } - if (capability === 'download' && resourceObject.type === 'folder') { - return (config.options.allowFolderDownload === true); + }; + + // 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 + }); } - if (typeof(resourceObject.attributes.capabilities) !== 'undefined') { - return $.inArray(capability, resourceObject.attributes.capabilities) > -1 + + // Check if plugin instance created inside some context + function hasContext() { + return window.opener // window.open() + || (window.parent && window.self !== window.parent) //