diff --git a/config/filemanager.config.default.json b/config/filemanager.config.default.json index 39d588bd..d7cb6d91 100644 --- a/config/filemanager.config.default.json +++ b/config/filemanager.config.default.json @@ -56,6 +56,7 @@ "api": { "lang": "php", "connectorUrl": false, + "prevFilePath": false, "requestParams": { "GET": {}, "POST": {}, diff --git a/package.json b/package.json index fc28c933..e6a65b3a 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" @@ -30,10 +33,10 @@ "grunt-contrib-uglify": "^1.0.1", "grunt-sass": "^2.1.0", "npm-run-all": "^4.0.2", - "uglify-js": "^3.0.27" + "uglify-js": "^3.0.27", + "node-sass": "^4.9.0" }, "dependencies": { - "node-sass": "^4.9.0", "sass": "^1.0.0-beta.5.3" } -} +} \ No newline at end of file diff --git a/src/css/libs-main.css b/src/css/libs-main.css index 6c5e9da2..e9fd2b04 100644 --- a/src/css/libs-main.css +++ b/src/css/libs-main.css @@ -1,15 +1,15 @@ -html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{line-height:1;overflow-x:hidden}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}:focus{outline:0}ins{text-decoration:none}del{text-decoration:line-through}table{border-collapse:collapse;border-spacing:0}/*! - * jQuery contextMenu - Plugin for simple contextMenu handling - * - * Version: v@VERSION - * - * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) - * Web: http://swisnl.github.io/jQuery-contextMenu/ - * - * Copyright (c) 2011-@YEAR SWIS BV and contributors - * - * Licensed under - * MIT License http://www.opensource.org/licenses/mit-license - * - * Date: @DATE - */@font-face{font-family:"context-menu-icons";src:url("font/context-menu-icons.eot?2z8kr");src:url("font/context-menu-icons.eot?2z8kr#iefix") format("embedded-opentype"),url("font/context-menu-icons.woff2?2z8kr") format("woff2"),url("font/context-menu-icons.woff?2z8kr") format("woff"),url("font/context-menu-icons.ttf?2z8kr") format("truetype");font-weight:normal;font-style:normal}.context-menu-icon-add:before,.context-menu-icon-copy:before,.context-menu-icon-cut:before,.context-menu-icon-delete:before,.context-menu-icon-edit:before,.context-menu-icon-paste:before,.context-menu-icon-quit:before{color:#2980B9;font-family:"context-menu-icons";font-style:normal;font-weight:normal;font-size:16px;left:0;line-height:1;position:absolute;text-align:center;top:50%;transform:translateY(-50%);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:28px}.context-menu-icon-add:before{content:""}.context-menu-icon-add.context-menu-hover:before{color:#fff}.context-menu-icon-copy:before{content:""}.context-menu-icon-copy.context-menu-hover:before{color:#fff}.context-menu-icon-cut:before{content:""}.context-menu-icon-cut.context-menu-hover:before{color:#fff}.context-menu-icon-delete:before{content:""}.context-menu-icon-delete.context-menu-hover:before{color:#fff}.context-menu-icon-edit:before{content:""}.context-menu-icon-edit.context-menu-hover:before{color:#fff}.context-menu-icon-paste:before{content:""}.context-menu-icon-paste.context-menu-hover:before{color:#fff}.context-menu-icon-quit:before{content:""}.context-menu-icon-quit.context-menu-hover:before{color:#fff}.context-menu-list{background:#fff;border:1px solid #bebebe;border-radius:3px;box-shadow:0 2px 5px rgba(0,0,0,0.5);font-family:inherit;font-size:inherit;display:inline-block;list-style-type:none;margin:5px;max-width:360px;min-width:180px;padding:4px 0;position:absolute;white-space:pre}.context-menu-item{background-color:#fff;color:#2F2F2F;padding:3px 28px;position:relative;user-select:none}.context-menu-separator{border-bottom:1px solid #e6e5e5;margin:5px 0;padding:0}.context-menu-item>label>input,.context-menu-item>label>textarea{user-select:text}.context-menu-item.context-menu-hover{background-color:#2980B9;color:#fff;cursor:pointer}.context-menu-item.context-menu-disabled{background-color:#fff;color:#626262}.context-menu-input.context-menu-hover,.context-menu-item.context-menu-disabled.context-menu-hover{background-color:#EEE;cursor:default}.context-menu-submenu:after{content:'';border-style:solid;border-width:4px 0 4px 4px;border-color:transparent transparent transparent #2F2F2F;height:0;position:absolute;right:8px;top:50%;transform:translateY(-50%);width:0;z-index:1}.context-menu-item.context-menu-input{padding:5px 10px}.context-menu-input>label>*{vertical-align:top}.context-menu-input>label>input[type="checkbox"],.context-menu-input>label>input[type="radio"]{position:relative;top:3px}.context-menu-input>label,.context-menu-input>label>input[type="text"],.context-menu-input>label>textarea,.context-menu-input>label>select{box-sizing:border-box;display:block;width:100%}.context-menu-input>label>textarea{height:100px}.context-menu-item>.context-menu-list{display:none;right:-5px;top:5px}.context-menu-item.context-menu-visible>.context-menu-list{display:block}.context-menu-accesskey{text-decoration:underline}.mCustomScrollbar{-ms-touch-action:pinch-zoom;touch-action:pinch-zoom}.mCustomScrollbar.mCS_no_scrollbar,.mCustomScrollbar.mCS_touch_action{-ms-touch-action:auto;touch-action:auto}.mCustomScrollBox{position:relative;overflow:hidden;height:100%;max-width:100%;outline:none;direction:ltr}.mCSB_container{overflow:hidden;width:auto;height:auto}.mCSB_inside>.mCSB_container{margin-right:30px}.mCSB_container.mCS_no_scrollbar_y.mCS_y_hidden{margin-right:0}.mCS-dir-rtl>.mCSB_inside>.mCSB_container{margin-right:0;margin-left:30px}.mCS-dir-rtl>.mCSB_inside>.mCSB_container.mCS_no_scrollbar_y.mCS_y_hidden{margin-left:0}.mCSB_scrollTools{position:absolute;width:16px;height:auto;left:auto;top:0;right:0;bottom:0}.mCSB_outside+.mCSB_scrollTools{right:-26px}.mCS-dir-rtl>.mCSB_inside>.mCSB_scrollTools,.mCS-dir-rtl>.mCSB_outside+.mCSB_scrollTools{right:auto;left:0}.mCS-dir-rtl>.mCSB_outside+.mCSB_scrollTools{left:-26px}.mCSB_scrollTools .mCSB_draggerContainer{position:absolute;top:0;left:0;bottom:0;right:0;height:auto}.mCSB_scrollTools a+.mCSB_draggerContainer{margin:20px 0}.mCSB_scrollTools .mCSB_draggerRail{width:2px;height:100%;margin:0 auto;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}.mCSB_scrollTools .mCSB_dragger{cursor:pointer;width:100%;height:30px;z-index:1}.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{position:relative;width:4px;height:100%;margin:0 auto;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px;text-align:center}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{width:12px}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:8px}.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_buttonDown{display:block;position:absolute;height:20px;width:100%;overflow:hidden;margin:0 auto;cursor:pointer}.mCSB_scrollTools .mCSB_buttonDown{bottom:0}.mCSB_horizontal.mCSB_inside>.mCSB_container{margin-right:0;margin-bottom:30px}.mCSB_horizontal.mCSB_outside>.mCSB_container{min-height:100%}.mCSB_horizontal>.mCSB_container.mCS_no_scrollbar_x.mCS_x_hidden{margin-bottom:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal{width:auto;height:16px;top:auto;right:0;bottom:0;left:0}.mCustomScrollBox+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal{bottom:-26px}.mCSB_scrollTools.mCSB_scrollTools_horizontal a+.mCSB_draggerContainer{margin:0 20px}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:2px;margin:7px 0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_dragger{width:30px;height:100%;left:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:4px;margin:6px auto}.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{height:12px;margin:2px auto}.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:8px;margin:4px 0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonLeft,.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonRight{display:block;position:absolute;width:20px;height:100%;overflow:hidden;margin:0 auto;cursor:pointer}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonLeft{left:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonRight{right:0}.mCSB_container_wrapper{position:absolute;height:auto;width:auto;overflow:hidden;top:0;left:0;right:0;bottom:0;margin-right:30px;margin-bottom:30px}.mCSB_container_wrapper>.mCSB_container{padding-right:30px;padding-bottom:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.mCSB_vertical_horizontal>.mCSB_scrollTools.mCSB_scrollTools_vertical{bottom:20px}.mCSB_vertical_horizontal>.mCSB_scrollTools.mCSB_scrollTools_horizontal{right:20px}.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden+.mCSB_scrollTools.mCSB_scrollTools_vertical{bottom:0}.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden+.mCSB_scrollTools ~ .mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_scrollTools.mCSB_scrollTools_horizontal{right:0}.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_scrollTools.mCSB_scrollTools_horizontal{left:20px}.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden+.mCSB_scrollTools ~ .mCSB_scrollTools.mCSB_scrollTools_horizontal{left:0}.mCS-dir-rtl>.mCSB_inside>.mCSB_container_wrapper{margin-right:0;margin-left:30px}.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden>.mCSB_container{padding-right:0}.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden>.mCSB_container{padding-bottom:0}.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden{margin-right:0;margin-left:0}.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden{margin-bottom:0}.mCSB_scrollTools,.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_buttonDown,.mCSB_scrollTools .mCSB_buttonLeft,.mCSB_scrollTools .mCSB_buttonRight{-webkit-transition:opacity .2s ease-in-out, background-color .2s ease-in-out;-moz-transition:opacity .2s ease-in-out, background-color .2s ease-in-out;-o-transition:opacity .2s ease-in-out, background-color .2s ease-in-out;transition:opacity .2s ease-in-out, background-color .2s ease-in-out}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger_bar,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerRail,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger_bar,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerRail{-webkit-transition:width .2s ease-out .2s, height .2s ease-out .2s, margin-left .2s ease-out .2s, margin-right .2s ease-out .2s, margin-top .2s ease-out .2s, margin-bottom .2s ease-out .2s, opacity .2s ease-in-out, background-color .2s ease-in-out;-moz-transition:width .2s ease-out .2s, height .2s ease-out .2s, margin-left .2s ease-out .2s, margin-right .2s ease-out .2s, margin-top .2s ease-out .2s, margin-bottom .2s ease-out .2s, opacity .2s ease-in-out, background-color .2s ease-in-out;-o-transition:width .2s ease-out .2s, height .2s ease-out .2s, margin-left .2s ease-out .2s, margin-right .2s ease-out .2s, margin-top .2s ease-out .2s, margin-bottom .2s ease-out .2s, opacity .2s ease-in-out, background-color .2s ease-in-out;transition:width .2s ease-out .2s, height .2s ease-out .2s, margin-left .2s ease-out .2s, margin-right .2s ease-out .2s, margin-top .2s ease-out .2s, margin-bottom .2s ease-out .2s, opacity .2s ease-in-out, background-color .2s ease-in-out}.mCSB_scrollTools{opacity:0.75;filter:"alpha(opacity=75)";-ms-filter:"alpha(opacity=75)"}.mCS-autoHide>.mCustomScrollBox>.mCSB_scrollTools,.mCS-autoHide>.mCustomScrollBox ~ .mCSB_scrollTools{opacity:0;filter:"alpha(opacity=0)";-ms-filter:"alpha(opacity=0)"}.mCustomScrollbar>.mCustomScrollBox>.mCSB_scrollTools.mCSB_scrollTools_onDrag,.mCustomScrollbar>.mCustomScrollBox ~ .mCSB_scrollTools.mCSB_scrollTools_onDrag,.mCustomScrollBox:hover>.mCSB_scrollTools,.mCustomScrollBox:hover ~ .mCSB_scrollTools,.mCS-autoHide:hover>.mCustomScrollBox>.mCSB_scrollTools,.mCS-autoHide:hover>.mCustomScrollBox ~ .mCSB_scrollTools{opacity:1;filter:"alpha(opacity=100)";-ms-filter:"alpha(opacity=100)"}.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.4);filter:"alpha(opacity=40)";-ms-filter:"alpha(opacity=40)"}.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.75);filter:"alpha(opacity=75)";-ms-filter:"alpha(opacity=75)"}.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85);filter:"alpha(opacity=85)";-ms-filter:"alpha(opacity=85)"}.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9);filter:"alpha(opacity=90)";-ms-filter:"alpha(opacity=90)"}.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_buttonDown,.mCSB_scrollTools .mCSB_buttonLeft,.mCSB_scrollTools .mCSB_buttonRight{background-image:url(mCSB_buttons.png);background-repeat:no-repeat;opacity:0.4;filter:"alpha(opacity=40)";-ms-filter:"alpha(opacity=40)"}.mCSB_scrollTools .mCSB_buttonUp{background-position:0 0}.mCSB_scrollTools .mCSB_buttonDown{background-position:0 -20px}.mCSB_scrollTools .mCSB_buttonLeft{background-position:0 -40px}.mCSB_scrollTools .mCSB_buttonRight{background-position:0 -56px}.mCSB_scrollTools .mCSB_buttonUp:hover,.mCSB_scrollTools .mCSB_buttonDown:hover,.mCSB_scrollTools .mCSB_buttonLeft:hover,.mCSB_scrollTools .mCSB_buttonRight:hover{opacity:0.75;filter:"alpha(opacity=75)";-ms-filter:"alpha(opacity=75)"}.mCSB_scrollTools .mCSB_buttonUp:active,.mCSB_scrollTools .mCSB_buttonDown:active,.mCSB_scrollTools .mCSB_buttonLeft:active,.mCSB_scrollTools .mCSB_buttonRight:active{opacity:0.9;filter:"alpha(opacity=90)";-ms-filter:"alpha(opacity=90)"}.mCS-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.15)}.mCS-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:rgba(0,0,0,0.85)}.mCS-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:rgba(0,0,0,0.9)}.mCS-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px 0}.mCS-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -20px}.mCS-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -40px}.mCS-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -56px}.mCS-light-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-dark-2.mCSB_scrollTools .mCSB_draggerRail{width:4px;background-color:#fff;background-color:rgba(255,255,255,0.1);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.mCS-light-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:4px;background-color:#fff;background-color:rgba(255,255,255,0.75);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.mCS-light-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-light-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:4px;margin:6px auto}.mCS-light-2.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85)}.mCS-light-2.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-light-2.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9)}.mCS-light-2.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px 0}.mCS-light-2.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -20px}.mCS-light-2.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -40px}.mCS-light-2.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -56px}.mCS-dark-2.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px 0}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -20px}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -40px}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -56px}.mCS-light-thick.mCSB_scrollTools .mCSB_draggerRail,.mCS-dark-thick.mCSB_scrollTools .mCSB_draggerRail{width:4px;background-color:#fff;background-color:rgba(255,255,255,0.1);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px;background-color:#fff;background-color:rgba(255,255,255,0.75);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mCS-light-thick.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-thick.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:4px;margin:6px 0}.mCS-light-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:6px;margin:5px auto}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85)}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-light-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9)}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-16px 0}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-16px -20px}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-20px -40px}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-20px -56px}.mCS-dark-thick.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-96px 0}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-96px -20px}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-100px -40px}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-100px -56px}.mCS-light-thin.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,0.1)}.mCS-light-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:2px}.mCS-light-thin.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-thin.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%}.mCS-light-thin.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:2px;margin:7px auto}.mCS-dark-thin.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.15)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px 0}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -20px}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -40px}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -56px}.mCS-rounded.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,0.15)}.mCS-rounded.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dots.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger{height:14px}.mCS-rounded.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:14px;margin:0 1px}.mCS-rounded.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dark.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_dragger{width:14px}.mCS-rounded.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{height:14px;margin:1px 0}.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{width:16px;height:16px;margin:-1px 0}.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:4px}.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{height:16px;width:16px;margin:0 -1px}.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:4px;margin:6px 0}.mCS-rounded.mCSB_scrollTools .mCSB_buttonUp{background-position:0 -72px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonDown{background-position:0 -92px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonLeft{background-position:0 -112px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonRight{background-position:0 -128px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.15)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px -72px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -92px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -112px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -128px}.mCS-rounded-dots.mCSB_scrollTools_vertical .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_vertical .mCSB_draggerRail{width:4px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{background-color:transparent;background-position:center}.mCS-rounded-dots.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAANElEQVQYV2NkIAAYiVbw//9/Y6DiM1ANJoyMjGdBbLgJQAX/kU0DKgDLkaQAvxW4HEvQFwCRcxIJK1XznAAAAABJRU5ErkJggg==");background-repeat:repeat-y;opacity:0.3;filter:"alpha(opacity=30)";-ms-filter:"alpha(opacity=30)"}.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{height:4px;margin:6px 0;background-repeat:repeat-x}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonUp{background-position:-16px -72px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonDown{background-position:-16px -92px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonLeft{background-position:-20px -112px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonRight{background-position:-20px -128px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAALElEQVQYV2NkIAAYSVFgDFR8BqrBBEifBbGRTfiPZhpYjiQFBK3A6l6CvgAAE9kGCd1mvgEAAAAASUVORK5CYII=")}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-96px -72px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-96px -92px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-100px -112px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-100px -128px}.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-repeat:repeat-y;background-image:-moz-linear-gradient(left, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:-webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255,255,255,0.5)), color-stop(100%, rgba(255,255,255,0)));background-image:-webkit-linear-gradient(left, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:-o-linear-gradient(left, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:-ms-linear-gradient(left, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:linear-gradient(to right, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%)}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{background-repeat:repeat-x;background-image:-moz-linear-gradient(top, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(255,255,255,0.5)), color-stop(100%, rgba(255,255,255,0)));background-image:-webkit-linear-gradient(top, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:-o-linear-gradient(top, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:-ms-linear-gradient(top, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:linear-gradient(to bottom, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%)}.mCS-3d.mCSB_scrollTools_vertical .mCSB_dragger,.mCS-3d-dark.mCSB_scrollTools_vertical .mCSB_dragger{height:70px}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger{width:70px}.mCS-3d.mCSB_scrollTools,.mCS-3d-dark.mCSB_scrollTools{opacity:1;filter:"alpha(opacity=30)";-ms-filter:"alpha(opacity=30)"}.mCS-3d.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}.mCS-3d.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail{width:8px;background-color:#000;background-color:rgba(0,0,0,0.2);box-shadow:inset 1px 0 1px rgba(0,0,0,0.5),inset -1px 0 1px rgba(255,255,255,0.2)}.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#555}.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:8px}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:8px;margin:4px 0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.5),inset 0 -1px 1px rgba(255,255,255,0.2)}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:8px;margin:4px auto}.mCS-3d.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-3d.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-3d.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-3d.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1);box-shadow:inset 1px 0 1px rgba(0,0,0,0.1)}.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{box-shadow:inset 0 1px 1px rgba(0,0,0,0.1)}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-3d-thick.mCSB_scrollTools,.mCS-3d-thick-dark.mCSB_scrollTools{opacity:1;filter:"alpha(opacity=30)";-ms-filter:"alpha(opacity=30)"}.mCS-3d-thick.mCSB_scrollTools,.mCS-3d-thick-dark.mCSB_scrollTools,.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerContainer,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerContainer{-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.mCSB_inside+.mCS-3d-thick.mCSB_scrollTools_vertical,.mCSB_inside+.mCS-3d-thick-dark.mCSB_scrollTools_vertical{right:1px}.mCS-3d-thick.mCSB_scrollTools_vertical,.mCS-3d-thick-dark.mCSB_scrollTools_vertical{box-shadow:inset 1px 0 1px rgba(0,0,0,0.1),inset 0 0 14px rgba(0,0,0,0.5)}.mCS-3d-thick.mCSB_scrollTools_horizontal,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal{bottom:1px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.1),inset 0 0 14px rgba(0,0,0,0.5)}.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 1px 0 0 rgba(255,255,255,0.4);width:12px;margin:2px;position:absolute;height:auto;top:0;bottom:0;left:0;right:0}.mCS-3d-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 0 1px 0 rgba(255,255,255,0.4)}.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#555}.mCS-3d-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{height:12px;width:auto}.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerContainer{background-color:#000;background-color:rgba(0,0,0,0.05);box-shadow:inset 1px 1px 16px rgba(0,0,0,0.1)}.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-3d-thick-dark.mCSB_scrollTools{box-shadow:inset 0 0 14px rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools_horizontal{box-shadow:inset 0 1px 1px rgba(0,0,0,0.1),inset 0 0 14px rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 1px 0 0 rgba(255,255,255,0.4),inset -1px 0 0 rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 0 1px 0 rgba(255,255,255,0.4),inset 0 -1px 0 rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#777}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerContainer{background-color:#fff;background-color:rgba(0,0,0,0.05);box-shadow:inset 1px 1px 16px rgba(0,0,0,0.1)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCSB_outside+.mCS-minimal.mCSB_scrollTools_vertical,.mCSB_outside+.mCS-minimal-dark.mCSB_scrollTools_vertical{right:0;margin:12px 0}.mCustomScrollBox.mCS-minimal+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal-dark+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal-dark+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal{bottom:0;margin:0 12px}.mCS-dir-rtl>.mCSB_outside+.mCS-minimal.mCSB_scrollTools_vertical,.mCS-dir-rtl>.mCSB_outside+.mCS-minimal-dark.mCSB_scrollTools_vertical{left:0;right:auto}.mCS-minimal.mCSB_scrollTools .mCSB_draggerRail,.mCS-minimal-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-minimal.mCSB_scrollTools_vertical .mCSB_dragger,.mCS-minimal-dark.mCSB_scrollTools_vertical .mCSB_dragger{height:50px}.mCS-minimal.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-minimal-dark.mCSB_scrollTools_horizontal .mCSB_dragger{width:50px}.mCS-minimal.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.2);filter:"alpha(opacity=20)";-ms-filter:"alpha(opacity=20)"}.mCS-minimal.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-minimal.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.5);filter:"alpha(opacity=50)";-ms-filter:"alpha(opacity=50)"}.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.2);filter:"alpha(opacity=20)";-ms-filter:"alpha(opacity=20)"}.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.5);filter:"alpha(opacity=50)";-ms-filter:"alpha(opacity=50)"}.mCS-light-3.mCSB_scrollTools .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools .mCSB_draggerRail{width:6px;background-color:#000;background-color:rgba(0,0,0,0.2)}.mCS-light-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px}.mCS-light-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-light-3.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:6px;margin:5px 0}.mCS-light-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:12px}.mCS-light-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:12px;margin:2px 0}.mCS-light-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-3.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1)}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-inset.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{width:12px;background-color:#000;background-color:rgba(0,0,0,0.2)}.mCS-inset.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px;margin:3px 5px;position:absolute;height:auto;top:0;bottom:0;left:0;right:0}.mCS-inset.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{height:6px;margin:5px 3px;position:absolute;width:auto;top:0;bottom:0;left:0;right:0}.mCS-inset.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-3.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:12px;margin:2px 0}.mCS-inset.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-inset.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-inset.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-inset.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-inset-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1)}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-inset-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent;border-width:1px;border-style:solid;border-color:#fff;border-color:rgba(255,255,255,0.2);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail{border-color:#000;border-color:rgba(0,0,0,0.2)}.mCS-inset-3.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,0.6)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.6)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.75)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9)}.alertify{position:fixed;background-color:rgba(0,0,0,0.3);left:0;right:0;top:0;bottom:0;width:100%;height:100%;z-index:99999}.alertify.hide{opacity:0;pointer-events:none}.alertify,.alertify.show{box-sizing:border-box;transition:all 0.33s cubic-bezier(0.25, 0.8, 0.25, 1)}.alertify,.alertify *{box-sizing:border-box}.alertify .dialog,.alertify .alert{width:100%;padding:0;margin:0 auto;position:relative;top:50%}.alertify .dialog>*,.alertify .alert>*{width:400px;max-width:95%;margin:0 auto;text-align:left;padding:18px;background:#fff;box-shadow:0 2px 4px -1px rgba(0,0,0,0.14),0 4px 5px 0 rgba(0,0,0,0.098),0 1px 10px 0 rgba(0,0,0,0.084)}.alertify .dialog [data-alertify-msg],.alertify .alert [data-alertify-msg]{padding-bottom:12px;text-align:left}.alertify .dialog input[data-alertify-input]:not(.form-control),.alertify .alert input[data-alertify-input]:not(.form-control){width:100%;font-size:100%;padding:8px}.alertify .dialog input[data-alertify-input]:not(.form-control):focus,.alertify .alert input[data-alertify-input]:not(.form-control):focus{outline-offset:-2px}.alertify .dialog [data-alertify-btn-holder],.alertify .alert [data-alertify-btn-holder]{text-align:right;margin-top:12px}.alertify .dialog [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button),.alertify .alert [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button){background:transparent;box-sizing:border-box;color:rgba(0,0,0,0.87);position:relative;outline:0;display:inline-block;align-items:center;padding:0 6px;margin-left:15px;line-height:36px;min-height:36px;white-space:nowrap;min-width:88px;text-align:center;text-transform:uppercase;font-size:14px;text-decoration:none;cursor:pointer;border:1px solid #DBDBDB;border-radius:2px}.alertify .dialog [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):hover,.alertify .dialog [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):active,.alertify .alert [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):hover,.alertify .alert [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):active{background-color:rgba(0,0,0,0.05)}.alertify .dialog [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):focus,.alertify .alert [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):focus{border:1px solid rgba(0,0,0,0.1)}.alertify .dialog [data-alertify-btn-holder] [data-alertify-btn].btn,.alertify .alert [data-alertify-btn-holder] [data-alertify-btn].btn{margin:6px 4px}.alertify-logs{position:fixed;z-index:100000}.alertify-logs.bottom,.alertify-logs:not(.top){bottom:16px}.alertify-logs.left,.alertify-logs:not(.right){left:16px}.alertify-logs.left>*,.alertify-logs:not(.right)>*{float:left;transform:translate3d(0, 0, 0);height:auto}.alertify-logs.left>*.show,.alertify-logs:not(.right)>*.show{left:0}.alertify-logs.left>*,.alertify-logs.left>*.hide,.alertify-logs:not(.right)>*,.alertify-logs:not(.right)>*.hide{left:-110%}.alertify-logs.right{right:16px}.alertify-logs.right>*{float:right;transform:translate3d(0, 0, 0)}.alertify-logs.right>*.show{right:0;opacity:1}.alertify-logs.right>*,.alertify-logs.right>*.hide{right:-110%;opacity:0}.alertify-logs.top{top:0}.alertify-logs>*{box-sizing:border-box;transition:all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);position:relative;clear:both;backface-visibility:hidden;perspective:1000}.alertify-logs>*{max-height:0;margin:0;padding:0;overflow:hidden;opacity:0;pointer-events:none}.alertify-logs>*.show{margin-top:12px;opacity:1;max-height:1000px;pointer-events:auto}.alertify-logs [data-alertify-log-msg]{padding:12px 24px;color:#fff;box-shadow:0 2px 5px 0 rgba(0,0,0,0.2);border-radius:1px;transition:.2s all;display:block !important}.alertify-logs [data-alertify-log-msg],.alertify-logs [data-alertify-log-msg].default{color:#fff;background:rgba(0,0,0,0.8)}.alertify-logs [data-alertify-log-msg].success{color:#fff;background:rgba(76,175,80,0.9)}.alertify-logs [data-alertify-log-msg].warning{color:#fff;background:rgba(255,125,25,0.9)}.alertify-logs [data-alertify-log-msg].error{color:#fff;background:rgba(244,67,54,0.9)} +html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{line-height:1;overflow-x:hidden}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}:focus{outline:0}ins{text-decoration:none}del{text-decoration:line-through}table{border-collapse:collapse;border-spacing:0}/*! + * jQuery contextMenu - Plugin for simple contextMenu handling + * + * Version: v@VERSION + * + * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) + * Web: http://swisnl.github.io/jQuery-contextMenu/ + * + * Copyright (c) 2011-@YEAR SWIS BV and contributors + * + * Licensed under + * MIT License http://www.opensource.org/licenses/mit-license + * + * Date: @DATE + */@font-face{font-family:"context-menu-icons";src:url("font/context-menu-icons.eot?2z8kr");src:url("font/context-menu-icons.eot?2z8kr#iefix") format("embedded-opentype"),url("font/context-menu-icons.woff2?2z8kr") format("woff2"),url("font/context-menu-icons.woff?2z8kr") format("woff"),url("font/context-menu-icons.ttf?2z8kr") format("truetype");font-weight:normal;font-style:normal}.context-menu-icon-add:before,.context-menu-icon-copy:before,.context-menu-icon-cut:before,.context-menu-icon-delete:before,.context-menu-icon-edit:before,.context-menu-icon-paste:before,.context-menu-icon-quit:before{color:#2980B9;font-family:"context-menu-icons";font-style:normal;font-weight:normal;font-size:16px;left:0;line-height:1;position:absolute;text-align:center;top:50%;transform:translateY(-50%);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:28px}.context-menu-icon-add:before{content:""}.context-menu-icon-add.context-menu-hover:before{color:#fff}.context-menu-icon-copy:before{content:""}.context-menu-icon-copy.context-menu-hover:before{color:#fff}.context-menu-icon-cut:before{content:""}.context-menu-icon-cut.context-menu-hover:before{color:#fff}.context-menu-icon-delete:before{content:""}.context-menu-icon-delete.context-menu-hover:before{color:#fff}.context-menu-icon-edit:before{content:""}.context-menu-icon-edit.context-menu-hover:before{color:#fff}.context-menu-icon-paste:before{content:""}.context-menu-icon-paste.context-menu-hover:before{color:#fff}.context-menu-icon-quit:before{content:""}.context-menu-icon-quit.context-menu-hover:before{color:#fff}.context-menu-list{background:#fff;border:1px solid #bebebe;border-radius:3px;box-shadow:0 2px 5px rgba(0,0,0,0.5);font-family:inherit;font-size:inherit;display:inline-block;list-style-type:none;margin:5px;max-width:360px;min-width:180px;padding:4px 0;position:absolute;white-space:pre}.context-menu-item{background-color:#fff;color:#2F2F2F;padding:3px 28px;position:relative;user-select:none}.context-menu-separator{border-bottom:1px solid #e6e5e5;margin:5px 0;padding:0}.context-menu-item>label>input,.context-menu-item>label>textarea{user-select:text}.context-menu-item.context-menu-hover{background-color:#2980B9;color:#fff;cursor:pointer}.context-menu-item.context-menu-disabled{background-color:#fff;color:#626262}.context-menu-input.context-menu-hover,.context-menu-item.context-menu-disabled.context-menu-hover{background-color:#EEE;cursor:default}.context-menu-submenu:after{content:'';border-style:solid;border-width:4px 0 4px 4px;border-color:transparent transparent transparent #2F2F2F;height:0;position:absolute;right:8px;top:50%;transform:translateY(-50%);width:0;z-index:1}.context-menu-item.context-menu-input{padding:5px 10px}.context-menu-input>label>*{vertical-align:top}.context-menu-input>label>input[type="checkbox"],.context-menu-input>label>input[type="radio"]{position:relative;top:3px}.context-menu-input>label,.context-menu-input>label>input[type="text"],.context-menu-input>label>textarea,.context-menu-input>label>select{box-sizing:border-box;display:block;width:100%}.context-menu-input>label>textarea{height:100px}.context-menu-item>.context-menu-list{display:none;right:-5px;top:5px}.context-menu-item.context-menu-visible>.context-menu-list{display:block}.context-menu-accesskey{text-decoration:underline}.mCustomScrollbar{-ms-touch-action:pinch-zoom;touch-action:pinch-zoom}.mCustomScrollbar.mCS_no_scrollbar,.mCustomScrollbar.mCS_touch_action{-ms-touch-action:auto;touch-action:auto}.mCustomScrollBox{position:relative;overflow:hidden;height:100%;max-width:100%;outline:none;direction:ltr}.mCSB_container{overflow:hidden;width:auto;height:auto}.mCSB_inside>.mCSB_container{margin-right:30px}.mCSB_container.mCS_no_scrollbar_y.mCS_y_hidden{margin-right:0}.mCS-dir-rtl>.mCSB_inside>.mCSB_container{margin-right:0;margin-left:30px}.mCS-dir-rtl>.mCSB_inside>.mCSB_container.mCS_no_scrollbar_y.mCS_y_hidden{margin-left:0}.mCSB_scrollTools{position:absolute;width:16px;height:auto;left:auto;top:0;right:0;bottom:0}.mCSB_outside+.mCSB_scrollTools{right:-26px}.mCS-dir-rtl>.mCSB_inside>.mCSB_scrollTools,.mCS-dir-rtl>.mCSB_outside+.mCSB_scrollTools{right:auto;left:0}.mCS-dir-rtl>.mCSB_outside+.mCSB_scrollTools{left:-26px}.mCSB_scrollTools .mCSB_draggerContainer{position:absolute;top:0;left:0;bottom:0;right:0;height:auto}.mCSB_scrollTools a+.mCSB_draggerContainer{margin:20px 0}.mCSB_scrollTools .mCSB_draggerRail{width:2px;height:100%;margin:0 auto;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}.mCSB_scrollTools .mCSB_dragger{cursor:pointer;width:100%;height:30px;z-index:1}.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{position:relative;width:4px;height:100%;margin:0 auto;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px;text-align:center}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{width:12px}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:8px}.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_buttonDown{display:block;position:absolute;height:20px;width:100%;overflow:hidden;margin:0 auto;cursor:pointer}.mCSB_scrollTools .mCSB_buttonDown{bottom:0}.mCSB_horizontal.mCSB_inside>.mCSB_container{margin-right:0;margin-bottom:30px}.mCSB_horizontal.mCSB_outside>.mCSB_container{min-height:100%}.mCSB_horizontal>.mCSB_container.mCS_no_scrollbar_x.mCS_x_hidden{margin-bottom:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal{width:auto;height:16px;top:auto;right:0;bottom:0;left:0}.mCustomScrollBox+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal{bottom:-26px}.mCSB_scrollTools.mCSB_scrollTools_horizontal a+.mCSB_draggerContainer{margin:0 20px}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:2px;margin:7px 0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_dragger{width:30px;height:100%;left:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:4px;margin:6px auto}.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{height:12px;margin:2px auto}.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:8px;margin:4px 0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonLeft,.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonRight{display:block;position:absolute;width:20px;height:100%;overflow:hidden;margin:0 auto;cursor:pointer}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonLeft{left:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonRight{right:0}.mCSB_container_wrapper{position:absolute;height:auto;width:auto;overflow:hidden;top:0;left:0;right:0;bottom:0;margin-right:30px;margin-bottom:30px}.mCSB_container_wrapper>.mCSB_container{padding-right:30px;padding-bottom:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.mCSB_vertical_horizontal>.mCSB_scrollTools.mCSB_scrollTools_vertical{bottom:20px}.mCSB_vertical_horizontal>.mCSB_scrollTools.mCSB_scrollTools_horizontal{right:20px}.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden+.mCSB_scrollTools.mCSB_scrollTools_vertical{bottom:0}.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden+.mCSB_scrollTools ~ .mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_scrollTools.mCSB_scrollTools_horizontal{right:0}.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_scrollTools.mCSB_scrollTools_horizontal{left:20px}.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden+.mCSB_scrollTools ~ .mCSB_scrollTools.mCSB_scrollTools_horizontal{left:0}.mCS-dir-rtl>.mCSB_inside>.mCSB_container_wrapper{margin-right:0;margin-left:30px}.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden>.mCSB_container{padding-right:0}.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden>.mCSB_container{padding-bottom:0}.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden{margin-right:0;margin-left:0}.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden{margin-bottom:0}.mCSB_scrollTools,.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_buttonDown,.mCSB_scrollTools .mCSB_buttonLeft,.mCSB_scrollTools .mCSB_buttonRight{-webkit-transition:opacity .2s ease-in-out, background-color .2s ease-in-out;-moz-transition:opacity .2s ease-in-out, background-color .2s ease-in-out;-o-transition:opacity .2s ease-in-out, background-color .2s ease-in-out;transition:opacity .2s ease-in-out, background-color .2s ease-in-out}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger_bar,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerRail,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger_bar,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerRail{-webkit-transition:width .2s ease-out .2s, height .2s ease-out .2s, margin-left .2s ease-out .2s, margin-right .2s ease-out .2s, margin-top .2s ease-out .2s, margin-bottom .2s ease-out .2s, opacity .2s ease-in-out, background-color .2s ease-in-out;-moz-transition:width .2s ease-out .2s, height .2s ease-out .2s, margin-left .2s ease-out .2s, margin-right .2s ease-out .2s, margin-top .2s ease-out .2s, margin-bottom .2s ease-out .2s, opacity .2s ease-in-out, background-color .2s ease-in-out;-o-transition:width .2s ease-out .2s, height .2s ease-out .2s, margin-left .2s ease-out .2s, margin-right .2s ease-out .2s, margin-top .2s ease-out .2s, margin-bottom .2s ease-out .2s, opacity .2s ease-in-out, background-color .2s ease-in-out;transition:width .2s ease-out .2s, height .2s ease-out .2s, margin-left .2s ease-out .2s, margin-right .2s ease-out .2s, margin-top .2s ease-out .2s, margin-bottom .2s ease-out .2s, opacity .2s ease-in-out, background-color .2s ease-in-out}.mCSB_scrollTools{opacity:0.75;filter:"alpha(opacity=75)";-ms-filter:"alpha(opacity=75)"}.mCS-autoHide>.mCustomScrollBox>.mCSB_scrollTools,.mCS-autoHide>.mCustomScrollBox ~ .mCSB_scrollTools{opacity:0;filter:"alpha(opacity=0)";-ms-filter:"alpha(opacity=0)"}.mCustomScrollbar>.mCustomScrollBox>.mCSB_scrollTools.mCSB_scrollTools_onDrag,.mCustomScrollbar>.mCustomScrollBox ~ .mCSB_scrollTools.mCSB_scrollTools_onDrag,.mCustomScrollBox:hover>.mCSB_scrollTools,.mCustomScrollBox:hover ~ .mCSB_scrollTools,.mCS-autoHide:hover>.mCustomScrollBox>.mCSB_scrollTools,.mCS-autoHide:hover>.mCustomScrollBox ~ .mCSB_scrollTools{opacity:1;filter:"alpha(opacity=100)";-ms-filter:"alpha(opacity=100)"}.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.4);filter:"alpha(opacity=40)";-ms-filter:"alpha(opacity=40)"}.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.75);filter:"alpha(opacity=75)";-ms-filter:"alpha(opacity=75)"}.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85);filter:"alpha(opacity=85)";-ms-filter:"alpha(opacity=85)"}.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9);filter:"alpha(opacity=90)";-ms-filter:"alpha(opacity=90)"}.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_buttonDown,.mCSB_scrollTools .mCSB_buttonLeft,.mCSB_scrollTools .mCSB_buttonRight{background-image:url(mCSB_buttons.png);background-repeat:no-repeat;opacity:0.4;filter:"alpha(opacity=40)";-ms-filter:"alpha(opacity=40)"}.mCSB_scrollTools .mCSB_buttonUp{background-position:0 0}.mCSB_scrollTools .mCSB_buttonDown{background-position:0 -20px}.mCSB_scrollTools .mCSB_buttonLeft{background-position:0 -40px}.mCSB_scrollTools .mCSB_buttonRight{background-position:0 -56px}.mCSB_scrollTools .mCSB_buttonUp:hover,.mCSB_scrollTools .mCSB_buttonDown:hover,.mCSB_scrollTools .mCSB_buttonLeft:hover,.mCSB_scrollTools .mCSB_buttonRight:hover{opacity:0.75;filter:"alpha(opacity=75)";-ms-filter:"alpha(opacity=75)"}.mCSB_scrollTools .mCSB_buttonUp:active,.mCSB_scrollTools .mCSB_buttonDown:active,.mCSB_scrollTools .mCSB_buttonLeft:active,.mCSB_scrollTools .mCSB_buttonRight:active{opacity:0.9;filter:"alpha(opacity=90)";-ms-filter:"alpha(opacity=90)"}.mCS-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.15)}.mCS-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:rgba(0,0,0,0.85)}.mCS-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:rgba(0,0,0,0.9)}.mCS-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px 0}.mCS-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -20px}.mCS-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -40px}.mCS-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -56px}.mCS-light-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-dark-2.mCSB_scrollTools .mCSB_draggerRail{width:4px;background-color:#fff;background-color:rgba(255,255,255,0.1);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.mCS-light-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:4px;background-color:#fff;background-color:rgba(255,255,255,0.75);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.mCS-light-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-light-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:4px;margin:6px auto}.mCS-light-2.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85)}.mCS-light-2.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-light-2.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9)}.mCS-light-2.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px 0}.mCS-light-2.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -20px}.mCS-light-2.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -40px}.mCS-light-2.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -56px}.mCS-dark-2.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px 0}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -20px}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -40px}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -56px}.mCS-light-thick.mCSB_scrollTools .mCSB_draggerRail,.mCS-dark-thick.mCSB_scrollTools .mCSB_draggerRail{width:4px;background-color:#fff;background-color:rgba(255,255,255,0.1);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px;background-color:#fff;background-color:rgba(255,255,255,0.75);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mCS-light-thick.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-thick.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:4px;margin:6px 0}.mCS-light-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:6px;margin:5px auto}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85)}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-light-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9)}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-16px 0}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-16px -20px}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-20px -40px}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-20px -56px}.mCS-dark-thick.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-96px 0}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-96px -20px}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-100px -40px}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-100px -56px}.mCS-light-thin.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,0.1)}.mCS-light-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:2px}.mCS-light-thin.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-thin.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%}.mCS-light-thin.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:2px;margin:7px auto}.mCS-dark-thin.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.15)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px 0}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -20px}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -40px}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -56px}.mCS-rounded.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,0.15)}.mCS-rounded.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dots.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger{height:14px}.mCS-rounded.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:14px;margin:0 1px}.mCS-rounded.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dark.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_dragger{width:14px}.mCS-rounded.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{height:14px;margin:1px 0}.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{width:16px;height:16px;margin:-1px 0}.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:4px}.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{height:16px;width:16px;margin:0 -1px}.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:4px;margin:6px 0}.mCS-rounded.mCSB_scrollTools .mCSB_buttonUp{background-position:0 -72px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonDown{background-position:0 -92px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonLeft{background-position:0 -112px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonRight{background-position:0 -128px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.15)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px -72px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -92px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -112px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -128px}.mCS-rounded-dots.mCSB_scrollTools_vertical .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_vertical .mCSB_draggerRail{width:4px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{background-color:transparent;background-position:center}.mCS-rounded-dots.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAANElEQVQYV2NkIAAYiVbw//9/Y6DiM1ANJoyMjGdBbLgJQAX/kU0DKgDLkaQAvxW4HEvQFwCRcxIJK1XznAAAAABJRU5ErkJggg==");background-repeat:repeat-y;opacity:0.3;filter:"alpha(opacity=30)";-ms-filter:"alpha(opacity=30)"}.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{height:4px;margin:6px 0;background-repeat:repeat-x}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonUp{background-position:-16px -72px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonDown{background-position:-16px -92px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonLeft{background-position:-20px -112px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonRight{background-position:-20px -128px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAALElEQVQYV2NkIAAYSVFgDFR8BqrBBEifBbGRTfiPZhpYjiQFBK3A6l6CvgAAE9kGCd1mvgEAAAAASUVORK5CYII=")}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-96px -72px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-96px -92px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-100px -112px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-100px -128px}.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-repeat:repeat-y;background-image:-moz-linear-gradient(left, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:-webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255,255,255,0.5)), color-stop(100%, rgba(255,255,255,0)));background-image:-webkit-linear-gradient(left, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:-o-linear-gradient(left, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:-ms-linear-gradient(left, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:linear-gradient(to right, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%)}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{background-repeat:repeat-x;background-image:-moz-linear-gradient(top, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(255,255,255,0.5)), color-stop(100%, rgba(255,255,255,0)));background-image:-webkit-linear-gradient(top, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:-o-linear-gradient(top, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:-ms-linear-gradient(top, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);background-image:linear-gradient(to bottom, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%)}.mCS-3d.mCSB_scrollTools_vertical .mCSB_dragger,.mCS-3d-dark.mCSB_scrollTools_vertical .mCSB_dragger{height:70px}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger{width:70px}.mCS-3d.mCSB_scrollTools,.mCS-3d-dark.mCSB_scrollTools{opacity:1;filter:"alpha(opacity=30)";-ms-filter:"alpha(opacity=30)"}.mCS-3d.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}.mCS-3d.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail{width:8px;background-color:#000;background-color:rgba(0,0,0,0.2);box-shadow:inset 1px 0 1px rgba(0,0,0,0.5),inset -1px 0 1px rgba(255,255,255,0.2)}.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#555}.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:8px}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:8px;margin:4px 0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.5),inset 0 -1px 1px rgba(255,255,255,0.2)}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:8px;margin:4px auto}.mCS-3d.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-3d.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-3d.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-3d.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1);box-shadow:inset 1px 0 1px rgba(0,0,0,0.1)}.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{box-shadow:inset 0 1px 1px rgba(0,0,0,0.1)}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-3d-thick.mCSB_scrollTools,.mCS-3d-thick-dark.mCSB_scrollTools{opacity:1;filter:"alpha(opacity=30)";-ms-filter:"alpha(opacity=30)"}.mCS-3d-thick.mCSB_scrollTools,.mCS-3d-thick-dark.mCSB_scrollTools,.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerContainer,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerContainer{-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.mCSB_inside+.mCS-3d-thick.mCSB_scrollTools_vertical,.mCSB_inside+.mCS-3d-thick-dark.mCSB_scrollTools_vertical{right:1px}.mCS-3d-thick.mCSB_scrollTools_vertical,.mCS-3d-thick-dark.mCSB_scrollTools_vertical{box-shadow:inset 1px 0 1px rgba(0,0,0,0.1),inset 0 0 14px rgba(0,0,0,0.5)}.mCS-3d-thick.mCSB_scrollTools_horizontal,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal{bottom:1px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.1),inset 0 0 14px rgba(0,0,0,0.5)}.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 1px 0 0 rgba(255,255,255,0.4);width:12px;margin:2px;position:absolute;height:auto;top:0;bottom:0;left:0;right:0}.mCS-3d-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 0 1px 0 rgba(255,255,255,0.4)}.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#555}.mCS-3d-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{height:12px;width:auto}.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerContainer{background-color:#000;background-color:rgba(0,0,0,0.05);box-shadow:inset 1px 1px 16px rgba(0,0,0,0.1)}.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-3d-thick-dark.mCSB_scrollTools{box-shadow:inset 0 0 14px rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools_horizontal{box-shadow:inset 0 1px 1px rgba(0,0,0,0.1),inset 0 0 14px rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 1px 0 0 rgba(255,255,255,0.4),inset -1px 0 0 rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 0 1px 0 rgba(255,255,255,0.4),inset 0 -1px 0 rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#777}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerContainer{background-color:#fff;background-color:rgba(0,0,0,0.05);box-shadow:inset 1px 1px 16px rgba(0,0,0,0.1)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCSB_outside+.mCS-minimal.mCSB_scrollTools_vertical,.mCSB_outside+.mCS-minimal-dark.mCSB_scrollTools_vertical{right:0;margin:12px 0}.mCustomScrollBox.mCS-minimal+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal-dark+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal-dark+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal{bottom:0;margin:0 12px}.mCS-dir-rtl>.mCSB_outside+.mCS-minimal.mCSB_scrollTools_vertical,.mCS-dir-rtl>.mCSB_outside+.mCS-minimal-dark.mCSB_scrollTools_vertical{left:0;right:auto}.mCS-minimal.mCSB_scrollTools .mCSB_draggerRail,.mCS-minimal-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-minimal.mCSB_scrollTools_vertical .mCSB_dragger,.mCS-minimal-dark.mCSB_scrollTools_vertical .mCSB_dragger{height:50px}.mCS-minimal.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-minimal-dark.mCSB_scrollTools_horizontal .mCSB_dragger{width:50px}.mCS-minimal.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.2);filter:"alpha(opacity=20)";-ms-filter:"alpha(opacity=20)"}.mCS-minimal.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-minimal.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.5);filter:"alpha(opacity=50)";-ms-filter:"alpha(opacity=50)"}.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.2);filter:"alpha(opacity=20)";-ms-filter:"alpha(opacity=20)"}.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.5);filter:"alpha(opacity=50)";-ms-filter:"alpha(opacity=50)"}.mCS-light-3.mCSB_scrollTools .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools .mCSB_draggerRail{width:6px;background-color:#000;background-color:rgba(0,0,0,0.2)}.mCS-light-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px}.mCS-light-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-light-3.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:6px;margin:5px 0}.mCS-light-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:12px}.mCS-light-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:12px;margin:2px 0}.mCS-light-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-3.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1)}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-inset.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{width:12px;background-color:#000;background-color:rgba(0,0,0,0.2)}.mCS-inset.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px;margin:3px 5px;position:absolute;height:auto;top:0;bottom:0;left:0;right:0}.mCS-inset.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{height:6px;margin:5px 3px;position:absolute;width:auto;top:0;bottom:0;left:0;right:0}.mCS-inset.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-3.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:12px;margin:2px 0}.mCS-inset.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-inset.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-inset.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-inset.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-inset-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1)}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-inset-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent;border-width:1px;border-style:solid;border-color:#fff;border-color:rgba(255,255,255,0.2);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail{border-color:#000;border-color:rgba(0,0,0,0.2)}.mCS-inset-3.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,0.6)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.6)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.75)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9)}.alertify{position:fixed;background-color:rgba(0,0,0,0.3);left:0;right:0;top:0;bottom:0;width:100%;height:100%;z-index:99999}.alertify.hide{opacity:0;pointer-events:none}.alertify,.alertify.show{box-sizing:border-box;transition:all 0.33s cubic-bezier(0.25, 0.8, 0.25, 1)}.alertify,.alertify *{box-sizing:border-box}.alertify .dialog,.alertify .alert{width:100%;padding:0;margin:0 auto;position:relative;top:50%}.alertify .dialog>*,.alertify .alert>*{width:400px;max-width:95%;margin:0 auto;text-align:left;padding:18px;background:#fff;box-shadow:0 2px 4px -1px rgba(0,0,0,0.14),0 4px 5px 0 rgba(0,0,0,0.098),0 1px 10px 0 rgba(0,0,0,0.084)}.alertify .dialog [data-alertify-msg],.alertify .alert [data-alertify-msg]{padding-bottom:12px;text-align:left}.alertify .dialog input[data-alertify-input]:not(.form-control),.alertify .alert input[data-alertify-input]:not(.form-control){width:100%;font-size:100%;padding:8px}.alertify .dialog input[data-alertify-input]:not(.form-control):focus,.alertify .alert input[data-alertify-input]:not(.form-control):focus{outline-offset:-2px}.alertify .dialog [data-alertify-btn-holder],.alertify .alert [data-alertify-btn-holder]{text-align:right;margin-top:12px}.alertify .dialog [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button),.alertify .alert [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button){background:transparent;box-sizing:border-box;color:rgba(0,0,0,0.87);position:relative;outline:0;display:inline-block;align-items:center;padding:0 6px;margin-left:15px;line-height:36px;min-height:36px;white-space:nowrap;min-width:88px;text-align:center;text-transform:uppercase;font-size:14px;text-decoration:none;cursor:pointer;border:1px solid #DBDBDB;border-radius:2px}.alertify .dialog [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):hover,.alertify .dialog [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):active,.alertify .alert [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):hover,.alertify .alert [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):active{background-color:rgba(0,0,0,0.05)}.alertify .dialog [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):focus,.alertify .alert [data-alertify-btn-holder] [data-alertify-btn]:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):focus{border:1px solid rgba(0,0,0,0.1)}.alertify .dialog [data-alertify-btn-holder] [data-alertify-btn].btn,.alertify .alert [data-alertify-btn-holder] [data-alertify-btn].btn{margin:6px 4px}.alertify-logs{position:fixed;z-index:100000}.alertify-logs.bottom,.alertify-logs:not(.top){bottom:16px}.alertify-logs.left,.alertify-logs:not(.right){left:16px}.alertify-logs.left>*,.alertify-logs:not(.right)>*{float:left;transform:translate3d(0, 0, 0);height:auto}.alertify-logs.left>*.show,.alertify-logs:not(.right)>*.show{left:0}.alertify-logs.left>*,.alertify-logs.left>*.hide,.alertify-logs:not(.right)>*,.alertify-logs:not(.right)>*.hide{left:-110%}.alertify-logs.right{right:16px}.alertify-logs.right>*{float:right;transform:translate3d(0, 0, 0)}.alertify-logs.right>*.show{right:0;opacity:1}.alertify-logs.right>*,.alertify-logs.right>*.hide{right:-110%;opacity:0}.alertify-logs.top{top:0}.alertify-logs>*{box-sizing:border-box;transition:all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);position:relative;clear:both;backface-visibility:hidden;perspective:1000}.alertify-logs>*{max-height:0;margin:0;padding:0;overflow:hidden;opacity:0;pointer-events:none}.alertify-logs>*.show{margin-top:12px;opacity:1;max-height:1000px;pointer-events:auto}.alertify-logs [data-alertify-log-msg]{padding:12px 24px;color:#fff;box-shadow:0 2px 5px 0 rgba(0,0,0,0.2);border-radius:1px;transition:.2s all;display:block !important}.alertify-logs [data-alertify-log-msg],.alertify-logs [data-alertify-log-msg].default{color:#fff;background:rgba(0,0,0,0.8)}.alertify-logs [data-alertify-log-msg].success{color:#fff;background:rgba(76,175,80,0.9)}.alertify-logs [data-alertify-log-msg].warning{color:#fff;background:rgba(255,125,25,0.9)}.alertify-logs [data-alertify-log-msg].error{color:#fff;background:rgba(244,67,54,0.9)} diff --git a/src/js/filemanager.js b/src/js/filemanager.js index bdf1561d..31c444b3 100644 --- a/src/js/filemanager.js +++ b/src/js/filemanager.js @@ -10,4977 +10,5019 @@ * @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; } - } - - if(assets.length) { - assets.push(function() { - // after all required assets are loaded - 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); + }; + + 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); + } } - - $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; - } + }; + + 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 + editor_model.mode(currentMode); + }); + loadAssets(assets); + } else { + editor_model.mode(currentMode); + } } }; - - 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; + if (config && config.api && config.api.prevFilePath) { + url = url + config.api.prevFilePath; + } + 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()); + } + }); }); - } 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 (log && process.isProcessed()) { + log.stick(false); + setTimeout(function() { + log.remove(); + }, 6000); + } + }); + + 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) //