File Editor
Directories:
.. (Back)
commands
extras
i18n
proxy
ui
worker
Files:
elFinder.command.js
elFinder.history.js
elFinder.js
elFinder.mimetypes.js
elFinder.options.js
elFinder.options.netmount.js
elFinder.resources.js
elFinder.version.js
elfinder.full.js
elfinder.min.js
jquery.dialogelfinder.js
jquery.elfinder.js
Create New File
Create
Edit File: elfinder.full.js
/*! * elFinder - file manager for web * Version 2.1.49 (2019-04-14) * http://elfinder.org * * Copyright 2009-2019, Studio 42 * Licensed under a 3-clauses BSD license */ (function(root, factory) { if (typeof define === 'function' && define.amd) { // AMD define(['jquery','jquery-ui'], factory); } else if (typeof exports !== 'undefined') { // CommonJS var $, ui; try { $ = require('jquery'); ui = require('jquery-ui'); } catch (e) {} module.exports = factory($, ui); } else { // Browser globals (Note: root is window) factory(root.jQuery, root.jQuery.ui, true); } }(this, function($, _ui, toGlobal) { toGlobal = toGlobal || false; /* * File: /js/elFinder.js */ /** * @class elFinder - file manager for web * * @author Dmitry (dio) Levashov **/ var elFinder = function(elm, opts, bootCallback) { //this.time('load'); var self = this, /** * Objects array of jQuery.Deferred that calls before elFinder boot up * * @type Array */ dfrdsBeforeBootup = [], /** * Plugin name to check for conflicts with bootstrap etc * * @type Array **/ conflictChecks = ['button', 'tooltip'], /** * Node on which elfinder creating * * @type jQuery **/ node = jQuery(elm), /** * Object of events originally registered in this node * * @type Object */ prevEvents = jQuery.extend(true, {}, jQuery._data(node.get(0), 'events')), /** * Store node contents. * * @see this.destroy * @type jQuery **/ prevContent = jQuery('<div/>').append(node.contents()).attr('class', node.attr('class') || '').attr('style', node.attr('style') || ''), /** * Instance ID. Required to get/set cookie * * @type String **/ id = node.attr('id') || '', /** * Events namespace * * @type String **/ namespace = 'elfinder-' + (id ? id : Math.random().toString().substr(2, 7)), /** * Mousedown event * * @type String **/ mousedown = 'mousedown.'+namespace, /** * Keydown event * * @type String **/ keydown = 'keydown.'+namespace, /** * Keypress event * * @type String **/ keypress = 'keypress.'+namespace, /** * Keypup event * * @type String **/ keyup = 'keyup.'+namespace, /** * Is shortcuts/commands enabled * * @type Boolean **/ enabled = false, /** * Store enabled value before ajax request * * @type Boolean **/ prevEnabled = false, /** * List of build-in events which mapped into methods with same names * * @type Array **/ events = ['enable', 'disable', 'load', 'open', 'reload', 'select', 'add', 'remove', 'change', 'dblclick', 'getfile', 'lockfiles', 'unlockfiles', 'selectfiles', 'unselectfiles', 'dragstart', 'dragstop', 'search', 'searchend', 'viewchange'], /** * Rules to validate data from backend * * @type Object **/ rules = {}, /** * Current working directory hash * * @type String **/ cwd = '', /** * Current working directory options default * * @type Object **/ cwdOptionsDefault = { path : '', url : '', tmbUrl : '', disabled : [], separator : '/', archives : [], extract : [], copyOverwrite : true, uploadOverwrite : true, uploadMaxSize : 0, jpgQuality : 100, tmbCrop : false, tmb : false // old API }, /** * Current working directory options * * @type Object **/ cwdOptions = {}, /** * Files/dirs cache * * @type Object **/ files = {}, /** * Hidden Files/dirs cache * * @type Object **/ hiddenFiles = {}, /** * Files/dirs hash cache of each dirs * * @type Object **/ ownFiles = {}, /** * Selected files hashes * * @type Array **/ selected = [], /** * Events listeners * * @type Object **/ listeners = {}, /** * Shortcuts * * @type Object **/ shortcuts = {}, /** * Buffer for copied files * * @type Array **/ clipboard = [], /** * Copied/cuted files hashes * Prevent from remove its from cache. * Required for dispaly correct files names in error messages * * @type Object **/ remember = {}, /** * Queue for 'open' requests * * @type Array **/ queue = [], /** * Queue for only cwd requests e.g. `tmb` * * @type Array **/ cwdQueue = [], /** * Commands prototype * * @type Object **/ base = new self.command(self), /** * elFinder node width * * @type String * @default "auto" **/ width = 'auto', /** * elFinder node height * Number: pixcel or String: Number + "%" * * @type Number | String * @default 400 **/ height = 400, /** * Base node object or selector * Element which is the reference of the height percentage * * @type Object|String * @default null | jQuery(window) (if height is percentage) **/ heightBase = null, /** * MIME type list(Associative array) handled as a text file * * @type Object|null */ textMimes = null, /** * elfinder path for sound played on remove * @type String * @default ./sounds/ **/ soundPath = 'sounds/', /** * JSON.stringify of previous fm.sorters * @type String */ prevSorterStr = '', /** * Map table of file extention to MIME-Type * @type Object */ extToMimeTable, beeper = jQuery(document.createElement('audio')).hide().appendTo('body')[0], syncInterval, autoSyncStop = 0, uiCmdMapPrev = '', gcJobRes = null, open = function(data) { // NOTES: Do not touch data object var volumeid, contextmenu, emptyDirs = {}, stayDirs = {}, rmClass, hashes, calc, gc, collapsed, prevcwd, sorterStr; if (self.api >= 2.1) { // support volume driver option `uiCmdMap` self.commandMap = (data.options.uiCmdMap && Object.keys(data.options.uiCmdMap).length)? data.options.uiCmdMap : {}; if (uiCmdMapPrev !== JSON.stringify(self.commandMap)) { uiCmdMapPrev = JSON.stringify(self.commandMap); } } else { self.options.sync = 0; } if (data.init) { // init - reset cache files = {}; ownFiles = {}; } else { // remove only files from prev cwd // and collapsed directory (included 100+ directories) to empty for perfomance tune in DnD prevcwd = cwd; rmClass = 'elfinder-subtree-loaded ' + self.res('class', 'navexpand'); collapsed = self.res('class', 'navcollapse'); hashes = Object.keys(files); calc = function(i) { if (!files[i]) { return true; } var isDir = (files[i].mime === 'directory'), phash = files[i].phash, pnav; if ( (!isDir || emptyDirs[phash] || (!stayDirs[phash] && self.navHash2Elm(files[i].hash).is(':hidden') && self.navHash2Elm(phash).next('.elfinder-navbar-subtree').children().length > 100 ) ) && (isDir || phash !== cwd) && ! remember[i] ) { if (isDir && !emptyDirs[phash]) { emptyDirs[phash] = true; self.navHash2Elm(phash) .removeClass(rmClass) .next('.elfinder-navbar-subtree').empty(); } deleteCache(files[i]); } else if (isDir) { stayDirs[phash] = true; } }; gc = function() { if (hashes.length) { gcJobRes && gcJobRes._abort(); gcJobRes = self.asyncJob(calc, hashes, { interval : 20, numPerOnce : 100 }).done(function() { var hd = self.storage('hide') || {items: {}}; if (Object.keys(hiddenFiles).length) { jQuery.each(hiddenFiles, function(h) { if (!hd.items[h]) { delete hiddenFiles[h]; } }); } }); } }; self.trigger('filesgc').one('filesgc', function() { hashes = []; }); self.one('opendone', function() { if (prevcwd !== cwd) { if (! node.data('lazycnt')) { gc(); } else { self.one('lazydone', gc); } } }); } self.sorters = {}; cwd = data.cwd.hash; cache(data.files); if (!files[cwd]) { cache([data.cwd]); } // trigger event 'sorterupdate' sorterStr = JSON.stringify(self.sorters); if (prevSorterStr !== sorterStr) { self.trigger('sorterupdate'); prevSorterStr = sorterStr; } self.lastDir(cwd); self.autoSync(); }, /** * Store info about files/dirs in "files" object. * * @param Array files * @param String data type * @return void **/ cache = function(data, type) { var defsorter = { name: true, perm: true, date: true, size: true, kind: true }, sorterChk = !self.sorters._checked, l = data.length, setSorter = function(file) { var f = file || {}, sorters = []; jQuery.each(self.sortRules, function(key) { if (defsorter[key] || typeof f[key] !== 'undefined' || (key === 'mode' && typeof f.perm !== 'undefined')) { sorters.push(key); } }); self.sorters = self.arrayFlip(sorters, true); self.sorters._checked = true; }, keeps = ['sizeInfo'], changedParents = {}, hideData = self.storage('hide') || {}, hides = hideData.items || {}, f, i, keepProp, parents, hidden; for (i = 0; i < l; i++) { f = Object.assign({}, data[i]); hidden = (!hideData.show && hides[f.hash])? true : false; if (f.name && f.hash && f.mime) { if (!hidden) { if (sorterChk && f.phash === cwd) { setSorter(f); sorterChk = false; } if (f.phash && (type === 'add' || type === 'change')) { if (parents = self.parents(f.phash)) { jQuery.each(parents, function() { changedParents[this] = true; }); } } } if (files[f.hash]) { jQuery.each(keeps, function() { if(files[f.hash][this] && ! f[this]) { f[this] = files[f.hash][this]; } }); if (f.sizeInfo && !f.size) { f.size = f.sizeInfo.size; } deleteCache(files[f.hash], true); } if (hides[f.hash]) { hiddenFiles[f.hash] = f; } if (hidden) { l--; data.splice(i--, 1); } else { files[f.hash] = f; if (f.mime === 'directory' && !ownFiles[f.hash]) { ownFiles[f.hash] = {}; } if (f.phash) { if (!ownFiles[f.phash]) { ownFiles[f.phash] = {}; } ownFiles[f.phash][f.hash] = true; } } } } // delete sizeInfo cache jQuery.each(Object.keys(changedParents), function() { var target = files[this]; if (target && target.sizeInfo) { delete target.sizeInfo; } }); // for empty folder sorterChk && setSorter(); }, /** * Delete file object from files caches * * @param Array removed hashes * @return void */ remove = function(removed) { var l = removed.length, roots = {}, rm = function(hash) { var file = files[hash], i; if (file) { if (file.mime === 'directory') { if (roots[hash]) { delete self.roots[roots[hash]]; } // restore stats of deleted root parent directory jQuery.each(self.leafRoots, function(phash, roots) { var idx, pdir; if ((idx = jQuery.inArray(hash, roots))!== -1) { if (roots.length === 1) { if ((pdir = Object.assign({}, files[phash])) && pdir._realStats) { jQuery.each(pdir._realStats, function(k, v) { pdir[k] = v; }); remove(files[phash]._realStats); self.change({ changed: [pdir] }); } delete self.leafRoots[phash]; } else { self.leafRoots[phash].splice(idx, 1); } } }); if (self.searchStatus.state < 2) { jQuery.each(files, function(h, f) { f.phash == hash && rm(h); }); } } if (file.phash) { if (parents = self.parents(file.phash)) { jQuery.each(parents, function() { changedParents[this] = true; }); } } deleteCache(files[hash]); } }, changedParents = {}, parents; jQuery.each(self.roots, function(k, v) { roots[v] = k; }); while (l--) { rm(removed[l]); } // delete sizeInfo cache jQuery.each(Object.keys(changedParents), function() { var target = files[this]; if (target && target.sizeInfo) { delete target.sizeInfo; } }); }, /** * Update file object in files caches * * @param Array changed file objects * @return void */ change = function(changed) { jQuery.each(changed, function(i, file) { var hash = file.hash; if (files[hash]) { jQuery.each(Object.keys(files[hash]), function(i, v){ if (typeof file[v] === 'undefined') { delete files[hash][v]; } }); } files[hash] = files[hash] ? Object.assign(files[hash], file) : file; }); }, /** * Delete cache data of files, ownFiles and self.optionsByHashes * * @param Object file * @param Boolean update * @return void */ deleteCache = function(file, update) { var hash = file.hash, phash = file.phash; if (phash && ownFiles[phash]) { delete ownFiles[phash][hash]; } if (!update) { ownFiles[hash] && delete ownFiles[hash]; self.optionsByHashes[hash] && delete self.optionsByHashes[hash]; } delete files[hash]; }, /** * Maximum number of concurrent connections on request * * @type Number */ requestMaxConn, /** * Current number of connections * * @type Number */ requestCnt = 0, /** * Queue waiting for connection * * @type Array */ requestQueue = [], /** * Flag to cancel the `open` command waiting for connection * * @type Boolean */ requestQueueSkipOpen = false, /** * Exec shortcut * * @param jQuery.Event keydown/keypress event * @return void */ execShortcut = function(e) { var code = e.keyCode, ctrlKey = !!(e.ctrlKey || e.metaKey), isMousedown = e.type === 'mousedown', ddm; !isMousedown && (self.keyState.keyCode = code); self.keyState.ctrlKey = ctrlKey; self.keyState.shiftKey = e.shiftKey; self.keyState.metaKey = e.metaKey; self.keyState.altKey = e.altKey; if (isMousedown) { return; } else if (e.type === 'keyup') { self.keyState.keyCode = null; return; } if (enabled) { jQuery.each(shortcuts, function(i, shortcut) { if (shortcut.type == e.type && shortcut.keyCode == code && shortcut.shiftKey == e.shiftKey && shortcut.ctrlKey == ctrlKey && shortcut.altKey == e.altKey) { e.preventDefault(); e.stopPropagation(); shortcut.callback(e, self); self.debug('shortcut-exec', i+' : '+shortcut.description); } }); // prevent tab out of elfinder if (code == jQuery.ui.keyCode.TAB && !jQuery(e.target).is(':input')) { e.preventDefault(); } // cancel any actions by [Esc] key if (e.type === 'keydown' && code == jQuery.ui.keyCode.ESCAPE) { // copy or cut if (! node.find('.ui-widget:visible').length) { self.clipboard().length && self.clipboard([]); } // dragging if (jQuery.ui.ddmanager) { ddm = jQuery.ui.ddmanager.current; ddm && ddm.helper && ddm.cancel(); } // button menus self.toHide(node.find('.ui-widget.elfinder-button-menu.elfinder-frontmost:visible')); // trigger keydownEsc self.trigger('keydownEsc', e); } } }, date = new Date(), utc, i18n, inFrame = (window.parent !== window), parentIframe = (function() { var pifm, ifms; if (inFrame) { try { ifms = jQuery('iframe', window.parent.document); if (ifms.length) { jQuery.each(ifms, function(i, ifm) { if (ifm.contentWindow === window) { pifm = jQuery(ifm); return false; } }); } } catch(e) {} } return pifm; })(), /** * elFinder boot up function * * @type Function */ bootUp, /** * Original function of XMLHttpRequest.prototype.send * * @type Function */ savedXhrSend; // opts must be an object if (!opts) { opts = {}; } // set UA.Angle, UA.Rotated for mobile devices if (self.UA.Mobile) { jQuery(window).on('orientationchange.'+namespace, function() { var a = ((screen && screen.orientation && screen.orientation.angle) || window.orientation || 0) + 0; if (a === -90) { a = 270; } self.UA.Angle = a; self.UA.Rotated = a % 180 === 0? false : true; }).trigger('orientationchange.'+namespace); } // check opt.bootCallback if (opts.bootCallback && typeof opts.bootCallback === 'function') { (function() { var func = bootCallback, opFunc = opts.bootCallback; bootCallback = function(fm, extraObj) { func && typeof func === 'function' && func.call(this, fm, extraObj); opFunc.call(this, fm, extraObj); }; })(); } delete opts.bootCallback; /** * Protocol version * * @type String **/ this.api = null; /** * elFinder use new api * * @type Boolean **/ this.newAPI = false; /** * elFinder use old api * * @type Boolean **/ this.oldAPI = false; /** * Net drivers names * * @type Array **/ this.netDrivers = []; /** * Base URL of elfFinder library starting from Manager HTML * * @type String */ this.baseUrl = ''; /** * Base URL of i18n js files * baseUrl + "js/i18n/" when empty value * * @type String */ this.i18nBaseUrl = ''; /** * Is elFinder CSS loaded * * @type Boolean */ this.cssloaded = false; /** * Current theme object * * @type Object|Null */ this.theme = null; this.mimesCanMakeEmpty = {}; /** * Callback function at boot up that option specified at elFinder starting * * @type Function */ this.bootCallback; /** * ID. Required to create unique cookie name * * @type String **/ this.id = id; /** * Method to store/fetch data * * @type Function **/ this.storage = (function() { try { if ('localStorage' in window && window.localStorage !== null) { if (self.UA.Safari) { // check for Mac/iOS safari private browsing mode window.localStorage.setItem('elfstoragecheck', 1); window.localStorage.removeItem('elfstoragecheck'); } return self.localStorage; } else { return self.cookie; } } catch (e) { return self.cookie; } })(); /** * Configuration options * * @type Object **/ //this.options = jQuery.extend(true, {}, this._options, opts); this.options = Object.assign({}, this._options); // for old type configuration if (opts.uiOptions) { if (opts.uiOptions.toolbar && Array.isArray(opts.uiOptions.toolbar)) { if (jQuery.isPlainObject(opts.uiOptions.toolbar[opts.uiOptions.toolbar.length - 1])) { self.options.uiOptions.toolbarExtra = Object.assign(self.options.uiOptions.toolbarExtra || {}, opts.uiOptions.toolbar.pop()); } } } // Overwrite if opts value is an array (function() { var arrOv = function(obj, base) { if (jQuery.isPlainObject(obj)) { jQuery.each(obj, function(k, v) { if (jQuery.isPlainObject(v)) { if (!base[k]) { base[k] = {}; } arrOv(v, base[k]); } else { base[k] = v; } }); } }; arrOv(opts, self.options); })(); // join toolbarExtra to toolbar this.options.uiOptions.toolbar.push(this.options.uiOptions.toolbarExtra); delete this.options.uiOptions.toolbarExtra; /** * Arrays that has to unbind events * * @type Object */ this.toUnbindEvents = {}; /** * Attach listener to events * To bind to multiply events at once, separate events names by space * * @param String event(s) name(s) * @param Object event handler or {done: handler} * @param Boolean priority first * @return elFinder */ this.bind = function(event, callback, priorityFirst) { var i, len; if (callback && (typeof callback === 'function' || typeof callback.done === 'function')) { event = ('' + event).toLowerCase().replace(/^\s+|\s+$/g, '').split(/\s+/); len = event.length; for (i = 0; i < len; i++) { if (listeners[event[i]] === void(0)) { listeners[event[i]] = []; } listeners[event[i]][priorityFirst? 'unshift' : 'push'](callback); } } return this; }; /** * Remove event listener if exists * To un-bind to multiply events at once, separate events names by space * * @param String event(s) name(s) * @param Function callback * @return elFinder */ this.unbind = function(event, callback) { var i, len, l, ci; event = ('' + event).toLowerCase().split(/\s+/); len = event.length; for (i = 0; i < len; i++) { if (l = listeners[event[i]]) { ci = jQuery.inArray(callback, l); ci > -1 && l.splice(ci, 1); } } callback = null; return this; }; /** * Fire event - send notification to all event listeners * In the callback `this` becames an event object * * @param String event type * @param Object data to send across event * @param Boolean allow modify data (call by reference of data) default: true * @return elFinder */ this.trigger = function(evType, data, allowModify) { var type = evType.toLowerCase(), isopen = (type === 'open'), dataIsObj = (typeof data === 'object'), handlers = listeners[type] || [], dones = [], i, l, jst, event; this.debug('event-'+type, data); if (! dataIsObj || typeof allowModify === 'undefined') { allowModify = true; } if (l = handlers.length) { event = jQuery.Event(type); if (data) { data._event = event; } if (allowModify) { event.data = data; } for (i = 0; i < l; i++) { if (! handlers[i]) { // probably un-binded this handler continue; } // handler is jQuery.Deferred(), call all functions upon completion if (handlers[i].done) { dones.push(handlers[i].done); continue; } // set `event.data` only callback has argument if (handlers[i].length) { if (!allowModify) { // to avoid data modifications. remember about "sharing" passing arguments in js :) if (typeof jst === 'undefined') { try { jst = JSON.stringify(data); } catch(e) { jst = false; } } event.data = jst? JSON.parse(jst) : data; } } try { if (handlers[i].call(event, event, this) === false || event.isDefaultPrevented()) { this.debug('event-stoped', event.type); break; } } catch (ex) { window.console && window.console.log && window.console.log(ex); } } // call done functions if (l = dones.length) { for (i = 0; i < l; i++) { try { if (dones[i].call(event, event, this) === false || event.isDefaultPrevented()) { this.debug('event-stoped', event.type + '(done)'); break; } } catch (ex) { window.console && window.console.log && window.console.log(ex); } } } if (this.toUnbindEvents[type] && this.toUnbindEvents[type].length) { jQuery.each(this.toUnbindEvents[type], function(i, v) { self.unbind(v.type, v.callback); }); delete this.toUnbindEvents[type]; } } return this; }; /** * Get event listeners * * @param String event type * @return Array listed event functions */ this.getListeners = function(event) { return event? listeners[event.toLowerCase()] : listeners; }; // set fm.baseUrl this.baseUrl = (function() { var myTag, myCss, base, baseUrl; if (self.options.baseUrl) { return self.options.baseUrl; } else { baseUrl = ''; //myTag = jQuery('head > script[src$="js/elfinder.min.js"],script[src$="js/elfinder.full.js"]:first'); myTag = null; jQuery('head > script').each(function() { if (this.src && this.src.match(/js\/elfinder(?:-[a-z0-9_-]+)?\.(?:min|full)\.js$/i)) { myTag = jQuery(this); return false; } }); if (myTag) { myCss = jQuery('head > link[href$="css/elfinder.min.css"],link[href$="css/elfinder.full.css"]:first').length; if (! myCss) { // to request CSS auto loading self.cssloaded = null; } baseUrl = myTag.attr('src').replace(/js\/[^\/]+$/, ''); if (! baseUrl.match(/^(https?\/\/|\/)/)) { // check <base> tag if (base = jQuery('head > base[href]').attr('href')) { baseUrl = base.replace(/\/$/, '') + '/' + baseUrl; } } } if (baseUrl !== '') { self.options.baseUrl = baseUrl; } else { if (! self.options.baseUrl) { self.options.baseUrl = './'; } baseUrl = self.options.baseUrl; } return baseUrl; } })(); this.i18nBaseUrl = (this.options.i18nBaseUrl || this.baseUrl + 'js/i18n').replace(/\/$/, '') + '/'; this.options.maxErrorDialogs = Math.max(1, parseInt(this.options.maxErrorDialogs || 5)); // set dispInlineRegex cwdOptionsDefault.dispInlineRegex = this.options.dispInlineRegex; // auto load required CSS if (this.options.cssAutoLoad) { (function() { var baseUrl = self.baseUrl; // additional CSS files if (Array.isArray(self.options.cssAutoLoad)) { if (self.cssloaded === true) { self.loadCss(self.options.cssAutoLoad); } else { self.bind('cssloaded', function() { self.loadCss(self.options.cssAutoLoad); }); } } // try to load main css if (self.cssloaded === null) { // hide elFinder node while css loading node.data('cssautoloadHide', jQuery('<style>.elfinder{visibility:hidden;overflow:hidden}</style>')); jQuery('head').append(node.data('cssautoloadHide')); // set default theme if (!self.options.themes.default) { self.options.themes = Object.assign({ 'default' : { 'name': 'default', 'cssurls': 'css/theme.css', 'author': 'elFinder Project', 'license': '3-clauses BSD' } }, self.options.themes); if (!self.options.theme) { self.options.theme = 'default'; } } // load CSS self.loadCss([baseUrl+'css/elfinder.min.css'], { dfd: jQuery.Deferred().always(function() { if (node.data('cssautoloadHide')) { node.data('cssautoloadHide').remove(); node.removeData('cssautoloadHide'); } }).done(function() { if (!self.cssloaded) { self.cssloaded = true; self.trigger('cssloaded'); } }).fail(function() { self.cssloaded = false; self.error(['errRead', 'CSS (elfinder or theme)']); }) }); } self.options.cssAutoLoad = false; })(); } // load theme if exists this.changeTheme(this.storage('theme') || this.options.theme); /** * Volume option to set the properties of the root Stat * * @type Object */ this.optionProperties = { icon: void(0), csscls: void(0), tmbUrl: void(0), uiCmdMap: {}, netkey: void(0), disabled: [] }; if (! inFrame && ! this.options.enableAlways && jQuery('body').children().length === 2) { // only node and beeper this.options.enableAlways = true; } // make options.debug if (this.options.debug === true) { this.options.debug = 'all'; } else if (Array.isArray(this.options.debug)) { (function() { var d = {}; jQuery.each(self.options.debug, function() { d[this] = true; }); self.options.debug = d; })(); } else { this.options.debug = false; } /** * Original functions evacuated by conflict check * * @type Object */ this.noConflicts = {}; /** * Check and save conflicts with bootstrap etc * * @type Function */ this.noConflict = function() { jQuery.each(conflictChecks, function(i, p) { if (jQuery.fn[p] && typeof jQuery.fn[p].noConflict === 'function') { self.noConflicts[p] = jQuery.fn[p].noConflict(); } }); }; // do check conflict this.noConflict(); /** * Is elFinder over CORS * * @type Boolean **/ this.isCORS = false; // configure for CORS (function(){ if (typeof self.options.cors !== 'undefined' && self.options.cors !== null) { self.isCORS = self.options.cors? true : false; } else { var parseUrl = document.createElement('a'), parseUploadUrl, selfProtocol = window.location.protocol, portReg = function(protocol) { protocol = (!protocol || protocol === ':')? selfProtocol : protocol; return protocol === 'https:'? /\:443$/ : /\:80$/; }, selfHost = window.location.host.replace(portReg(selfProtocol), ''); parseUrl.href = opts.url; if (opts.urlUpload && (opts.urlUpload !== opts.url)) { parseUploadUrl = document.createElement('a'); parseUploadUrl.href = opts.urlUpload; } if (selfHost !== parseUrl.host.replace(portReg(parseUrl.protocol), '') || (parseUrl.protocol !== ':'&& parseUrl.protocol !== '' && (selfProtocol !== parseUrl.protocol)) || (parseUploadUrl && (selfHost !== parseUploadUrl.host.replace(portReg(parseUploadUrl.protocol), '') || (parseUploadUrl.protocol !== ':' && parseUploadUrl.protocol !== '' && (selfProtocol !== parseUploadUrl.protocol)) ) ) ) { self.isCORS = true; } } if (self.isCORS) { if (!jQuery.isPlainObject(self.options.customHeaders)) { self.options.customHeaders = {}; } if (!jQuery.isPlainObject(self.options.xhrFields)) { self.options.xhrFields = {}; } self.options.requestType = 'post'; self.options.customHeaders['X-Requested-With'] = 'XMLHttpRequest'; self.options.xhrFields['withCredentials'] = true; } })(); /** * Ajax request type * * @type String * @default "get" **/ this.requestType = /^(get|post)$/i.test(this.options.requestType) ? this.options.requestType.toLowerCase() : 'get'; // set `requestMaxConn` by option requestMaxConn = Math.max(parseInt(this.options.requestMaxConn), 1); /** * Custom data that given as options * * @type Object * @default {} */ this.optsCustomData = jQuery.isPlainObject(this.options.customData) ? this.options.customData : {}; /** * Any data to send across every ajax request * * @type Object * @default {} **/ this.customData = Object.assign({}, this.optsCustomData); /** * Previous custom data from connector * * @type Object|null */ this.prevCustomData = null; /** * Any custom headers to send across every ajax request * * @type Object * @default {} */ this.customHeaders = jQuery.isPlainObject(this.options.customHeaders) ? this.options.customHeaders : {}; /** * Any custom xhrFields to send across every ajax request * * @type Object * @default {} */ this.xhrFields = jQuery.isPlainObject(this.options.xhrFields) ? this.options.xhrFields : {}; /** * Replace XMLHttpRequest.prototype.send to extended function for 3rd party libs XHR request etc. * * @type Function */ this.replaceXhrSend = function() { if (! savedXhrSend) { savedXhrSend = XMLHttpRequest.prototype.send; } XMLHttpRequest.prototype.send = function() { var xhr = this; // set request headers if (self.customHeaders) { jQuery.each(self.customHeaders, function(key) { xhr.setRequestHeader(key, this); }); } // set xhrFields if (self.xhrFields) { jQuery.each(self.xhrFields, function(key) { if (key in xhr) { xhr[key] = this; } }); } return savedXhrSend.apply(this, arguments); }; }; /** * Restore saved original XMLHttpRequest.prototype.send * * @type Function */ this.restoreXhrSend = function() { savedXhrSend && (XMLHttpRequest.prototype.send = savedXhrSend); }; /** * command names for into queue for only cwd requests * these commands aborts before `open` request * * @type Array * @default ['tmb', 'parents'] */ this.abortCmdsOnOpen = this.options.abortCmdsOnOpen || ['tmb', 'parents']; /** * ui.nav id prefix * * @type String */ this.navPrefix = 'nav' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-'; /** * ui.cwd id prefix * * @type String */ this.cwdPrefix = elFinder.prototype.uniqueid? ('cwd' + elFinder.prototype.uniqueid + '-') : ''; // Increment elFinder.prototype.uniqueid ++elFinder.prototype.uniqueid; /** * URL to upload files * * @type String **/ this.uploadURL = opts.urlUpload || opts.url; /** * Events namespace * * @type String **/ this.namespace = namespace; /** * Today timestamp * * @type Number **/ this.today = (new Date(date.getFullYear(), date.getMonth(), date.getDate())).getTime()/1000; /** * Yesterday timestamp * * @type Number **/ this.yesterday = this.today - 86400; utc = this.options.UTCDate ? 'UTC' : ''; this.getHours = 'get'+utc+'Hours'; this.getMinutes = 'get'+utc+'Minutes'; this.getSeconds = 'get'+utc+'Seconds'; this.getDate = 'get'+utc+'Date'; this.getDay = 'get'+utc+'Day'; this.getMonth = 'get'+utc+'Month'; this.getFullYear = 'get'+utc+'FullYear'; /** * elFinder node z-index (auto detect on elFinder load) * * @type null | Number **/ this.zIndex; /** * Current search status * * @type Object */ this.searchStatus = { state : 0, // 0: search ended, 1: search started, 2: in search result query : '', target : '', mime : '', mixed : false, // in multi volumes search: false or Array that target volume ids ininc : false // in incremental search }; /** * Interface language * * @type String * @default "en" **/ this.lang = this.storage('lang') || this.options.lang; if (this.lang === 'jp') { this.lang = this.options.lang = 'ja'; } this.viewType = this.storage('view') || this.options.defaultView || 'icons'; this.sortType = this.storage('sortType') || this.options.sortType || 'name'; this.sortOrder = this.storage('sortOrder') || this.options.sortOrder || 'asc'; this.sortStickFolders = this.storage('sortStickFolders'); if (this.sortStickFolders === null) { this.sortStickFolders = !!this.options.sortStickFolders; } else { this.sortStickFolders = !!this.sortStickFolders; } this.sortAlsoTreeview = this.storage('sortAlsoTreeview'); if (this.sortAlsoTreeview === null || this.options.sortAlsoTreeview === null) { this.sortAlsoTreeview = !!this.options.sortAlsoTreeview; } else { this.sortAlsoTreeview = !!this.sortAlsoTreeview; } this.sortRules = jQuery.extend(true, {}, this._sortRules, this.options.sortRules); jQuery.each(this.sortRules, function(name, method) { if (typeof method != 'function') { delete self.sortRules[name]; } }); this.compare = jQuery.proxy(this.compare, this); /** * Delay in ms before open notification dialog * * @type Number * @default 500 **/ this.notifyDelay = this.options.notifyDelay > 0 ? parseInt(this.options.notifyDelay) : 500; /** * Dragging UI Helper object * * @type jQuery | null **/ this.draggingUiHelper = null; /** * Base droppable options * * @type Object **/ this.droppable = { greedy : true, tolerance : 'pointer', accept : '.elfinder-cwd-file-wrapper,.elfinder-navbar-dir,.elfinder-cwd-file,.elfinder-cwd-filename', hoverClass : this.res('class', 'adroppable'), classes : { // Deprecated hoverClass jQueryUI>=1.12.0 'ui-droppable-hover': this.res('class', 'adroppable') }, autoDisable: true, // elFinder original, see jquery.elfinder.js drop : function(e, ui) { var dst = jQuery(this), targets = jQuery.grep(ui.helper.data('files')||[], function(h) { return h? true : false; }), result = [], dups = [], faults = [], isCopy = ui.helper.hasClass('elfinder-drag-helper-plus'), c = 'class', cnt, hash, i, h; if (typeof e.button === 'undefined' || ui.helper.data('namespace') !== namespace || ! self.insideWorkzone(e.pageX, e.pageY)) { return false; } if (dst.hasClass(self.res(c, 'cwdfile'))) { hash = self.cwdId2Hash(dst.attr('id')); } else if (dst.hasClass(self.res(c, 'navdir'))) { hash = self.navId2Hash(dst.attr('id')); } else { hash = cwd; } cnt = targets.length; while (cnt--) { h = targets[cnt]; // ignore drop into itself or in own location if (h != hash && files[h].phash != hash) { result.push(h); } else { ((isCopy && h !== hash && files[hash].write)? dups : faults).push(h); } } if (faults.length) { return false; } ui.helper.data('droped', true); if (dups.length) { ui.helper.hide(); self.exec('duplicate', dups, {_userAction: true}); } if (result.length) { ui.helper.hide(); self.clipboard(result, !isCopy); self.exec('paste', hash, {_userAction: true}, hash).always(function(){ self.clipboard([]); self.trigger('unlockfiles', {files : targets}); }); self.trigger('drop', {files : targets}); } } }; /** * Return true if filemanager is active * * @return Boolean **/ this.enabled = function() { return enabled && this.visible(); }; /** * Return true if filemanager is visible * * @return Boolean **/ this.visible = function() { return node[0].elfinder && node.is(':visible'); }; /** * Return file is root? * * @param Object target file object * @return Boolean */ this.isRoot = function(file) { return (file.isroot || ! file.phash)? true : false; }; /** * Return root dir hash for current working directory * * @param String target hash * @param Boolean include fake parent (optional) * @return String */ this.root = function(hash, fake) { hash = hash || cwd; var dir, i; if (! fake) { jQuery.each(self.roots, function(id, rhash) { if (hash.indexOf(id) === 0) { dir = rhash; return false; } }); if (dir) { return dir; } } dir = files[hash]; while (dir && dir.phash && (fake || ! dir.isroot)) { dir = files[dir.phash]; } if (dir) { return dir.hash; } while (i in files && files.hasOwnProperty(i)) { dir = files[i]; if (dir.mime === 'directory' && !dir.phash && dir.read) { return dir.hash; } } return ''; }; /** * Return current working directory info * * @return Object */ this.cwd = function() { return files[cwd] || {}; }; /** * Return required cwd option * * @param String option name * @param String target hash (optional) * @return mixed */ this.option = function(name, target) { var res, item; target = target || cwd; if (self.optionsByHashes[target] && typeof self.optionsByHashes[target][name] !== 'undefined') { return self.optionsByHashes[target][name]; } if (self.hasVolOptions && cwd !== target && (!(item = self.file(target)) || item.phash !== cwd)) { res = ''; jQuery.each(self.volOptions, function(id, opt) { if (target.indexOf(id) === 0) { res = opt[name] || ''; return false; } }); return res; } else { return cwdOptions[name] || ''; } }; /** * Return disabled commands by each folder * * @param Array target hashes * @return Array */ this.getDisabledCmds = function(targets, flip) { var disabled = {'hidden': true}; if (! Array.isArray(targets)) { targets = [ targets ]; } jQuery.each(targets, function(i, h) { var disCmds = self.option('disabledFlip', h); if (disCmds) { Object.assign(disabled, disCmds); } }); return flip? disabled : Object.keys(disabled); }; /** * Return file data from current dir or tree by it's hash * * @param String file hash * @return Object */ this.file = function(hash, alsoHidden) { return hash? (files[hash] || (alsoHidden? hiddenFiles[hash] : void(0))) : void(0); }; /** * Return all cached files * * @param String parent hash * @return Object */ this.files = function(phash) { var items = {}; if (phash) { if (!ownFiles[phash]) { return {}; } jQuery.each(ownFiles[phash], function(h) { if (files[h]) { items[h] = files[h]; } else { delete ownFiles[phash][h]; } }); return Object.assign({}, items); } return Object.assign({}, files); }; /** * Return list of file parents hashes include file hash * * @param String file hash * @return Array */ this.parents = function(hash) { var parents = [], dir; while (hash && (dir = this.file(hash))) { parents.unshift(dir.hash); hash = dir.phash; } return parents; }; this.path2array = function(hash, i18) { var file, path = []; while (hash) { if ((file = files[hash]) && file.hash) { path.unshift(i18 && file.i18 ? file.i18 : file.name); hash = file.isroot? null : file.phash; } else { path = []; break; } } return path; }; /** * Return file path or Get path async with jQuery.Deferred * * @param Object file * @param Boolean i18 * @param Object asyncOpt * @return String|jQuery.Deferred */ this.path = function(hash, i18, asyncOpt) { var path = files[hash] && files[hash].path ? files[hash].path : this.path2array(hash, i18).join(cwdOptions.separator); if (! asyncOpt || ! files[hash]) { return path; } else { asyncOpt = Object.assign({notify: {type : 'parents', cnt : 1, hideCnt : true}}, asyncOpt); var dfd = jQuery.Deferred(), notify = asyncOpt.notify, noreq = false, req = function() { self.request({ data : {cmd : 'parents', target : files[hash].phash}, notify : notify, preventFail : true }) .done(done) .fail(function() { dfd.reject(); }); }, done = function() { self.one('parentsdone', function() { path = self.path(hash, i18); if (path === '' && noreq) { //retry with request noreq = false; req(); } else { if (notify) { clearTimeout(ntftm); notify.cnt = -(parseInt(notify.cnt || 0)); self.notify(notify); } dfd.resolve(path); } }); }, ntftm; if (path) { return dfd.resolve(path); } else { if (self.ui['tree']) { // try as no request if (notify) { ntftm = setTimeout(function() { self.notify(notify); }, self.notifyDelay); } noreq = true; done(true); } else { req(); } return dfd; } } }; /** * Return file url if set * * @param String file hash * @param Object Options * @return String|Object of jQuery Deferred */ this.url = function(hash, o) { var file = files[hash], opts = o || {}, async = opts.async || false, temp = opts.temporary || false, onetm = (opts.onetime && self.option('onetimeUrl', hash)) || false, absurl = opts.absurl || false, dfrd = (async || onetm)? jQuery.Deferred() : null, filter = function(url) { if (url && absurl) { url = self.convAbsUrl(url); } return url; }, getUrl = function(url) { if (url) { return filter(url); } if (file.url) { return filter(file.url); } if (typeof baseUrl === 'undefined') { baseUrl = self.option('url', (!self.isRoot(file) && file.phash) || file.hash); } if (baseUrl) { return filter(baseUrl + jQuery.map(self.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/')); } var params = Object.assign({}, self.customData, { cmd: 'file', target: file.hash }); if (self.oldAPI) { params.cmd = 'open'; params.current = file.phash; } return filter(self.options.url + (self.options.url.indexOf('?') === -1 ? '?' : '&') + jQuery.param(params, true)); }, baseUrl, res; if (!file || !file.read) { return async? dfrd.resolve('') : ''; } if (onetm) { async = true; this.request({ data : { cmd : 'url', target : hash, options : { onetime: 1 } }, preventDefault : true, options: {async: async}, notify: {type : 'file', cnt : 1, hideCnt : true} }).done(function(data) { dfrd.resolve(filter(data.url || '')); }).fail(function() { dfrd.resolve(''); }); } else { if (file.url == '1' || (temp && !file.url && !(baseUrl = self.option('url', (!self.isRoot(file) && file.phash) || file.hash)))) { this.request({ data : { cmd : 'url', target : hash, options : { temporary: temp? 1 : 0 } }, preventDefault : true, options: {async: async}, notify: async? {type : temp? 'file' : 'url', cnt : 1, hideCnt : true} : {} }) .done(function(data) { file.url = data.url || ''; }) .fail(function() { file.url = ''; }) .always(function() { var url; if (file.url && temp) { url = file.url; file.url = '1'; // restore } if (async) { dfrd.resolve(getUrl(url)); } else { return getUrl(url); } }); } else { if (async) { dfrd.resolve(getUrl()); } else { return getUrl(); } } } if (async) { return dfrd; } }; /** * Return file url for the extarnal service * * @param String hash The hash * @param Object options The options * @return Object jQuery Deferred */ this.forExternalUrl = function(hash, options) { var onetime = self.option('onetimeUrl', hash), opts = { async: true, absurl: true }; opts[onetime? 'onetime' : 'temporary'] = true; return self.url(hash, Object.assign({}, options, opts)); }; /** * Return file url for open in elFinder * * @param String file hash * @param Boolean for download link * @return String */ this.openUrl = function(hash, download) { var file = files[hash], url = ''; if (!file || !file.read) { return ''; } if (!download) { if (file.url) { if (file.url != 1) { url = file.url; } } else if (cwdOptions.url && file.hash.indexOf(self.cwd().volumeid) === 0) { url = cwdOptions.url + jQuery.map(this.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/'); } if (url) { url += (url.match(/\?/)? '&' : '?') + '_'.repeat((url.match(/[\?&](_+)t=/g) || ['&t=']).sort().shift().match(/[\?&](_*)t=/)[1].length + 1) + 't=' + (file.ts || parseInt(+new Date()/1000)); return url; } } url = this.options.url; url = url + (url.indexOf('?') === -1 ? '?' : '&') + (this.oldAPI ? 'cmd=open¤t='+file.phash : 'cmd=file') + '&target=' + file.hash + '&_t=' + (file.ts || parseInt(+new Date()/1000)); if (download) { url += '&download=1'; } jQuery.each(this.customData, function(key, val) { url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(val); }); return url; }; /** * Return thumbnail url * * @param Object file object * @return String */ this.tmb = function(file) { var tmbUrl, tmbCrop, cls = 'elfinder-cwd-bgurl', url = ''; if (jQuery.isPlainObject(file)) { if (self.searchStatus.state && file.hash.indexOf(self.cwd().volumeid) !== 0) { tmbUrl = self.option('tmbUrl', file.hash); tmbCrop = self.option('tmbCrop', file.hash); } else { tmbUrl = cwdOptions['tmbUrl']; tmbCrop = cwdOptions['tmbCrop']; } if (tmbCrop) { cls += ' elfinder-cwd-bgurl-crop'; } if (tmbUrl === 'self' && file.mime.indexOf('image/') === 0) { url = self.openUrl(file.hash); cls += ' elfinder-cwd-bgself'; } else if ((self.oldAPI || tmbUrl) && file && file.tmb && file.tmb != 1) { url = tmbUrl + file.tmb; } else if (self.newAPI && file && file.tmb && file.tmb != 1) { url = file.tmb; } if (url) { if (file.ts && tmbUrl !== 'self') { url += (url.match(/\?/)? '&' : '?') + '_t=' + file.ts; } return { url: url, className: cls }; } } return false; }; /** * Return selected files hashes * * @return Array **/ this.selected = function() { return selected.slice(0); }; /** * Return selected files info * * @return Array */ this.selectedFiles = function() { return jQuery.map(selected, function(hash) { return files[hash] ? Object.assign({}, files[hash]) : null; }); }; /** * Return true if file with required name existsin required folder * * @param String file name * @param String parent folder hash * @return Boolean */ this.fileByName = function(name, phash) { var hash; for (hash in files) { if (files.hasOwnProperty(hash) && files[hash].phash == phash && files[hash].name == name) { return files[hash]; } } }; /** * Valid data for required command based on rules * * @param String command name * @param Object cammand's data * @return Boolean */ this.validResponse = function(cmd, data) { return data.error || this.rules[this.rules[cmd] ? cmd : 'defaults'](data); }; /** * Return bytes from ini formated size * * @param String ini formated size * @return Integer */ this.returnBytes = function(val) { var last; if (isNaN(val)) { if (! val) { val = ''; } // for ex. 1mb, 1KB val = val.replace(/b$/i, ''); last = val.charAt(val.length - 1).toLowerCase(); val = val.replace(/[tgmk]$/i, ''); if (last == 't') { val = val * 1024 * 1024 * 1024 * 1024; } else if (last == 'g') { val = val * 1024 * 1024 * 1024; } else if (last == 'm') { val = val * 1024 * 1024; } else if (last == 'k') { val = val * 1024; } val = isNaN(val)? 0 : parseInt(val); } else { val = parseInt(val); if (val < 1) val = 0; } return val; }; /** * Process ajax request. * Fired events : * @todo * @example * @todo * @return jQuery.Deferred */ this.request = function(opts) { var self = this, o = this.options, dfrd = jQuery.Deferred(), // request ID reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16), // request data data = Object.assign({}, self.customData, {mimes : o.onlyMimes}, opts.data || opts), // command name cmd = data.cmd, // request type is binary isBinary = (opts.options || {}).dataType === 'binary', // current cmd is "open" isOpen = (!opts.asNotOpen && cmd === 'open'), // call default fail callback (display error dialog) ? deffail = !(isBinary || opts.preventDefault || opts.preventFail), // call default success callback ? defdone = !(isBinary || opts.preventDefault || opts.preventDone), // options for notify dialog notify = Object.assign({}, opts.notify), // make cancel button cancel = !!opts.cancel, // do not normalize data - return as is raw = isBinary || !!opts.raw, // sync files on request fail syncOnFail = opts.syncOnFail, // use lazy() lazy = !!opts.lazy, // prepare function before done() prepare = opts.prepare, // navigate option object when cmd done navigate = opts.navigate, // open notify dialog timeout timeout, // use browser cache useCache = (opts.options || {}).cache, // request options options = Object.assign({ url : o.url, async : true, type : this.requestType, dataType : 'json', cache : (self.api >= 2.1029), // api >= 2.1029 has unique request ID data : data, headers : this.customHeaders, xhrFields: this.xhrFields }, opts.options || {}), /** * Default success handler. * Call default data handlers and fire event with command name. * * @param Object normalized response data * @return void **/ done = function(data) { data.warning && self.error(data.warning); if (isOpen) { open(data); } else { self.updateCache(data); } data.changed && data.changed.length && change(data.changed); self.lazy(function() { // fire some event to update cache/ui data.removed && data.removed.length && self.remove(data); data.added && data.added.length && self.add(data); data.changed && data.changed.length && self.change(data); }).then(function() { // fire event with command name return self.lazy(function() { self.trigger(cmd, data, false); }); }).then(function() { // fire event with command name + 'done' return self.lazy(function() { self.trigger(cmd + 'done'); }); }).then(function() { // make toast message if (data.toasts && Array.isArray(data.toasts)) { jQuery.each(data.toasts, function() { this.msg && self.toast(this); }); } // force update content data.sync && self.sync(); }); }, /** * Request error handler. Reject dfrd with correct error message. * * @param jqxhr request object * @param String request status * @return void **/ error = function(xhr, status) { var error, data, d = self.options.debug; switch (status) { case 'abort': error = xhr.quiet ? '' : ['errConnect', 'errAbort']; break; case 'timeout': error = ['errConnect', 'errTimeout']; break; case 'parsererror': error = ['errResponse', 'errDataNotJSON']; if (xhr.responseText) { if (! cwd || (d && (d === 'all' || d['backend-error']))) { error.push(xhr.responseText); } } break; default: if (xhr.responseText) { // check responseText, Is that JSON? try { data = JSON.parse(xhr.responseText); if (data && data.error) { error = data.error; } } catch(e) {} } if (! error) { if (xhr.status == 403) { error = ['errConnect', 'errAccess', 'HTTP error ' + xhr.status]; } else if (xhr.status == 404) { error = ['errConnect', 'errNotFound', 'HTTP error ' + xhr.status]; } else if (xhr.status >= 500) { error = ['errResponse', 'errServerError', 'HTTP error ' + xhr.status]; } else { if (xhr.status == 414 && options.type === 'get') { // retry by POST method options.type = 'post'; self.abortXHR(xhr); dfrd.xhr = xhr = self.transport.send(options).fail(error).done(success); return; } error = xhr.quiet ? '' : ['errConnect', 'HTTP error ' + xhr.status]; } } } self.trigger(cmd + 'done'); dfrd.reject({error: error}, xhr, status); }, /** * Request success handler. Valid response data and reject/resolve dfrd. * * @param Object response data * @param String request status * @return void **/ success = function(response) { var d = self.options.debug; // Set currrent request command name self.currentReqCmd = cmd; if (response.debug && (!d || d !== 'all')) { if (!d) { d = self.options.debug = {}; } d['backend-error'] = true; d['warning'] = true; } if (raw) { self.abortXHR(xhr); response && response.debug && self.debug('backend-debug', response); return dfrd.resolve(response); } if (!response) { return dfrd.reject({error :['errResponse', 'errDataEmpty']}, xhr, response); } else if (!jQuery.isPlainObject(response)) { return dfrd.reject({error :['errResponse', 'errDataNotJSON']}, xhr, response); } else if (response.error) { if (isOpen) { // check leafRoots jQuery.each(self.leafRoots, function(phash, roots) { self.leafRoots[phash] = jQuery.grep(roots, function(h) { return h !== data.target; }); }); } return dfrd.reject({error :response.error}, xhr, response); } var resolve = function() { var pushLeafRoots = function(name) { if (self.leafRoots[data.target] && response[name]) { jQuery.each(self.leafRoots[data.target], function(i, h) { var root; if (root = self.file(h)) { response[name].push(root); } }); } }, setTextMimes = function() { self.textMimes = {}; jQuery.each(self.res('mimes', 'text'), function() { self.textMimes[this.toLowerCase()] = true; }); }, actionTarget; if (isOpen) { pushLeafRoots('files'); } else if (cmd === 'tree') { pushLeafRoots('tree'); } response = self.normalize(response); if (!self.validResponse(cmd, response)) { return dfrd.reject({error :(response.norError || 'errResponse')}, xhr, response); } if (isOpen) { if (!self.api) { self.api = response.api || 1; if (self.api == '2.0' && typeof response.options.uploadMaxSize !== 'undefined') { self.api = '2.1'; } self.newAPI = self.api >= 2; self.oldAPI = !self.newAPI; } if (response.textMimes && Array.isArray(response.textMimes)) { self.resources.mimes.text = response.textMimes; setTextMimes(); } !self.textMimes && setTextMimes(); if (response.options) { cwdOptions = Object.assign({}, cwdOptionsDefault, response.options); } if (response.netDrivers) { self.netDrivers = response.netDrivers; } if (response.maxTargets) { self.maxTargets = response.maxTargets; } if (!!data.init) { self.uplMaxSize = self.returnBytes(response.uplMaxSize); self.uplMaxFile = !!response.uplMaxFile? Math.min(parseInt(response.uplMaxFile), 50) : 20; } } if (typeof prepare === 'function') { prepare(response); } if (navigate) { actionTarget = navigate.target || 'added'; if (response[actionTarget] && response[actionTarget].length) { self.one(cmd + 'done', function() { var targets = response[actionTarget], newItems = self.findCwdNodes(targets), inCwdHashes = function() { var cwdHash = self.cwd().hash; return jQuery.map(targets, function(f) { return (f.phash && cwdHash === f.phash)? f.hash : null; }); }, hashes = inCwdHashes(), makeToast = function(t) { var node = void(0), data = t.action? t.action.data : void(0), cmd, msg, done; if ((data || hashes.length) && t.action && (msg = t.action.msg) && (cmd = t.action.cmd) && (!t.action.cwdNot || t.action.cwdNot !== self.cwd().hash)) { done = t.action.done; data = t.action.data; node = jQuery('<div/>') .append( jQuery('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all elfinder-tabstop"><span class="ui-button-text">' +self.i18n(msg) +'</span></button>') .on('mouseenter mouseleave', function(e) { jQuery(this).toggleClass('ui-state-hover', e.type == 'mouseenter'); }) .on('click', function() { self.exec(cmd, data || hashes, {_userAction: true, _currentType: 'toast', _currentNode: jQuery(this) }); if (done) { self.one(cmd+'done', function() { if (typeof done === 'function') { done(); } else if (done === 'select') { self.trigger('selectfiles', {files : inCwdHashes()}); } }); } }) ); } delete t.action; t.extNode = node; return t; }; if (! navigate.toast) { navigate.toast = {}; } !navigate.noselect && self.trigger('selectfiles', {files : self.searchStatus.state > 1 ? jQuery.map(targets, function(f) { return f.hash; }) : hashes}); if (newItems.length) { if (!navigate.noscroll) { newItems.first().trigger('scrolltoview', {blink : false}); self.resources.blink(newItems, 'lookme'); } if (jQuery.isPlainObject(navigate.toast.incwd)) { self.toast(makeToast(navigate.toast.incwd)); } } else { if (jQuery.isPlainObject(navigate.toast.inbuffer)) { self.toast(makeToast(navigate.toast.inbuffer)); } } }); } } dfrd.resolve(response); response.debug && self.debug('backend-debug', response); }; self.abortXHR(xhr); lazy? self.lazy(resolve) : resolve(); }, xhr, _xhr, xhrAbort = function(e) { if (xhr && xhr.state() === 'pending') { self.abortXHR(xhr, { quiet: true , abort: true }); if (!e || (e.type !== 'unload' && e.type !== 'destroy')) { self.autoSync(); } } }, abort = function(e){ self.trigger(cmd + 'done'); if (e.type == 'autosync') { if (e.data.action != 'stop') return; } else if (e.type != 'unload' && e.type != 'destroy' && e.type != 'openxhrabort') { if (!e.data.added || !e.data.added.length) { return; } } xhrAbort(e); }, request = function(mode) { var queueAbort = function() { syncOnFail = false; dfrd.reject(); }; if (mode) { if (mode === 'cmd') { return cmd; } } if (isOpen) { if (requestQueueSkipOpen) { return dfrd.reject(); } requestQueueSkipOpen = true; } dfrd.always(function() { delete options.headers['X-elFinderReqid']; }).fail(function(error, xhr, response) { var errData = { cmd: cmd, err: error, xhr: xhr, rc: response }; // unset this cmd queue when user canceling // see notify : function - `cancel.reject(0);` if (error === 0) { if (requestQueue.length) { requestQueue = jQuery.grep(requestQueue, function(req) { return (req('cmd') === cmd) ? false : true; }); } } // trigger "requestError" event self.trigger('requestError', errData); if (errData._event && errData._event.isDefaultPrevented()) { deffail = false; syncOnFail = false; if (error) { error.error = ''; } } // abort xhr xhrAbort(); if (isOpen) { openDir = self.file(data.target); openDir && openDir.volumeid && self.isRoot(openDir) && delete self.volumeExpires[openDir.volumeid]; } self.trigger(cmd + 'fail', response); if (error) { deffail ? self.error(error) : self.debug('error', self.i18n(error)); } syncOnFail && self.sync(); }); if (!cmd) { syncOnFail = false; return dfrd.reject({error :'errCmdReq'}); } if (self.maxTargets && data.targets && data.targets.length > self.maxTargets) { syncOnFail = false; return dfrd.reject({error :['errMaxTargets', self.maxTargets]}); } defdone && dfrd.done(done); // quiet abort not completed "open" requests if (isOpen) { while ((_xhr = queue.pop())) { _xhr.queueAbort(); } if (cwd !== data.target) { while ((_xhr = cwdQueue.pop())) { _xhr.queueAbort(); } } } // trigger abort autoSync for commands to add the item if (jQuery.inArray(cmd, (self.cmdsToAdd + ' autosync').split(' ')) !== -1) { if (cmd !== 'autosync') { self.autoSync('stop'); dfrd.always(function() { self.autoSync(); }); } self.trigger('openxhrabort'); } delete options.preventFail; if (self.api >= 2.1029) { if (useCache) { options.headers['X-elFinderReqid'] = reqId; } else { Object.assign(options.data, { reqid : reqId }); } } // function for set value of this syncOnFail dfrd.syncOnFail = function(state) { syncOnFail = !!state; }; requestCnt++; dfrd.xhr = xhr = self.transport.send(options).always(function() { // set responseURL from native xhr object if (options._xhr && typeof options._xhr.responseURL !== 'undefined') { xhr.responseURL = options._xhr.responseURL || ''; } --requestCnt; if (requestQueue.length) { requestQueue.shift()(); } else { requestQueueSkipOpen = false; } }).fail(error).done(success); if (self.api >= 2.1029) { xhr._requestId = reqId; } if (isOpen || (data.compare && cmd === 'info')) { // regist function queueAbort xhr.queueAbort = queueAbort; // add autoSync xhr into queue queue.unshift(xhr); // bind abort() data.compare && self.bind(self.cmdsToAdd + ' autosync openxhrabort', abort); dfrd.always(function() { var ndx = jQuery.inArray(xhr, queue); data.compare && self.unbind(self.cmdsToAdd + ' autosync openxhrabort', abort); ndx !== -1 && queue.splice(ndx, 1); }); } else if (jQuery.inArray(cmd, self.abortCmdsOnOpen) !== -1) { // regist function queueAbort xhr.queueAbort = queueAbort; // add "open" xhr, only cwd xhr into queue cwdQueue.unshift(xhr); dfrd.always(function() { var ndx = jQuery.inArray(xhr, cwdQueue); ndx !== -1 && cwdQueue.splice(ndx, 1); }); } // abort pending xhr on window unload or elFinder destroy self.bind('unload destroy', abort); dfrd.always(function() { self.unbind('unload destroy', abort); }); return dfrd; }, queueingRequest = function() { // show notify if (notify.type && notify.cnt) { if (cancel) { notify.cancel = dfrd; opts.eachCancel && (notify.id = +new Date()); } timeout = setTimeout(function() { self.notify(notify); dfrd.always(function() { notify.cnt = -(parseInt(notify.cnt)||0); self.notify(notify); }); }, self.notifyDelay); dfrd.always(function() { clearTimeout(timeout); }); } // queueing if (isOpen) { requestQueueSkipOpen = false; } if (requestCnt < requestMaxConn) { // do request return request(); } else { if (isOpen) { requestQueue.unshift(request); } else { requestQueue.push(request); } return dfrd; } }, bindData = {opts: opts, result: true}, openDir; // prevent request initial request is completed if (!self.api && !data.init) { syncOnFail = false; return dfrd.reject(); } // trigger "request.cmd" that callback be able to cancel request by substituting "false" for "event.data.result" self.trigger('request.' + cmd, bindData, true); if (! bindData.result) { self.trigger(cmd + 'done'); return dfrd.reject(); } else if (typeof bindData.result === 'object' && bindData.result.promise) { bindData.result .done(queueingRequest) .fail(function() { self.trigger(cmd + 'done'); dfrd.reject(); }); return dfrd; } return queueingRequest(); }; /** * Call cache() * Store info about files/dirs in "files" object. * * @param Array files * @return void */ this.cache = function(dataArray) { if (! Array.isArray(dataArray)) { dataArray = [ dataArray ]; } cache(dataArray); }; /** * Update file object caches by respose data object * * @param Object respose data object * @return void */ this.updateCache = function(data) { if (jQuery.isPlainObject(data)) { data.files && data.files.length && cache(data.files, 'files'); data.tree && data.tree.length && cache(data.tree, 'tree'); data.removed && data.removed.length && remove(data.removed); data.added && data.added.length && cache(data.added, 'add'); data.changed && data.changed.length && change(data.changed, 'change'); } }; /** * Compare current files cache with new files and return diff * * @param Array new files * @param String target folder hash * @param Array exclude properties to compare * @return Object */ this.diff = function(incoming, onlydir, excludeProps) { var raw = {}, added = [], removed = [], changed = [], excludes = null, isChanged = function(hash) { var l = changed.length; while (l--) { if (changed[l].hash == hash) { return true; } } }; jQuery.each(incoming, function(i, f) { raw[f.hash] = f; }); // make excludes object if (excludeProps && excludeProps.length) { excludes = {}; jQuery.each(excludeProps, function() { excludes[this] = true; }); } // find removed jQuery.each(files, function(hash, f) { if (! raw[hash] && (! onlydir || f.phash === onlydir)) { removed.push(hash); } }); // compare files jQuery.each(raw, function(hash, file) { var origin = files[hash], orgKeys = {}, chkKeyLen; if (!origin) { added.push(file); } else { // make orgKeys object jQuery.each(Object.keys(origin), function() { orgKeys[this] = true; }); jQuery.each(file, function(prop) { delete orgKeys[prop]; if (! excludes || ! excludes[prop]) { if (file[prop] !== origin[prop]) { changed.push(file); orgKeys = {}; return false; } } }); chkKeyLen = Object.keys(orgKeys).length; if (chkKeyLen !== 0) { if (excludes) { jQuery.each(orgKeys, function(prop) { if (excludes[prop]) { --chkKeyLen; } }); } (chkKeyLen !== 0) && changed.push(file); } } }); // parents of removed dirs mark as changed (required for tree correct work) jQuery.each(removed, function(i, hash) { var file = files[hash], phash = file.phash; if (phash && file.mime == 'directory' && jQuery.inArray(phash, removed) === -1 && raw[phash] && !isChanged(phash)) { changed.push(raw[phash]); } }); return { added : added, removed : removed, changed : changed }; }; /** * Sync content * * @return jQuery.Deferred */ this.sync = function(onlydir, polling) { this.autoSync('stop'); var self = this, compare = function(){ var c = '', cnt = 0, mtime = 0; if (onlydir && polling) { jQuery.each(files, function(h, f) { if (f.phash && f.phash === onlydir) { ++cnt; mtime = Math.max(mtime, f.ts); } c = cnt+':'+mtime; }); } return c; }, comp = compare(), dfrd = jQuery.Deferred().done(function() { self.trigger('sync'); }), opts = [this.request({ data : {cmd : 'open', reload : 1, target : cwd, tree : (! onlydir && this.ui.tree) ? 1 : 0, compare : comp}, preventDefault : true })], exParents = function() { var parents = [], curRoot = self.file(self.root(cwd)), curId = curRoot? curRoot.volumeid : null, phash = self.cwd().phash, isroot,pdir; while(phash) { if (pdir = self.file(phash)) { if (phash.indexOf(curId) !== 0) { parents.push( {target: phash, cmd: 'tree'} ); if (! self.isRoot(pdir)) { parents.push( {target: phash, cmd: 'parents'} ); } curRoot = self.file(self.root(phash)); curId = curRoot? curRoot.volumeid : null; } phash = pdir.phash; } else { phash = null; } } return parents; }; if (! onlydir && self.api >= 2) { (cwd !== this.root()) && opts.push(this.request({ data : {cmd : 'parents', target : cwd}, preventDefault : true })); jQuery.each(exParents(), function(i, data) { opts.push(self.request({ data : {cmd : data.cmd, target : data.target}, preventDefault : true })); }); } jQuery.when.apply($, opts) .fail(function(error, xhr) { if (! polling || jQuery.inArray('errOpen', error) !== -1) { dfrd.reject(error); self.parseError(error) && self.request({ data : {cmd : 'open', target : (self.lastDir('') || self.root()), tree : 1, init : 1}, notify : {type : 'open', cnt : 1, hideCnt : true} }); } else { dfrd.reject((error && xhr.status != 0)? error : void 0); } }) .done(function(odata) { var pdata, argLen, i; if (odata.cwd.compare) { if (comp === odata.cwd.compare) { return dfrd.reject(); } } // for 2nd and more requests pdata = {tree : []}; // results marge of 2nd and more requests argLen = arguments.length; if (argLen > 1) { for(i = 1; i < argLen; i++) { if (arguments[i].tree && arguments[i].tree.length) { pdata.tree.push.apply(pdata.tree, arguments[i].tree); } } } if (self.api < 2.1) { if (! pdata.tree) { pdata.tree = []; } pdata.tree.push(odata.cwd); } // data normalize odata = self.normalize(odata); if (!self.validResponse('open', odata)) { return dfrd.reject((odata.norError || 'errResponse')); } pdata = self.normalize(pdata); if (!self.validResponse('tree', pdata)) { return dfrd.reject((pdata.norError || 'errResponse')); } var diff = self.diff(odata.files.concat(pdata && pdata.tree ? pdata.tree : []), onlydir); diff.added.push(odata.cwd); self.updateCache(diff); // trigger events diff.removed.length && self.remove(diff); diff.added.length && self.add(diff); diff.changed.length && self.change(diff); return dfrd.resolve(diff); }) .always(function() { self.autoSync(); }); return dfrd; }; this.upload = function(files) { return this.transport.upload(files, this); }; /** * Bind keybord shortcut to keydown event * * @example * elfinder.shortcut({ * pattern : 'ctrl+a', * description : 'Select all files', * callback : function(e) { ... }, * keypress : true|false (bind to keypress instead of keydown) * }) * * @param Object shortcut config * @return elFinder */ this.shortcut = function(s) { var patterns, pattern, code, i, parts; if (this.options.allowShortcuts && s.pattern && jQuery.isFunction(s.callback)) { patterns = s.pattern.toUpperCase().split(/\s+/); for (i= 0; i < patterns.length; i++) { pattern = patterns[i]; parts = pattern.split('+'); code = (code = parts.pop()).length == 1 ? (code > 0 ? code : code.charCodeAt(0)) : (code > 0 ? code : jQuery.ui.keyCode[code]); if (code && !shortcuts[pattern]) { shortcuts[pattern] = { keyCode : code, altKey : jQuery.inArray('ALT', parts) != -1, ctrlKey : jQuery.inArray('CTRL', parts) != -1, shiftKey : jQuery.inArray('SHIFT', parts) != -1, type : s.type || 'keydown', callback : s.callback, description : s.description, pattern : pattern }; } } } return this; }; /** * Registered shortcuts * * @type Object **/ this.shortcuts = function() { var ret = []; jQuery.each(shortcuts, function(i, s) { ret.push([s.pattern, self.i18n(s.description)]); }); return ret; }; /** * Get/set clipboard content. * Return new clipboard content. * * @example * this.clipboard([]) - clean clipboard * this.clipboard([{...}, {...}], true) - put 2 files in clipboard and mark it as cutted * * @param Array new files hashes * @param Boolean cut files? * @return Array */ this.clipboard = function(hashes, cut) { var map = function() { return jQuery.map(clipboard, function(f) { return f.hash; }); }; if (hashes !== void(0)) { clipboard.length && this.trigger('unlockfiles', {files : map()}); remember = {}; clipboard = jQuery.map(hashes||[], function(hash) { var file = files[hash]; if (file) { remember[hash] = true; return { hash : hash, phash : file.phash, name : file.name, mime : file.mime, read : file.read, locked : file.locked, cut : !!cut }; } return null; }); this.trigger('changeclipboard', {clipboard : clipboard.slice(0, clipboard.length)}); cut && this.trigger('lockfiles', {files : map()}); } // return copy of clipboard instead of refrence return clipboard.slice(0, clipboard.length); }; /** * Return true if command enabled * * @param String command name * @param String|void hash for check of own volume's disabled cmds * @return Boolean */ this.isCommandEnabled = function(name, dstHash) { var disabled, cmd, cvid = self.cwd().volumeid || ''; // In serach results use selected item hash to check if (!dstHash && self.searchStatus.state > 1 && self.selected().length) { dstHash = self.selected()[0]; } if (dstHash && (! cvid || dstHash.indexOf(cvid) !== 0)) { disabled = self.option('disabledFlip', dstHash); //if (! disabled) { // disabled = {}; //} } else { disabled = cwdOptions.disabledFlip/* || {}*/; } cmd = this._commands[name]; return cmd ? (cmd.alwaysEnabled || !disabled[name]) : false; }; /** * Exec command and return result; * * @param String command name * @param String|Array usualy files hashes * @param String|Array command options * @param String|void hash for enabled check of own volume's disabled cmds * @return jQuery.Deferred */ this.exec = function(cmd, files, opts, dstHash) { var dfrd, resType; // apply commandMap for keyboard shortcut if (!dstHash && this.commandMap[cmd] && this.commandMap[cmd] !== 'hidden') { cmd = this.commandMap[cmd]; } if (cmd === 'open') { if (this.searchStatus.state || this.searchStatus.ininc) { this.trigger('searchend', { noupdate: true }); } this.autoSync('stop'); } if (!dstHash && files) { if (jQuery.isArray(files)) { if (files.length) { dstHash = files[0]; } } else { dstHash = files; } } dfrd = this._commands[cmd] && this.isCommandEnabled(cmd, dstHash) ? this._commands[cmd].exec(files, opts) : jQuery.Deferred().reject('No such command'); resType = typeof dfrd; if (!(resType === 'object' && dfrd.promise)) { self.debug('warning', '"cmd.exec()" should be returned "jQuery.Deferred" but cmd "' + cmd + '" returned "' + resType + '"'); dfrd = jQuery.Deferred().resolve(); } this.trigger('exec', { dfrd : dfrd, cmd : cmd, files : files, opts : opts, dstHash : dstHash }); return dfrd; }; /** * Create and return dialog. * * @param String|DOMElement dialog content * @param Object dialog options * @return jQuery */ this.dialog = function(content, options) { var dialog = jQuery('<div/>').append(content).appendTo(node).elfinderdialog(options, self), dnode = dialog.closest('.ui-dialog'), resize = function(){ ! dialog.data('draged') && dialog.is(':visible') && dialog.elfinderdialog('posInit'); }; if (dnode.length) { self.bind('resize', resize); dnode.on('remove', function() { self.unbind('resize', resize); }); } return dialog; }; /** * Create and return toast. * * @param Object toast options - see ui/toast.js * @return jQuery */ this.toast = function(options) { return jQuery('<div class="ui-front"/>').appendTo(this.ui.toast).elfindertoast(options || {}, this); }; /** * Return UI widget or node * * @param String ui name * @return jQuery */ this.getUI = function(ui) { return this.ui[ui] || (ui? jQuery() : node); }; /** * Return elFinder.command instance or instances array * * @param String command name * @return Object | Array */ this.getCommand = function(name) { return name === void(0) ? this._commands : this._commands[name]; }; /** * Resize elfinder node * * @param String|Number width * @param String|Number height * @return void */ this.resize = function(w, h) { var getMargin = function() { var m = node.outerHeight(true) - node.innerHeight(), p = node; while(p.get(0) !== heightBase.get(0)) { p = p.parent(); m += p.outerHeight(true) - p.innerHeight(); if (! p.parent().length) { // reached the document break; } } return m; }, fit = ! node.hasClass('ui-resizable'), prv = node.data('resizeSize') || {w: 0, h: 0}, mt, size = {}; if (heightBase && heightBase.data('resizeTm')) { clearTimeout(heightBase.data('resizeTm')); } if (typeof h === 'string') { if (mt = h.match(/^([0-9.]+)%$/)) { // setup heightBase if (! heightBase || ! heightBase.length) { heightBase = jQuery(window); } if (! heightBase.data('marginToMyNode')) { heightBase.data('marginToMyNode', getMargin()); } if (! heightBase.data('fitToBaseFunc')) { heightBase.data('fitToBaseFunc', function(e) { var tm = heightBase.data('resizeTm'); e.preventDefault(); e.stopPropagation(); tm && cancelAnimationFrame(tm); if (! node.hasClass('elfinder-fullscreen') && (!self.UA.Mobile || heightBase.data('rotated') !== self.UA.Rotated)) { heightBase.data('rotated', self.UA.Rotated); heightBase.data('resizeTm', requestAnimationFrame(function() { self.restoreSize(); })); } }); } if (typeof heightBase.data('rotated') === 'undefined') { heightBase.data('rotated', self.UA.Rotated); } h = heightBase.height() * (mt[1] / 100) - heightBase.data('marginToMyNode'); heightBase.off('resize.' + self.namespace, heightBase.data('fitToBaseFunc')); fit && heightBase.on('resize.' + self.namespace, heightBase.data('fitToBaseFunc')); } } node.css({ width : w, height : parseInt(h) }); size.w = Math.round(node.width()); size.h = Math.round(node.height()); node.data('resizeSize', size); if (size.w !== prv.w || size.h !== prv.h) { node.trigger('resize'); this.trigger('resize', {width : size.w, height : size.h}); } }; /** * Restore elfinder node size * * @return elFinder */ this.restoreSize = function() { this.resize(width, height); }; this.show = function() { node.show(); this.enable().trigger('show'); }; this.hide = function() { if (this.options.enableAlways) { prevEnabled = enabled; enabled = false; } this.disable(); this.trigger('hide'); node.hide(); }; /** * Lazy execution function * * @param Object function * @param Number delay * @param Object options * @return Object jQuery.Deferred */ this.lazy = function(func, delay, opts) { var busy = function(state) { var cnt = node.data('lazycnt'), repaint; if (state) { repaint = node.data('lazyrepaint')? false : opts.repaint; if (! cnt) { node.data('lazycnt', 1) .addClass('elfinder-processing'); } else { node.data('lazycnt', ++cnt); } if (repaint) { node.data('lazyrepaint', true).css('display'); // force repaint } } else { if (cnt && cnt > 1) { node.data('lazycnt', --cnt); } else { repaint = node.data('lazyrepaint'); node.data('lazycnt', 0) .removeData('lazyrepaint') .removeClass('elfinder-processing'); repaint && node.css('display'); // force repaint; self.trigger('lazydone'); } } }, dfd = jQuery.Deferred(), callFunc = function() { dfd.resolve(func.call(dfd)); busy(false); }; delay = delay || 0; opts = opts || {}; busy(true); if (delay) { setTimeout(callFunc, delay); } else { requestAnimationFrame(callFunc); } return dfd; }; /** * Destroy this elFinder instance * * @return void **/ this.destroy = function() { if (node && node[0].elfinder) { node.hasClass('elfinder-fullscreen') && self.toggleFullscreen(node); this.options.syncStart = false; this.autoSync('forcestop'); this.trigger('destroy').disable(); clipboard = []; selected = []; listeners = {}; shortcuts = {}; jQuery(window).off('.' + namespace); jQuery(document).off('.' + namespace); self.trigger = function(){}; jQuery(beeper).remove(); node.off() .removeData() .empty() .append(prevContent.contents()) .attr('class', prevContent.attr('class')) .attr('style', prevContent.attr('style')); delete node[0].elfinder; // restore kept events jQuery.each(prevEvents, function(n, arr) { jQuery.each(arr, function(i, o) { node.on(o.type + (o.namespace? '.'+o.namespace : ''), o.selector, o.handler); }); }); } }; /** * Start or stop auto sync * * @param String|Bool stop * @return void */ this.autoSync = function(mode) { var sync; if (self.options.sync >= 1000) { if (syncInterval) { clearTimeout(syncInterval); syncInterval = null; self.trigger('autosync', {action : 'stop'}); } if (mode === 'stop') { ++autoSyncStop; } else { autoSyncStop = Math.max(0, --autoSyncStop); } if (autoSyncStop || mode === 'forcestop' || ! self.options.syncStart) { return; } // run interval sync sync = function(start){ var timeout; if (cwdOptions.syncMinMs && (start || syncInterval)) { start && self.trigger('autosync', {action : 'start'}); timeout = Math.max(self.options.sync, cwdOptions.syncMinMs); syncInterval && clearTimeout(syncInterval); syncInterval = setTimeout(function() { var dosync = true, hash = cwd, cts; if (cwdOptions.syncChkAsTs && files[hash] && (cts = files[hash].ts)) { self.request({ data : {cmd : 'info', targets : [hash], compare : cts, reload : 1}, preventDefault : true }) .done(function(data){ var ts; dosync = true; if (data.compare) { ts = data.compare; if (ts == cts) { dosync = false; } } if (dosync) { self.sync(hash).always(function(){ if (ts) { // update ts for cache clear etc. files[hash].ts = ts; } sync(); }); } else { sync(); } }) .fail(function(error, xhr){ var err = self.parseError(error); if (err && xhr.status != 0) { self.error(err); if (Array.isArray(err) && jQuery.inArray('errOpen', err) !== -1) { self.request({ data : {cmd : 'open', target : (self.lastDir('') || self.root()), tree : 1, init : 1}, notify : {type : 'open', cnt : 1, hideCnt : true} }); } } else { syncInterval = setTimeout(function() { sync(); }, timeout); } }); } else { self.sync(cwd, true).always(function(){ sync(); }); } }, timeout); } }; sync(true); } }; /** * Return bool is inside work zone of specific point * * @param Number event.pageX * @param Number event.pageY * @return Bool */ this.insideWorkzone = function(x, y, margin) { var rectangle = this.getUI('workzone').data('rectangle'); margin = margin || 1; if (x < rectangle.left + margin || x > rectangle.left + rectangle.width + margin || y < rectangle.top + margin || y > rectangle.top + rectangle.height + margin) { return false; } return true; }; /** * Target ui node move to last of children of elFinder node fot to show front * * @param Object target Target jQuery node object */ this.toFront = function(target) { var nodes = node.children('.ui-front').removeClass('elfinder-frontmost'), lastnode = nodes.last(); nodes.css('z-index', ''); jQuery(target).addClass('ui-front elfinder-frontmost').css('z-index', lastnode.css('z-index') + 1); }; /** * Remove class 'elfinder-frontmost' and hide() to target ui node * * @param Object target Target jQuery node object * @param Boolean nohide Do not hide */ this.toHide =function(target, nohide) { var tgt = jQuery(target), last; !nohide && tgt.hide(); if (tgt.hasClass('elfinder-frontmost')) { tgt.removeClass('elfinder-frontmost'); last = node.children('.ui-front:visible:not(.elfinder-frontmost)').last(); if (last.length) { requestAnimationFrame(function() { if (!node.children('.elfinder-frontmost:visible').length) { self.toFront(last); last.trigger('frontmost'); } }); } } }; /** * Return css object for maximize * * @return Object */ this.getMaximizeCss = function() { return { width : '100%', height : '100%', margin : 0, top : 0, left : 0, display : 'block', position: 'fixed', zIndex : Math.max(self.zIndex? (self.zIndex + 1) : 0 , 1000), maxWidth : '', maxHeight: '' }; }; // Closure for togglefullscreen (function() { // check is in iframe if (inFrame && self.UA.Fullscreen) { self.UA.Fullscreen = false; if (parentIframe && typeof parentIframe.attr('allowfullscreen') !== 'undefined') { self.UA.Fullscreen = true; } } var orgStyle, bodyOvf, resizeTm, fullElm, exitFull, toFull, cls = 'elfinder-fullscreen', clsN = 'elfinder-fullscreen-native', checkDialog = function() { var t = 0, l = 0; jQuery.each(node.children('.ui-dialog,.ui-draggable'), function(i, d) { var $d = jQuery(d), pos = $d.position(); if (pos.top < 0) { $d.css('top', t); t += 20; } if (pos.left < 0) { $d.css('left', l); l += 20; } }); }, funcObj = self.UA.Fullscreen? { // native full screen mode fullElm: function() { return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement || null; }, exitFull: function() { if (document.exitFullscreen) { return document.exitFullscreen(); } else if (document.webkitExitFullscreen) { return document.webkitExitFullscreen(); } else if (document.mozCancelFullScreen) { return document.mozCancelFullScreen(); } else if (document.msExitFullscreen) { return document.msExitFullscreen(); } }, toFull: function(elem) { if (elem.requestFullscreen) { return elem.requestFullscreen(); } else if (elem.webkitRequestFullscreen) { return elem.webkitRequestFullscreen(); } else if (elem.mozRequestFullScreen) { return elem.mozRequestFullScreen(); } else if (elem.msRequestFullscreen) { return elem.msRequestFullscreen(); } return false; } } : { // node element maximize mode fullElm: function() { var full; if (node.hasClass(cls)) { return node.get(0); } else { full = node.find('.' + cls); if (full.length) { return full.get(0); } } return null; }, exitFull: function() { var elm; jQuery(window).off('resize.' + namespace, resize); if (bodyOvf !== void(0)) { jQuery('body').css('overflow', bodyOvf); } bodyOvf = void(0); if (orgStyle) { elm = orgStyle.elm; restoreStyle(elm); jQuery(elm).trigger('resize', {fullscreen: 'off'}); } jQuery(window).trigger('resize'); }, toFull: function(elem) { bodyOvf = jQuery('body').css('overflow') || ''; jQuery('body').css('overflow', 'hidden'); jQuery(elem).css(self.getMaximizeCss()) .addClass(cls) .trigger('resize', {fullscreen: 'on'}); checkDialog(); jQuery(window).on('resize.' + namespace, resize).trigger('resize'); return true; } }, restoreStyle = function(elem) { if (orgStyle && orgStyle.elm == elem) { jQuery(elem).removeClass(cls + ' ' + clsN).attr('style', orgStyle.style); orgStyle = null; } }, resize = function(e) { var elm; if (e.target === window) { resizeTm && cancelAnimationFrame(resizeTm); resizeTm = requestAnimationFrame(function() { if (elm = funcObj.fullElm()) { jQuery(elm).trigger('resize', {fullscreen: 'on'}); } }); } }; jQuery(document).on('fullscreenchange.' + namespace + ' webkitfullscreenchange.' + namespace + ' mozfullscreenchange.' + namespace + ' MSFullscreenChange.' + namespace, function(e){ if (self.UA.Fullscreen) { var elm = funcObj.fullElm(), win = jQuery(window); resizeTm && cancelAnimationFrame(resizeTm); if (elm === null) { win.off('resize.' + namespace, resize); if (orgStyle) { elm = orgStyle.elm; restoreStyle(elm); jQuery(elm).trigger('resize', {fullscreen: 'off'}); } } else { jQuery(elm).addClass(cls + ' ' + clsN) .attr('style', 'width:100%; height:100%; margin:0; padding:0;') .trigger('resize', {fullscreen: 'on'}); win.on('resize.' + namespace, resize); checkDialog(); } win.trigger('resize'); } }); /** * Toggle Full Scrren Mode * * @param Object target * @param Bool full * @return Object | Null DOM node object of current full scrren */ self.toggleFullscreen = function(target, full) { var elm = jQuery(target).get(0), curElm = null; curElm = funcObj.fullElm(); if (curElm) { if (curElm == elm) { if (full === true) { return curElm; } } else { if (full === false) { return curElm; } } funcObj.exitFull(); return null; } else { if (full === false) { return null; } } orgStyle = {elm: elm, style: jQuery(elm).attr('style')}; if (funcObj.toFull(elm) !== false) { return elm; } else { orgStyle = null; return null; } }; })(); // Closure for toggleMaximize (function(){ var cls = 'elfinder-maximized', resizeTm, resize = function(e) { if (e.target === window && e.data && e.data.elm) { var elm = e.data.elm; resizeTm && cancelAnimationFrame(resizeTm); resizeTm = requestAnimationFrame(function() { elm.trigger('resize', {maximize: 'on'}); }); } }, exitMax = function(elm) { jQuery(window).off('resize.' + namespace, resize); jQuery('body').css('overflow', elm.data('bodyOvf')); elm.removeClass(cls) .attr('style', elm.data('orgStyle')) .removeData('bodyOvf') .removeData('orgStyle'); elm.trigger('resize', {maximize: 'off'}); }, toMax = function(elm) { elm.data('bodyOvf', jQuery('body').css('overflow') || '') .data('orgStyle', elm.attr('style')) .addClass(cls) .css(self.getMaximizeCss()); jQuery('body').css('overflow', 'hidden'); jQuery(window).on('resize.' + namespace, {elm: elm}, resize); elm.trigger('resize', {maximize: 'on'}); }; /** * Toggle Maximize target node * * @param Object target * @param Bool max * @return void */ self.toggleMaximize = function(target, max) { var elm = jQuery(target), maximized = elm.hasClass(cls); if (maximized) { if (max === true) { return; } exitMax(elm); } else { if (max === false) { return; } toMax(elm); } }; })(); /************* init stuffs ****************/ Object.assign(jQuery.ui.keyCode, { 'F1' : 112, 'F2' : 113, 'F3' : 114, 'F4' : 115, 'F5' : 116, 'F6' : 117, 'F7' : 118, 'F8' : 119, 'F9' : 120, 'F10' : 121, 'F11' : 122, 'F12' : 123, 'DIG0' : 48, 'DIG1' : 49, 'DIG2' : 50, 'DIG3' : 51, 'DIG4' : 52, 'DIG5' : 53, 'DIG6' : 54, 'DIG7' : 55, 'DIG8' : 56, 'DIG9' : 57, 'NUM0' : 96, 'NUM1' : 97, 'NUM2' : 98, 'NUM3' : 99, 'NUM4' : 100, 'NUM5' : 101, 'NUM6' : 102, 'NUM7' : 103, 'NUM8' : 104, 'NUM9' : 105, 'CONTEXTMENU' : 93, 'DOT' : 190 }); this.dragUpload = false; this.xhrUpload = (typeof XMLHttpRequestUpload != 'undefined' || typeof XMLHttpRequestEventTarget != 'undefined') && typeof File != 'undefined' && typeof FormData != 'undefined'; // configure transport object this.transport = {}; if (typeof(this.options.transport) == 'object') { this.transport = this.options.transport; if (typeof(this.transport.init) == 'function') { this.transport.init(this); } } if (typeof(this.transport.send) != 'function') { this.transport.send = function(opts) { if (!self.UA.IE) { // keep native xhr object for handling property responseURL opts._xhr = new XMLHttpRequest(); opts.xhr = function() { return opts._xhr; }; } return jQuery.ajax(opts); }; } if (this.transport.upload == 'iframe') { this.transport.upload = jQuery.proxy(this.uploads.iframe, this); } else if (typeof(this.transport.upload) == 'function') { this.dragUpload = !!this.options.dragUploadAllow; } else if (this.xhrUpload && !!this.options.dragUploadAllow) { this.transport.upload = jQuery.proxy(this.uploads.xhr, this); this.dragUpload = true; } else { this.transport.upload = jQuery.proxy(this.uploads.iframe, this); } /** * Decoding 'raw' string converted to unicode * * @param String str * @return String */ this.decodeRawString = function(str) { var charCodes = function(str) { var i, len, arr; for (i=0,len=str.length,arr=[]; i<len; i++) { arr.push(str.charCodeAt(i)); } return arr; }, scalarValues = function(arr) { var scalars = [], i, len, c; if (typeof arr === 'string') {arr = charCodes(arr);} for (i=0,len=arr.length; c=arr[i],i<len; i++) { if (c >= 0xd800 && c <= 0xdbff) { scalars.push((c & 1023) + 64 << 10 | arr[++i] & 1023); } else { scalars.push(c); } } return scalars; }, decodeUTF8 = function(arr) { var i, len, c, str, char = String.fromCharCode; for (i=0,len=arr.length,str=""; c=arr[i],i<len; i++) { if (c <= 0x7f) { str += char(c); } else if (c <= 0xdf && c >= 0xc2) { str += char((c&31)<<6 | arr[++i]&63); } else if (c <= 0xef && c >= 0xe0) { str += char((c&15)<<12 | (arr[++i]&63)<<6 | arr[++i]&63); } else if (c <= 0xf7 && c >= 0xf0) { str += char( 0xd800 | ((c&7)<<8 | (arr[++i]&63)<<2 | arr[++i]>>>4&3) - 64, 0xdc00 | (arr[i++]&15)<<6 | arr[i]&63 ); } else { str += char(0xfffd); } } return str; }; return decodeUTF8(scalarValues(str)); }; /** * Gets target file contents by file.hash * * @param String hash The hash * @param String responseType 'blob' or 'arraybuffer' (default) * @return arraybuffer|blob The contents. */ this.getContents = function(hash, responseType) { var self = this, dfd = jQuery.Deferred(), type = responseType || 'arraybuffer', url, req; dfd.fail(function() { req && req.state() === 'pending' && req.reject(); }); url = self.openUrl(hash); if (!self.isSameOrigin(url)) { url = self.openUrl(hash, true); } req = self.request({ data : {cmd : 'get'}, options : { url: url, type: 'get', cache : true, dataType : 'binary', responseType : type, processData: false } }) .fail(function() { dfd.reject(); }) .done(function(data) { dfd.resolve(data); }); return dfd; }; this.getMimetype = function(name, orgMime) { var mime = orgMime, ext, m; m = (name + '').match(/\.([^.]+)$/); if (m && (ext = m[1])) { if (!extToMimeTable) { extToMimeTable = self.arrayFlip(self.mimeTypes); } if (!(mime = extToMimeTable[ext.toLowerCase()])) { mime = orgMime; } } return mime; }; /** * Supported check hash algorisms * * @type Array */ self.hashCheckers = []; /** * Closure of getContentsHashes() */ (function(self) { var hashLibs = { check : true }, md5Calc = function(arr) { var spark = new hashLibs.SparkMD5.ArrayBuffer(), job; job = self.asyncJob(function(buf) { spark.append(buf); }, arr).done(function() { job._md5 = spark.end(); }); return job; }, shaCalc = function(arr, length) { var sha, job; try { sha = new hashLibs.jsSHA('SHA' + (length.substr(0, 1) === '3'? length : ('-' + length)), 'ARRAYBUFFER'); job = self.asyncJob(function(buf) { sha.update(buf); }, arr).done(function() { job._sha = sha.getHash('HEX'); }); } catch(e) { job = jQuery.Deferred.reject(); } return job; }; // make fm.hashCheckers if (self.options.cdns.sparkmd5) { self.hashCheckers.push('md5'); } if (self.options.cdns.jssha) { self.hashCheckers = self.hashCheckers.concat(['sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'shake128', 'shake256']); } /** * Gets the contents hashes. * * @param String target target file.hash * @param Object needHashes need hash lib names * @return Object hashes with lib name as key */ self.getContentsHashes = function(target, needHashes) { var dfd = jQuery.Deferred(), needs = self.arrayFlip(needHashes || ['md5'], true), libs = [], jobs = [], res = {}, req; dfd.fail(function() { req && req.reject(); }); if (hashLibs.check) { delete hashLibs.check; // load SparkMD5 var libsmd5 = jQuery.Deferred(); if (window.ArrayBuffer && self.options.cdns.sparkmd5) { libs.push(libsmd5); self.loadScript([self.options.cdns.sparkmd5], function(res) { var SparkMD5 = res || window.SparkMD5; window.SparkMD5 && delete window.SparkMD5; libsmd5.resolve(); if (SparkMD5) { hashLibs.SparkMD5 = SparkMD5; } }, { tryRequire: true, error: function() { libsmd5.reject(); } } ); } // load jsSha var libssha = jQuery.Deferred(); if (window.ArrayBuffer && self.options.cdns.jssha) { libs.push(libssha); self.loadScript([self.options.cdns.jssha], function(res) { var jsSHA = res || window.jsSHA; window.jsSHA && delete window.jsSHA; libssha.resolve(); if (jsSHA) { hashLibs.jsSHA = jsSHA; } }, { tryRequire: true, error: function() { libssha.reject(); } } ); } } jQuery.when.apply(null, libs).always(function() { if (Object.keys(hashLibs).length) { req = self.getContents(target).done(function(arrayBuffer) { var arr = (arrayBuffer instanceof ArrayBuffer && arrayBuffer.byteLength > 0)? self.sliceArrayBuffer(arrayBuffer, 1048576) : false, i; if (needs.md5 && hashLibs.SparkMD5) { jobs.push(function() { var job = md5Calc(arr).done(function() { var f; res.md5 = job._md5; if (f = self.file(target)) { f.md5 = job._md5; } dfd.notify(res); }); dfd.fail(function() { job.reject(); }); return job; }); } if (hashLibs.jsSHA) { jQuery.each(['1', '224', '256', '384', '512', '3-224', '3-256', '3-384', '3-512', 'ke128', 'ke256'], function(i, v) { if (needs['sha' + v]) { jobs.push(function() { var job = shaCalc(arr, v).done(function() { var f; res['sha' + v] = job._sha; if (f = self.file(target)) { f['sha' + v] = job._sha; } dfd.notify(res); }); return job; }); } }); } if (jobs.length) { self.sequence(jobs).always(function() { dfd.resolve(res); }); } else { dfd.reject(); } }).fail(function() { dfd.reject(); }); } else { dfd.reject(); } }); return dfd; }; })(this); /** * Parse error value to display * * @param Mixed error * @return Mixed parsed error */ this.parseError = function(error) { var arg = error; if (jQuery.isPlainObject(arg)) { arg = arg.error; } return arg; }; /** * Alias for this.trigger('error', {error : 'message'}) * * @param String error message * @return elFinder **/ this.error = function() { var arg = arguments[0], opts = arguments[1] || null, err; if (arguments.length == 1 && typeof(arg) === 'function') { return self.bind('error', arg); } else { err = this.parseError(arg); return (err === true || !err)? this : self.trigger('error', {error: err, opts : opts}); } }; // create bind/trigger aliases for build-in events jQuery.each(events, function(i, name) { self[name] = function() { var arg = arguments[0]; return arguments.length == 1 && typeof(arg) == 'function' ? self.bind(name, arg) : self.trigger(name, jQuery.isPlainObject(arg) ? arg : {}); }; }); // bind core event handlers this .enable(function() { if (!enabled && self.api && self.visible() && self.ui.overlay.is(':hidden') && ! node.children('.elfinder-dialog.' + self.res('class', 'editing') + ':visible').length) { enabled = true; document.activeElement && document.activeElement.blur(); node.removeClass('elfinder-disabled'); } }) .disable(function() { prevEnabled = enabled; enabled = false; node.addClass('elfinder-disabled'); }) .open(function() { selected = []; }) .select(function(e) { var cnt = 0, unselects = []; selected = jQuery.grep(e.data.selected || e.data.value|| [], function(hash) { if (unselects.length || (self.maxTargets && ++cnt > self.maxTargets)) { unselects.push(hash); return false; } else { return files[hash] ? true : false; } }); if (unselects.length) { self.trigger('unselectfiles', {files: unselects, inselect: true}); self.toast({mode: 'warning', msg: self.i18n(['errMaxTargets', self.maxTargets])}); } }) .error(function(e) { var opts = { cssClass : 'elfinder-dialog-error', title : self.i18n('error'), resizable : false, destroyOnClose : true, buttons : {} }, node = self.getUI(), cnt = node.children('.elfinder-dialog-error').length, last, counter; if (cnt < self.options.maxErrorDialogs) { opts.buttons[self.i18n(self.i18n('btnClose'))] = function() { jQuery(this).elfinderdialog('close'); }; if (e.data.opts && jQuery.isPlainObject(e.data.opts)) { Object.assign(opts, e.data.opts); } self.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-error"/>'+self.i18n(e.data.error), opts); } else { last = node.children('.elfinder-dialog-error:last').children('.ui-dialog-content:first'); counter = last.children('.elfinder-error-counter'); if (counter.length) { counter.data('cnt', parseInt(counter.data('cnt')) + 1).html(self.i18n(['moreErrors', counter.data('cnt')])); } else { counter = jQuery('<span class="elfinder-error-counter">'+ self.i18n(['moreErrors', 1]) +'</span>').data('cnt', 1); last.append('<br/>', counter); } } }) .bind('tmb', function(e) { jQuery.each(e.data.images||[], function(hash, tmb) { if (files[hash]) { files[hash].tmb = tmb; } }); }) .bind('searchstart', function(e) { Object.assign(self.searchStatus, e.data); self.searchStatus.state = 1; }) .bind('search', function(e) { self.searchStatus.state = 2; }) .bind('searchend', function() { self.searchStatus.state = 0; self.searchStatus.ininc = false; self.searchStatus.mixed = false; }) .bind('canMakeEmptyFile', function(e) { var data = e.data, obj = {}; if (data && Array.isArray(data.mimes)) { if (!data.unshift) { obj = self.mimesCanMakeEmpty; } jQuery.each(data.mimes, function() { if (!obj[this]) { obj[this] = self.mimeTypes[this]; } }); if (data.unshift) { self.mimesCanMakeEmpty = Object.assign(obj, self.mimesCanMakeEmpty); } } }) .bind('themechange', function() { requestAnimationFrame(function() { self.trigger('uiresize'); }); }) ; // We listen and emit a sound on delete according to option if (true === this.options.sound) { this.bind('playsound', function(e) { var play = beeper.canPlayType && beeper.canPlayType('audio/wav; codecs="1"'), file = e.data && e.data.soundFile; play && file && play != '' && play != 'no' && jQuery(beeper).html('<source src="' + soundPath + file + '" type="audio/wav">')[0].play(); }); } // bind external event handlers jQuery.each(this.options.handlers, function(event, callback) { self.bind(event, callback); }); /** * History object. Store visited folders * * @type Object **/ this.history = new this.history(this); /** * Root hashed * * @type Object */ this.roots = {}; /** * leaf roots * * @type Object */ this.leafRoots = {}; this.volumeExpires = {}; /** * Loaded commands * * @type Object **/ this._commands = {}; if (!Array.isArray(this.options.commands)) { this.options.commands = []; } if (jQuery.inArray('*', this.options.commands) !== -1) { this.options.commands = Object.keys(this.commands); } /** * UI command map of cwd volume ( That volume driver option `uiCmdMap` ) * * @type Object **/ this.commandMap = {}; /** * cwd options of each volume * key: volumeid * val: options object * * @type Object */ this.volOptions = {}; /** * Has volOptions data * * @type Boolean */ this.hasVolOptions = false; /** * Hash of trash holders * key: trash folder hash * val: source volume hash * * @type Object */ this.trashes = {}; /** * cwd options of each folder/file * key: hash * val: options object * * @type Object */ this.optionsByHashes = {}; /** * UI Auto Hide Functions * Each auto hide function mast be call to `fm.trigger('uiautohide')` at end of process * * @type Array **/ this.uiAutoHide = []; // trigger `uiautohide` this.one('open', function() { if (self.uiAutoHide.length) { setTimeout(function() { self.trigger('uiautohide'); }, 500); } }); // Auto Hide Functions sequential processing start this.bind('uiautohide', function() { if (self.uiAutoHide.length) { self.uiAutoHide.shift()(); } }); if (this.options.width) { width = this.options.width; } if (this.options.height) { height = this.options.height; } if (this.options.heightBase) { heightBase = jQuery(this.options.heightBase); } if (this.options.soundPath) { soundPath = this.options.soundPath.replace(/\/+$/, '') + '/'; } else { soundPath = this.baseUrl + soundPath; } self.one('opendone', function() { var tm; // attach events to document jQuery(document) // disable elfinder on click outside elfinder .on('click.'+namespace, function(e) { enabled && ! self.options.enableAlways && !jQuery(e.target).closest(node).length && self.disable(); }) // exec shortcuts .on(keydown+' '+keypress+' '+keyup+' '+mousedown, execShortcut); // attach events to window self.options.useBrowserHistory && jQuery(window) .on('popstate.' + namespace, function(ev) { var state = ev.originalEvent.state || {}, hasThash = state.thash? true : false, dialog = node.find('.elfinder-frontmost:visible'), input = node.find('.elfinder-navbar-dir,.elfinder-cwd-filename').find('input,textarea'), onOpen, toast; if (!hasThash) { state = { thash: self.cwd().hash }; // scroll to elFinder node jQuery('html,body').animate({ scrollTop: node.offset().top }); } if (dialog.length || input.length) { history.pushState(state, null, location.pathname + location.search + '#elf_' + state.thash); if (dialog.length) { if (!dialog.hasClass(self.res('class', 'preventback'))) { if (dialog.hasClass('elfinder-contextmenu')) { jQuery(document).trigger(jQuery.Event('keydown', { keyCode: jQuery.ui.keyCode.ESCAPE, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false })); } else if (dialog.hasClass('elfinder-dialog')) { dialog.elfinderdialog('close'); } else { dialog.trigger('close'); } } } else { input.trigger(jQuery.Event('keydown', { keyCode: jQuery.ui.keyCode.ESCAPE, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false })); } } else { if (hasThash) { !jQuery.isEmptyObject(self.files()) && self.request({ data : {cmd : 'open', target : state.thash, onhistory : 1}, notify : {type : 'open', cnt : 1, hideCnt : true}, syncOnFail : true }); } else { onOpen = function() { toast.trigger('click'); }; self.one('open', onOpen, true); toast = self.toast({ msg: self.i18n('pressAgainToExit'), onHidden: function() { self.unbind('open', onOpen); history.pushState(state, null, location.pathname + location.search + '#elf_' + state.thash); } }); } } }); jQuery(window).on('resize.' + namespace, function(e){ if (e.target === this) { tm && cancelAnimationFrame(tm); tm = requestAnimationFrame(function() { var prv = node.data('resizeSize') || {w: 0, h: 0}, size = {w: Math.round(node.width()), h: Math.round(node.height())}; node.data('resizeSize', size); if (size.w !== prv.w || size.h !== prv.h) { node.trigger('resize'); self.trigger('resize', {width : size.w, height : size.h}); } }); } }) .on('beforeunload.' + namespace,function(e){ var msg, cnt; if (node.is(':visible')) { if (self.ui.notify.children().length && jQuery.inArray('hasNotifyDialog', self.options.windowCloseConfirm) !== -1) { msg = self.i18n('ntfsmth'); } else if (node.find('.'+self.res('class', 'editing')).length && jQuery.inArray('editingFile', self.options.windowCloseConfirm) !== -1) { msg = self.i18n('editingFile'); } else if ((cnt = Object.keys(self.selected()).length) && jQuery.inArray('hasSelectedItem', self.options.windowCloseConfirm) !== -1) { msg = self.i18n('hasSelected', ''+cnt); } else if ((cnt = Object.keys(self.clipboard()).length) && jQuery.inArray('hasClipboardData', self.options.windowCloseConfirm) !== -1) { msg = self.i18n('hasClipboard', ''+cnt); } if (msg) { e.returnValue = msg; return msg; } } self.trigger('unload'); }); // bind window onmessage for CORS jQuery(window).on('message.' + namespace, function(e){ var res = e.originalEvent || null, obj, data; if (res && self.uploadURL.indexOf(res.origin) === 0) { try { obj = JSON.parse(res.data); data = obj.data || null; if (data) { if (data.error) { if (obj.bind) { self.trigger(obj.bind+'fail', data); } self.error(data.error); } else { data.warning && self.error(data.warning); self.updateCache(data); data.removed && data.removed.length && self.remove(data); data.added && data.added.length && self.add(data); data.changed && data.changed.length && self.change(data); if (obj.bind) { self.trigger(obj.bind, data); self.trigger(obj.bind+'done'); } data.sync && self.sync(); } } } catch (e) { self.sync(); } } }); // elFinder enable always if (self.options.enableAlways) { jQuery(window).on('focus.' + namespace, function(e){ (e.target === this) && self.enable(); }); if (inFrame) { jQuery(window.top).on('focus.' + namespace, function() { if (self.enable() && (! parentIframe || parentIframe.is(':visible'))) { requestAnimationFrame(function() { jQuery(window).trigger('focus'); }); } }); } } else if (inFrame) { jQuery(window).on('blur.' + namespace, function(e){ enabled && e.target === this && self.disable(); }); } // return focus to the window on click (elFInder in the frame) if (inFrame) { node.on('click', function(e) { jQuery(window).trigger('focus'); }); } // elFinder to enable by mouse over if (self.options.enableByMouseOver) { node.on('mouseenter touchstart', function(e) { (inFrame) && jQuery(window).trigger('focus'); ! self.enabled() && self.enable(); }); } }); // store instance in node node[0].elfinder = this; // auto load language file dfrdsBeforeBootup.push((function() { var lang = self.lang, langJs = self.i18nBaseUrl + 'elfinder.' + lang + '.js', dfd = jQuery.Deferred().done(function() { if (self.i18[lang]) { self.lang = lang; } self.trigger('i18load'); i18n = self.lang === 'en' ? self.i18['en'] : jQuery.extend(true, {}, self.i18['en'], self.i18[self.lang]); }); if (!self.i18[lang]) { self.lang = 'en'; if (self.hasRequire) { require([langJs], function() { dfd.resolve(); }, function() { dfd.resolve(); }); } else { self.loadScript([langJs], function() { dfd.resolve(); }, { loadType: 'tag', error : function() { dfd.resolve(); } }); } } else { dfd.resolve(); } return dfd; })()); // elFinder boot up function bootUp = function() { var columnNames; /** * i18 messages * * @type Object **/ self.messages = i18n.messages; // check jquery ui if (!(jQuery.fn.selectable && jQuery.fn.draggable && jQuery.fn.droppable && jQuery.fn.resizable && jQuery.fn.slider)) { return alert(self.i18n('errJqui')); } // check node if (!node.length) { return alert(self.i18n('errNode')); } // check connector url if (!self.options.url) { return alert(self.i18n('errURL')); } // column key/name map for fm.getColumnName() columnNames = Object.assign({ name : self.i18n('name'), perm : self.i18n('perms'), date : self.i18n('modify'), size : self.i18n('size'), kind : self.i18n('kind'), modestr : self.i18n('mode'), modeoct : self.i18n('mode'), modeboth : self.i18n('mode') }, self.options.uiOptions.cwd.listView.columnsCustomName); /** * Gets the column name of cwd list view * * @param String key The key * @return String The column name. */ self.getColumnName = function(key) { return columnNames[key] || self.i18n(key); }; /** * Interface direction * * @type String * @default "ltr" **/ self.direction = i18n.direction; /** * Date/time format * * @type String * @default "m.d.Y" **/ self.dateFormat = self.options.dateFormat || i18n.dateFormat; /** * Date format like "Yesterday 10:20:12" * * @type String * @default "{day} {time}" **/ self.fancyFormat = self.options.fancyDateFormat || i18n.fancyDateFormat; /** * Date format for if upload file has not original unique name * e.g. Clipboard image data, Image data taken with iOS * * @type String * @default "ymd-His" **/ self.nonameDateFormat = (self.options.nonameDateFormat || i18n.nonameDateFormat).replace(/[\/\\]/g, '_'); /** * Css classes * * @type String **/ self.cssClass = 'ui-helper-reset ui-helper-clearfix ui-widget ui-widget-content ui-corner-all elfinder elfinder-' +(self.direction == 'rtl' ? 'rtl' : 'ltr') +(self.UA.Touch? (' elfinder-touch' + (self.options.resizable ? ' touch-punch' : '')) : '') +(self.UA.Mobile? ' elfinder-mobile' : '') +(self.UA.iOS? ' elfinder-ios' : '') +' '+self.options.cssClass; // prepare node node.addClass(self.cssClass) .on(mousedown, function() { !enabled && self.enable(); }); // draggable closure (function() { var ltr, wzRect, wzBottom, wzBottom2, nodeStyle, keyEvt = keydown + 'draggable' + ' keyup.' + namespace + 'draggable'; /** * Base draggable options * * @type Object **/ self.draggable = { appendTo : node, addClasses : false, distance : 4, revert : true, refreshPositions : false, cursor : 'crosshair', cursorAt : {left : 50, top : 47}, scroll : false, start : function(e, ui) { var helper = ui.helper, targets = jQuery.grep(helper.data('files')||[], function(h) { if (h) { remember[h] = true; return true; } return false; }), locked = false, cnt, h; // fix node size nodeStyle = node.attr('style'); node.width(node.width()).height(node.height()); // set var for drag() ltr = (self.direction === 'ltr'); wzRect = self.getUI('workzone').data('rectangle'); wzBottom = wzRect.top + wzRect.height; wzBottom2 = wzBottom - self.getUI('navdock').outerHeight(true); self.draggingUiHelper = helper; cnt = targets.length; while (cnt--) { h = targets[cnt]; if (files[h].locked) { locked = true; helper.data('locked', true); break; } } !locked && self.trigger('lockfiles', {files : targets}); helper.data('autoScrTm', setInterval(function() { if (helper.data('autoScr')) { self.autoScroll[helper.data('autoScr')](helper.data('autoScrVal')); } }, 50)); }, drag : function(e, ui) { var helper = ui.helper, autoScr, autoUp, bottom; if ((autoUp = wzRect.top > e.pageY) || wzBottom2 < e.pageY) { if (wzRect.cwdEdge > e.pageX) { autoScr = (ltr? 'navbar' : 'cwd') + (autoUp? 'Up' : 'Down'); } else { autoScr = (ltr? 'cwd' : 'navbar') + (autoUp? 'Up' : 'Down'); } if (!autoUp) { if (autoScr.substr(0, 3) === 'cwd') { if (wzBottom < e.pageY) { bottom = wzBottom; } else { autoScr = null; } } else { bottom = wzBottom2; } } if (autoScr) { helper.data('autoScr', autoScr); helper.data('autoScrVal', Math.pow((autoUp? wzRect.top - e.pageY : e.pageY - bottom), 1.3)); } } if (! autoScr) { if (helper.data('autoScr')) { helper.data('refreshPositions', 1).data('autoScr', null); } } if (helper.data('refreshPositions') && jQuery(this).elfUiWidgetInstance('draggable')) { if (helper.data('refreshPositions') > 0) { jQuery(this).draggable('option', { refreshPositions : true, elfRefresh : true }); helper.data('refreshPositions', -1); } else { jQuery(this).draggable('option', { refreshPositions : false, elfRefresh : false }); helper.data('refreshPositions', null); } } }, stop : function(e, ui) { var helper = ui.helper, files; jQuery(document).off(keyEvt); jQuery(this).elfUiWidgetInstance('draggable') && jQuery(this).draggable('option', { refreshPositions : false }); self.draggingUiHelper = null; self.trigger('focus').trigger('dragstop'); if (! helper.data('droped')) { files = jQuery.grep(helper.data('files')||[], function(h) { return h? true : false ;}); self.trigger('unlockfiles', {files : files}); self.trigger('selectfiles', {files : self.selected()}); } self.enable(); // restore node style node.attr('style', nodeStyle); helper.data('autoScrTm') && clearInterval(helper.data('autoScrTm')); }, helper : function(e, ui) { var element = this.id ? jQuery(this) : jQuery(this).parents('[id]:first'), helper = jQuery('<div class="elfinder-drag-helper"><span class="elfinder-drag-helper-icon-status"/></div>'), icon = function(f) { var mime = f.mime, i, tmb = self.tmb(f); i = '<div class="elfinder-cwd-icon elfinder-cwd-icon-drag '+self.mime2class(mime)+' ui-corner-all"/>'; if (tmb) { i = jQuery(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML; } else if (f.icon) { i = jQuery(i).css(self.getIconStyle(f, true)).get(0).outerHTML; } if (f.csscls) { i = '<div class="'+f.csscls+'">' + i + '</div>'; } return i; }, hashes, l, ctr; self.draggingUiHelper && self.draggingUiHelper.stop(true, true); self.trigger('dragstart', {target : element[0], originalEvent : e}, true); hashes = element.hasClass(self.res('class', 'cwdfile')) ? self.selected() : [self.navId2Hash(element.attr('id'))]; helper.append(icon(files[hashes[0]])).data('files', hashes).data('locked', false).data('droped', false).data('namespace', namespace).data('dropover', 0); if ((l = hashes.length) > 1) { helper.append(icon(files[hashes[l-1]]) + '<span class="elfinder-drag-num">'+l+'</span>'); } jQuery(document).on(keyEvt, function(e){ var chk = (e.shiftKey||e.ctrlKey||e.metaKey); if (ctr !== chk) { ctr = chk; if (helper.is(':visible') && helper.data('dropover') && ! helper.data('droped')) { helper.toggleClass('elfinder-drag-helper-plus', helper.data('locked')? true : ctr); self.trigger(ctr? 'unlockfiles' : 'lockfiles', {files : hashes, helper: helper}); } } }); return helper; } }; })(); // in getFileCallback set - change default actions on double click/enter/ctrl+enter if (self.commands.getfile) { if (typeof(self.options.getFileCallback) == 'function') { self.bind('dblclick', function(e) { e.preventDefault(); self.exec('getfile').fail(function() { self.exec('open', e.data && e.data.file? [ e.data.file ]: void(0)); }); }); self.shortcut({ pattern : 'enter', description : self.i18n('cmdgetfile'), callback : function() { self.exec('getfile').fail(function() { self.exec(self.OS == 'mac' ? 'rename' : 'open'); }); } }) .shortcut({ pattern : 'ctrl+enter', description : self.i18n(self.OS == 'mac' ? 'cmdrename' : 'cmdopen'), callback : function() { self.exec(self.OS == 'mac' ? 'rename' : 'open'); } }); } else { self.options.getFileCallback = null; } } // load commands jQuery.each(self.commands, function(name, cmd) { var proto = Object.assign({}, cmd.prototype), extendsCmd, opts; if (jQuery.isFunction(cmd) && !self._commands[name] && (cmd.prototype.forceLoad || jQuery.inArray(name, self.options.commands) !== -1)) { extendsCmd = cmd.prototype.extendsCmd || ''; if (extendsCmd) { if (jQuery.isFunction(self.commands[extendsCmd])) { cmd.prototype = Object.assign({}, base, new self.commands[extendsCmd](), cmd.prototype); } else { return true; } } else { cmd.prototype = Object.assign({}, base, cmd.prototype); } self._commands[name] = new cmd(); cmd.prototype = proto; opts = self.options.commandsOptions[name] || {}; if (extendsCmd && self.options.commandsOptions[extendsCmd]) { opts = jQuery.extend(true, {}, self.options.commandsOptions[extendsCmd], opts); } self._commands[name].setup(name, opts); // setup linked commands if (self._commands[name].linkedCmds.length) { jQuery.each(self._commands[name].linkedCmds, function(i, n) { var lcmd = self.commands[n]; if (jQuery.isFunction(lcmd) && !self._commands[n]) { lcmd.prototype = base; self._commands[n] = new lcmd(); self._commands[n].setup(n, self.options.commandsOptions[n]||{}); } }); } } }); /** * UI nodes * * @type Object **/ self.ui = { // container for nav panel and current folder container workzone : jQuery('<div/>').appendTo(node).elfinderworkzone(self), // container for folders tree / places navbar : jQuery('<div/>').appendTo(node).elfindernavbar(self, self.options.uiOptions.navbar || {}), // container for for preview etc at below the navbar navdock : jQuery('<div/>').appendTo(node).elfindernavdock(self, self.options.uiOptions.navdock || {}), // contextmenu contextmenu : jQuery('<div/>').appendTo(node).elfindercontextmenu(self), // overlay overlay : jQuery('<div/>').appendTo(node).elfinderoverlay({ show : function() { self.disable(); }, hide : function() { prevEnabled && self.enable(); } }), // current folder container cwd : jQuery('<div/>').appendTo(node).elfindercwd(self, self.options.uiOptions.cwd || {}), // notification dialog window notify : self.dialog('', { cssClass : 'elfinder-dialog-notify', position : self.options.notifyDialog.position, absolute : true, resizable : false, autoOpen : false, closeOnEscape : false, title : ' ', width : self.options.notifyDialog.width? parseInt(self.options.notifyDialog.width) : null, minHeight : null }), statusbar : jQuery('<div class="ui-widget-header ui-helper-clearfix ui-corner-bottom elfinder-statusbar"/>').hide().appendTo(node), toast : jQuery('<div class="elfinder-toast"/>').appendTo(node), bottomtray : jQuery('<div class="elfinder-bottomtray">').appendTo(node) }; self.trigger('uiready'); // load required ui jQuery.each(self.options.ui || [], function(i, ui) { var name = 'elfinder'+ui, opts = self.options.uiOptions[ui] || {}; if (!self.ui[ui] && jQuery.fn[name]) { // regist to self.ui before make instance self.ui[ui] = jQuery('<'+(opts.tag || 'div')+'/>').appendTo(node); self.ui[ui][name](self, opts); } }); // update size self.resize(width, height); // make node resizable if (self.options.resizable) { node.resizable({ resize : function(e, ui) { self.resize(ui.size.width, ui.size.height); }, handles : 'se', minWidth : 300, minHeight : 200 }); if (self.UA.Touch) { node.addClass('touch-punch'); } } (function() { var navbar = self.getUI('navbar'), cwd = self.getUI('cwd').parent(); self.autoScroll = { navbarUp : function(v) { navbar.scrollTop(Math.max(0, navbar.scrollTop() - v)); }, navbarDown : function(v) { navbar.scrollTop(navbar.scrollTop() + v); }, cwdUp : function(v) { cwd.scrollTop(Math.max(0, cwd.scrollTop() - v)); }, cwdDown : function(v) { cwd.scrollTop(cwd.scrollTop() + v); } }; })(); // Swipe on the touch devices to show/hide of toolbar or navbar if (self.UA.Touch) { (function() { var lastX, lastY, nodeOffset, nodeWidth, nodeTop, navbarW, toolbarH, navbar = self.getUI('navbar'), toolbar = self.getUI('toolbar'), moveEv = 'touchmove.stopscroll', moveTm, moveUpOn = function(e) { var touches = e.originalEvent.touches || [{}], y = touches[0].pageY || null; if (!lastY || y < lastY) { e.preventDefault(); moveTm && clearTimeout(moveTm); } }, moveDownOn = function(e) { e.preventDefault(); moveTm && clearTimeout(moveTm); }, moveOff = function() { moveTm = setTimeout(function() { node.off(moveEv); }, 100); }, handleW, handleH = 50; navbar = navbar.children().length? navbar : null; toolbar = toolbar.length? toolbar : null; node.on('touchstart touchmove touchend', function(e) { if (e.type === 'touchend') { lastX = false; lastY = false; moveOff(); return; } var touches = e.originalEvent.touches || [{}], x = touches[0].pageX || null, y = touches[0].pageY || null, ltr = (self.direction === 'ltr'), navbarMode, treeWidth, swipeX, moveX, toolbarT, mode; if (x === null || y === null || (e.type === 'touchstart' && touches.length > 1)) { return; } if (e.type === 'touchstart') { nodeOffset = node.offset(); nodeWidth = node.width(); if (navbar) { lastX = false; if (navbar.is(':hidden')) { if (! handleW) { handleW = Math.max(50, nodeWidth / 10); } if ((ltr? (x - nodeOffset.left) : (nodeWidth + nodeOffset.left - x)) < handleW) { lastX = x; } } else if (! e.originalEvent._preventSwipeX) { navbarW = navbar.width(); if (ltr) { swipeX = (x < nodeOffset.left + navbarW); } else { swipeX = (x > nodeOffset.left + nodeWidth - navbarW); } if (swipeX) { handleW = Math.max(50, nodeWidth / 10); lastX = x; } else { lastX = false; } } } if (toolbar) { lastY = false; if (! e.originalEvent._preventSwipeY) { toolbarH = toolbar.height(); nodeTop = nodeOffset.top; if (y - nodeTop < (toolbar.is(':hidden')? handleH : (toolbarH + 30))) { lastY = y; node.on(moveEv, toolbar.is(':hidden')? moveDownOn: moveUpOn); } } } } else { if (navbar && lastX !== false) { navbarMode = (ltr? (lastX > x) : (lastX < x))? 'navhide' : 'navshow'; moveX = Math.abs(lastX - x); if (navbarMode === 'navhide' && moveX > navbarW * 0.6 || (moveX > (navbarMode === 'navhide'? navbarW / 3 : 45) && (navbarMode === 'navshow' || (ltr? x < nodeOffset.left + 20 : x > nodeOffset.left + nodeWidth - 20) )) ) { self.getUI('navbar').trigger(navbarMode, {handleW: handleW}); lastX = false; } } if (toolbar && lastY !== false ) { toolbarT = toolbar.offset().top; if (Math.abs(lastY - y) > Math.min(45, toolbarH / 3)) { mode = (lastY > y)? 'slideUp' : 'slideDown'; if (mode === 'slideDown' || toolbarT + 20 > y) { if (toolbar.is(mode === 'slideDown' ? ':hidden' : ':visible')) { toolbar.stop(true, true).trigger('toggle', {duration: 100, handleH: handleH}); } lastY = false; } } } } }); })(); } if (self.dragUpload) { // add event listener for HTML5 DnD upload (function() { var isin = function(e) { return (e.target.nodeName !== 'TEXTAREA' && e.target.nodeName !== 'INPUT' && jQuery(e.target).closest('div.ui-dialog-content').length === 0); }, ent = 'native-drag-enter', disable = 'native-drag-disable', c = 'class', navdir = self.res(c, 'navdir'), droppable = self.res(c, 'droppable'), dropover = self.res(c, 'adroppable'), arrow = self.res(c, 'navarrow'), clDropActive = self.res(c, 'adroppable'), wz = self.getUI('workzone'), ltr = (self.direction === 'ltr'), clearTm = function() { autoScrTm && cancelAnimationFrame(autoScrTm); autoScrTm = null; }, wzRect, autoScrFn, autoScrTm; node.on('dragenter', function(e) { clearTm(); if (isin(e)) { e.preventDefault(); e.stopPropagation(); wzRect = wz.data('rectangle'); } }) .on('dragleave', function(e) { clearTm(); if (isin(e)) { e.preventDefault(); e.stopPropagation(); } }) .on('dragover', function(e) { var autoUp; if (isin(e)) { e.preventDefault(); e.stopPropagation(); e.originalEvent.dataTransfer.dropEffect = 'none'; if (! autoScrTm) { autoScrTm = requestAnimationFrame(function() { var wzBottom = wzRect.top + wzRect.height, wzBottom2 = wzBottom - self.getUI('navdock').outerHeight(true), fn; if ((autoUp = e.pageY < wzRect.top) || e.pageY > wzBottom2 ) { if (wzRect.cwdEdge > e.pageX) { fn = (ltr? 'navbar' : 'cwd') + (autoUp? 'Up' : 'Down'); } else { fn = (ltr? 'cwd' : 'navbar') + (autoUp? 'Up' : 'Down'); } if (!autoUp) { if (fn.substr(0, 3) === 'cwd') { if (wzBottom < e.pageY) { wzBottom2 = wzBottom; } else { fn = ''; } } } fn && self.autoScroll[fn](Math.pow((autoUp? wzRect.top - e.pageY : e.pageY - wzBottom2), 1.3)); } autoScrTm = null; }); } } else { clearTm(); } }) .on('drop', function(e) { clearTm(); if (isin(e)) { e.stopPropagation(); e.preventDefault(); } }); node.on('dragenter', '.native-droppable', function(e){ if (e.originalEvent.dataTransfer) { var $elm = jQuery(e.currentTarget), id = e.currentTarget.id || null, cwd = null, elfFrom; if (!id) { // target is cwd cwd = self.cwd(); $elm.data(disable, false); try { jQuery.each(e.originalEvent.dataTransfer.types, function(i, v){ if (v.substr(0, 13) === 'elfinderfrom:') { elfFrom = v.substr(13).toLowerCase(); } }); } catch(e) {} } if (!cwd || (cwd.write && (!elfFrom || elfFrom !== (window.location.href + cwd.hash).toLowerCase()))) { e.preventDefault(); e.stopPropagation(); $elm.data(ent, true); $elm.addClass(clDropActive); } else { $elm.data(disable, true); } } }) .on('dragleave', '.native-droppable', function(e){ if (e.originalEvent.dataTransfer) { var $elm = jQuery(e.currentTarget); e.preventDefault(); e.stopPropagation(); if ($elm.data(ent)) { $elm.data(ent, false); } else { $elm.removeClass(clDropActive); } } }) .on('dragover', '.native-droppable', function(e){ if (e.originalEvent.dataTransfer) { var $elm = jQuery(e.currentTarget); e.preventDefault(); e.stopPropagation(); e.originalEvent.dataTransfer.dropEffect = $elm.data(disable)? 'none' : 'copy'; $elm.data(ent, false); } }) .on('drop', '.native-droppable', function(e){ if (e.originalEvent && e.originalEvent.dataTransfer) { var $elm = jQuery(e.currentTarget), id; e.preventDefault(); e.stopPropagation(); $elm.removeClass(clDropActive); if (e.currentTarget.id) { id = $elm.hasClass(navdir)? self.navId2Hash(e.currentTarget.id) : self.cwdId2Hash(e.currentTarget.id); } else { id = self.cwd().hash; } e.originalEvent._target = id; self.exec('upload', {dropEvt: e.originalEvent, target: id}, void 0, id); } }); })(); } // trigger event cssloaded if cddAutoLoad disabled if (self.cssloaded === null) { // check css loaded and remove hide (function() { var loaded = function() { if (node.data('cssautoloadHide')) { node.data('cssautoloadHide').remove(); node.removeData('cssautoloadHide'); } self.cssloaded = true; requestAnimationFrame(function() { self.trigger('cssloaded'); }); }, cnt, fi; if (node.css('visibility') === 'hidden') { cnt = 1000; // timeout 10 secs fi = setInterval(function() { if (--cnt < 0 || node.css('visibility') !== 'hidden') { clearInterval(fi); loaded(); } }, 10); } else { loaded(); } })(); } else { self.cssloaded = true; self.trigger('cssloaded'); } // calculate elFinder node z-index self.zIndexCalc(); // send initial request and start to pray >_< self.trigger('init') .request({ data : {cmd : 'open', target : self.startDir(), init : 1, tree : 1}, preventDone : true, notify : {type : 'open', cnt : 1, hideCnt : true}, freeze : true }) .fail(function() { self.trigger('fail').disable().lastDir(''); listeners = {}; shortcuts = {}; jQuery(document).add(node).off('.'+namespace); self.trigger = function() { }; }) .done(function(data) { var trashDisable = function(th) { var src = self.file(self.trashes[th]), d = self.options.debug, error; if (src && src.volumeid) { delete self.volOptions[src.volumeid].trashHash; } self.trashes[th] = false; self.debug('backend-error', 'Trash hash "'+th+'" was not found or not writable.'); }, toChkTh = {}; // regist rawStringDecoder if (self.options.rawStringDecoder) { self.registRawStringDecoder(self.options.rawStringDecoder); } // re-calculate elFinder node z-index self.zIndexCalc(); self.load().debug('api', self.api); // update ui's size after init node.trigger('resize'); // initial open open(data); self.trigger('open', data, false); self.trigger('opendone'); if (inFrame && self.options.enableAlways) { jQuery(window).trigger('focus'); } // check self.trashes jQuery.each(self.trashes, function(th) { var dir = self.file(th), src; if (! dir) { toChkTh[th] = true; } else if (dir.mime !== 'directory' || ! dir.write) { trashDisable(th); } }); if (Object.keys(toChkTh).length) { self.request({ data : {cmd : 'info', targets : Object.keys(toChkTh)}, preventDefault : true }).done(function(data) { if (data && data.files) { jQuery.each(data.files, function(i, dir) { if (dir.mime === 'directory' && dir.write) { delete toChkTh[dir.hash]; } }); } }).always(function() { jQuery.each(toChkTh, trashDisable); }); } // to enable / disable self[self.options.enableAlways? 'enable' : 'disable'](); }); // self.timeEnd('load'); // End of bootUp() }; // call bootCallback function with elFinder instance, extraObject - { dfrdsBeforeBootup: dfrdsBeforeBootup } if (bootCallback && typeof bootCallback === 'function') { self.bootCallback = bootCallback; bootCallback.call(node.get(0), self, { dfrdsBeforeBootup: dfrdsBeforeBootup }); } // call dfrdsBeforeBootup functions then boot up elFinder jQuery.when.apply(null, dfrdsBeforeBootup).done(function() { bootUp(); }).fail(function(error) { self.error(error); }); }; //register elFinder to global scope if (typeof toGlobal === 'undefined' || toGlobal) { window.elFinder = elFinder; } /** * Prototype * * @type Object */ elFinder.prototype = { uniqueid : 0, res : function(type, id) { return this.resources[type] && this.resources[type][id]; }, /** * User os. Required to bind native shortcuts for open/rename * * @type String **/ OS : navigator.userAgent.indexOf('Mac') !== -1 ? 'mac' : navigator.userAgent.indexOf('Win') !== -1 ? 'win' : 'other', /** * User browser UA. * jQuery.browser: version deprecated: 1.3, removed: 1.9 * * @type Object **/ UA : (function(){ var self = this, webkit = !document.unqueID && !window.opera && !window.sidebar && window.localStorage && 'WebkitAppearance' in document.documentElement.style, chrome = webkit && window.chrome, /*setRotated = function() { var a = ((screen && screen.orientation && screen.orientation.angle) || window.orientation || 0) + 0; if (a === -90) { a = 270; } UA.Angle = a; UA.Rotated = a % 180 === 0? false : true; },*/ UA = { // Browser IE <= IE 6 ltIE6 : typeof window.addEventListener == "undefined" && typeof document.documentElement.style.maxHeight == "undefined", // Browser IE <= IE 7 ltIE7 : typeof window.addEventListener == "undefined" && typeof document.querySelectorAll == "undefined", // Browser IE <= IE 8 ltIE8 : typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined", // Browser IE <= IE 9 ltIE9 : document.uniqueID && document.documentMode <= 9, // Browser IE <= IE 10 ltIE10 : document.uniqueID && document.documentMode <= 10, // Browser IE >= IE 11 gtIE11 : document.uniqueID && document.documentMode >= 11, IE : document.uniqueID, Firefox : window.sidebar, Opera : window.opera, Webkit : webkit, Chrome : chrome, Edge : (chrome && window.msCredentials)? true : false, Safari : webkit && !window.chrome, Mobile : typeof window.orientation != "undefined", Touch : typeof window.ontouchstart != "undefined", iOS : navigator.platform.match(/^iP(?:[ao]d|hone)/), Fullscreen : (typeof (document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen || document.msExitFullscreen) !== 'undefined'), Angle : 0, Rotated : false, CSS : (function() { var aStyle = document.createElement('a').style, pStyle = document.createElement('p').style, css; css = 'position:sticky;position:-webkit-sticky;'; css += 'width:-webkit-max-content;width:-moz-max-content;width:-ms-max-content;width:max-content;'; aStyle.cssText = css; return { positionSticky : aStyle.position.indexOf('sticky')!==-1, widthMaxContent : aStyle.width.indexOf('max-content')!==-1, flex : typeof pStyle.flex !== 'undefined' }; })() }; return UA; })(), /** * Has RequireJS? * * @type Boolean */ hasRequire : (typeof define === 'function' && define.amd), /** * Current request command * * @type String */ currentReqCmd : '', /** * Current keyboard state * * @type Object */ keyState : {}, /** * Internationalization object * * @type Object */ i18 : { en : { translator : '', language : 'English', direction : 'ltr', dateFormat : 'd.m.Y H:i', fancyDateFormat : '$1 H:i', nonameDateFormat : 'ymd-His', messages : {} }, months : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], monthsShort : ['msJan', 'msFeb', 'msMar', 'msApr', 'msMay', 'msJun', 'msJul', 'msAug', 'msSep', 'msOct', 'msNov', 'msDec'], days : ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], daysShort : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] }, /** * File mimetype to kind mapping * * @type Object */ kinds : { 'unknown' : 'Unknown', 'directory' : 'Folder', 'group' : 'Selects', 'symlink' : 'Alias', 'symlink-broken' : 'AliasBroken', 'application/x-empty' : 'TextPlain', 'application/postscript' : 'Postscript', 'application/vnd.ms-office' : 'MsOffice', 'application/msword' : 'MsWord', 'application/vnd.ms-word' : 'MsWord', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' : 'MsWord', 'application/vnd.ms-word.document.macroEnabled.12' : 'MsWord', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' : 'MsWord', 'application/vnd.ms-word.template.macroEnabled.12' : 'MsWord', 'application/vnd.ms-excel' : 'MsExcel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' : 'MsExcel', 'application/vnd.ms-excel.sheet.macroEnabled.12' : 'MsExcel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' : 'MsExcel', 'application/vnd.ms-excel.template.macroEnabled.12' : 'MsExcel', 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' : 'MsExcel', 'application/vnd.ms-excel.addin.macroEnabled.12' : 'MsExcel', 'application/vnd.ms-powerpoint' : 'MsPP', 'application/vnd.openxmlformats-officedocument.presentationml.presentation' : 'MsPP', 'application/vnd.ms-powerpoint.presentation.macroEnabled.12' : 'MsPP', 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' : 'MsPP', 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' : 'MsPP', 'application/vnd.openxmlformats-officedocument.presentationml.template' : 'MsPP', 'application/vnd.ms-powerpoint.template.macroEnabled.12' : 'MsPP', 'application/vnd.ms-powerpoint.addin.macroEnabled.12' : 'MsPP', 'application/vnd.openxmlformats-officedocument.presentationml.slide' : 'MsPP', 'application/vnd.ms-powerpoint.slide.macroEnabled.12' : 'MsPP', 'application/pdf' : 'PDF', 'application/xml' : 'XML', 'application/vnd.oasis.opendocument.text' : 'OO', 'application/vnd.oasis.opendocument.text-template' : 'OO', 'application/vnd.oasis.opendocument.text-web' : 'OO', 'application/vnd.oasis.opendocument.text-master' : 'OO', 'application/vnd.oasis.opendocument.graphics' : 'OO', 'application/vnd.oasis.opendocument.graphics-template' : 'OO', 'application/vnd.oasis.opendocument.presentation' : 'OO', 'application/vnd.oasis.opendocument.presentation-template' : 'OO', 'application/vnd.oasis.opendocument.spreadsheet' : 'OO', 'application/vnd.oasis.opendocument.spreadsheet-template' : 'OO', 'application/vnd.oasis.opendocument.chart' : 'OO', 'application/vnd.oasis.opendocument.formula' : 'OO', 'application/vnd.oasis.opendocument.database' : 'OO', 'application/vnd.oasis.opendocument.image' : 'OO', 'application/vnd.openofficeorg.extension' : 'OO', 'application/x-shockwave-flash' : 'AppFlash', 'application/flash-video' : 'Flash video', 'application/x-bittorrent' : 'Torrent', 'application/javascript' : 'JS', 'application/rtf' : 'RTF', 'application/rtfd' : 'RTF', 'application/x-font-ttf' : 'TTF', 'application/x-font-otf' : 'OTF', 'application/x-rpm' : 'RPM', 'application/x-web-config' : 'TextPlain', 'application/xhtml+xml' : 'HTML', 'application/docbook+xml' : 'DOCBOOK', 'application/x-awk' : 'AWK', 'application/x-gzip' : 'GZIP', 'application/x-bzip2' : 'BZIP', 'application/x-xz' : 'XZ', 'application/zip' : 'ZIP', 'application/x-zip' : 'ZIP', 'application/x-rar' : 'RAR', 'application/x-tar' : 'TAR', 'application/x-7z-compressed' : '7z', 'application/x-jar' : 'JAR', 'text/plain' : 'TextPlain', 'text/x-php' : 'PHP', 'text/html' : 'HTML', 'text/javascript' : 'JS', 'text/css' : 'CSS', 'text/rtf' : 'RTF', 'text/rtfd' : 'RTF', 'text/x-c' : 'C', 'text/x-csrc' : 'C', 'text/x-chdr' : 'CHeader', 'text/x-c++' : 'CPP', 'text/x-c++src' : 'CPP', 'text/x-c++hdr' : 'CPPHeader', 'text/x-shellscript' : 'Shell', 'application/x-csh' : 'Shell', 'text/x-python' : 'Python', 'text/x-java' : 'Java', 'text/x-java-source' : 'Java', 'text/x-ruby' : 'Ruby', 'text/x-perl' : 'Perl', 'text/x-sql' : 'SQL', 'text/xml' : 'XML', 'text/x-comma-separated-values' : 'CSV', 'text/x-markdown' : 'Markdown', 'image/x-ms-bmp' : 'BMP', 'image/jpeg' : 'JPEG', 'image/gif' : 'GIF', 'image/png' : 'PNG', 'image/tiff' : 'TIFF', 'image/x-targa' : 'TGA', 'image/vnd.adobe.photoshop' : 'PSD', 'image/xbm' : 'XBITMAP', 'image/pxm' : 'PXM', 'audio/mpeg' : 'AudioMPEG', 'audio/midi' : 'AudioMIDI', 'audio/ogg' : 'AudioOGG', 'audio/mp4' : 'AudioMPEG4', 'audio/x-m4a' : 'AudioMPEG4', 'audio/wav' : 'AudioWAV', 'audio/x-mp3-playlist' : 'AudioPlaylist', 'video/x-dv' : 'VideoDV', 'video/mp4' : 'VideoMPEG4', 'video/mpeg' : 'VideoMPEG', 'video/x-msvideo' : 'VideoAVI', 'video/quicktime' : 'VideoMOV', 'video/x-ms-wmv' : 'VideoWM', 'video/x-flv' : 'VideoFlash', 'video/x-matroska' : 'VideoMKV', 'video/ogg' : 'VideoOGG' }, /** * File mimetype to file extention mapping * * @type Object * @see elFinder.mimetypes.js */ mimeTypes : {}, /** * Ajax request data validation rules * * @type Object */ rules : { defaults : function(data) { if (!data || (data.added && !Array.isArray(data.added)) || (data.removed && !Array.isArray(data.removed)) || (data.changed && !Array.isArray(data.changed))) { return false; } return true; }, open : function(data) { return data && data.cwd && data.files && jQuery.isPlainObject(data.cwd) && Array.isArray(data.files); }, tree : function(data) { return data && data.tree && Array.isArray(data.tree); }, parents : function(data) { return data && data.tree && Array.isArray(data.tree); }, tmb : function(data) { return data && data.images && (jQuery.isPlainObject(data.images) || Array.isArray(data.images)); }, upload : function(data) { return data && (jQuery.isPlainObject(data.added) || Array.isArray(data.added));}, search : function(data) { return data && data.files && Array.isArray(data.files); } }, /** * Commands costructors * * @type Object */ commands : {}, /** * Commands to add the item (space delimited) * * @type String */ cmdsToAdd : 'archive duplicate extract mkdir mkfile paste rm upload', parseUploadData : function(text) { var self = this, data; if (!jQuery.trim(text)) { return {error : ['errResponse', 'errDataEmpty']}; } try { data = JSON.parse(text); } catch (e) { return {error : ['errResponse', 'errDataNotJSON']}; } data = self.normalize(data); if (!self.validResponse('upload', data)) { return {error : (response.norError || ['errResponse'])}; } data.removed = jQuery.merge((data.removed || []), jQuery.map(data.added || [], function(f) { return self.file(f.hash)? f.hash : null; })); return data; }, iframeCnt : 0, uploads : { // xhr muiti uploading flag xhrUploading: false, // Timer of request fail to sync failSyncTm: null, // current chunkfail requesting chunk chunkfailReq: {}, // check file/dir exists checkExists: function(files, target, fm, isDir) { var dfrd = jQuery.Deferred(), names, renames = [], hashes = {}, chkFiles = [], cancel = function() { var i = files.length; while (--i > -1) { files[i]._remove = true; } }, resolve = function() { dfrd.resolve(renames, hashes); }, check = function() { var existed = [], exists = [], i, c, pathStr = target !== fm.cwd().hash? fm.path(target, true) + fm.option('separator', target) : '', confirm = function(ndx) { alert(1); var last = ndx == exists.length-1, opts = { cssClass : 'elfinder-confirm-upload', title : fm.i18n('cmdupload'), text : ['errExists', pathStr + exists[ndx].name, 'confirmRepl'], all : !last, accept : { label : 'btnYes', callback : function(all) { !last && !all ? confirm(++ndx) : resolve(); } }, reject : { label : 'btnNo', callback : function(all) { var i; if (all) { i = exists.length; while (ndx < i--) { files[exists[i].i]._remove = true; } } else { files[exists[ndx].i]._remove = true; } !last && !all ? confirm(++ndx) : resolve(); } }, cancel : { label : 'btnCancel', callback : function() { cancel(); resolve(); } }, buttons : [ { label : 'btnBackup', cssClass : 'elfinder-confirm-btn-backup', callback : function(all) { var i; if (all) { i = exists.length; while (ndx < i--) { renames.push(exists[i].name); } } else { renames.push(exists[ndx].name); } !last && !all ? confirm(++ndx) : resolve(); } } ] }; if (!isDir) { opts.buttons.push({ label : 'btnRename' + (last? '' : 'All'), cssClass : 'elfinder-confirm-btn-rename', callback : function() { renames = null; resolve(); } }); } if (fm.iframeCnt > 0) { delete opts.reject; } fm.confirm(opts); }; if (! fm.file(target).read) { // for dropbox type resolve(); return; } names = jQuery.map(files, function(file, i) { return file.name && (!fm.UA.iOS || file.name !== 'image.jpg')? {i: i, name: file.name} : null ;}); fm.request({ data : {cmd : 'ls', target : target, intersect : jQuery.map(names, function(item) { return item.name;})}, notify : {type : 'preupload', cnt : 1, hideCnt : true}, preventDefault : true }) .done(function(data) { var existedArr, cwdItems; if (data) { if (data.error) { cancel(); } else { if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) { if (data.list) { if (Array.isArray(data.list)) { existed = data.list || []; } else { existedArr = []; existed = jQuery.map(data.list, function(n) { if (typeof n === 'string') { return n; } else { // support to >=2.1.11 plugin Normalizer, Sanitizer existedArr = existedArr.concat(n); return false; } }); if (existedArr.length) { existed = existed.concat(existedArr); } hashes = data.list; } exists = jQuery.grep(names, function(name){ return jQuery.inArray(name.name, existed) !== -1 ? true : false ; }); if (exists.length && existed.length && target == fm.cwd().hash) { cwdItems = jQuery.map(fm.files(target), function(file) { return file.name; } ); if (jQuery.grep(existed, function(n) { return jQuery.inArray(n, cwdItems) === -1? true : false; }).length){ fm.sync(); } } } } } } if (exists.length > 0) { confirm(0); } else { resolve(); } }) .fail(function(error) { cancel(); resolve(); error && fm.error(error); }); }; if (fm.api >= 2.1 && typeof files[0] == 'object') { check(); } else { resolve(); } return dfrd; }, // check droped contents checkFile : function(data, fm, target) { if (!!data.checked || data.type == 'files') { return data.files; } else if (data.type == 'data') { var dfrd = jQuery.Deferred(), scanDfd = jQuery.Deferred(), files = [], paths = [], dirctorys = [], processing = 0, items, mkdirs = [], cancel = false, toArray = function(list) { return Array.prototype.slice.call(list || [], 0); }, doScan = function(items) { var entry, readEntries, excludes = fm.options.folderUploadExclude[fm.OS] || null, length = items.length, check = function() { if (--processing < 1 && scanDfd.state() === 'pending') { scanDfd.resolve(); } }, pushItem = function(file) { if (! excludes || ! file.name.match(excludes)) { paths.push(entry.fullPath || ''); files.push(file); } check(); }, readEntries = function(dirReader) { var entries = [], read = function() { dirReader.readEntries(function(results) { if (cancel || !results.length) { for (var i = 0; i < entries.length; i++) { if (cancel) { scanDfd.reject(); break; } doScan([entries[i]]); } check(); } else { entries = entries.concat(toArray(results)); read(); } }, check); }; read(); }; processing++; for (var i = 0; i < length; i++) { if (cancel) { scanDfd.reject(); break; } entry = items[i]; if (entry) { if (entry.isFile) { processing++; entry.file(pushItem, check); } else if (entry.isDirectory) { if (fm.api >= 2.1) { processing++; mkdirs.push(entry.fullPath); readEntries(entry.createReader()); // Start reading dirs. } } } } check(); return scanDfd; }, hasDirs; items = jQuery.map(data.files.items, function(item){ return item.getAsEntry? item.getAsEntry() : item.webkitGetAsEntry(); }); jQuery.each(items, function(i, item) { if (item.isDirectory) { hasDirs = true; return false; } }); if (items.length > 0) { fm.uploads.checkExists(items, target, fm, hasDirs).done(function(renames, hashes){ var dfds = []; if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) { if (renames === null) { data.overwrite = 0; renames = []; } items = jQuery.grep(items, function(item){ var i, bak, hash, dfd, hi; if (item.isDirectory && renames.length) { i = jQuery.inArray(item.name, renames); if (i !== -1) { renames.splice(i, 1); bak = fm.uniqueName(item.name + fm.options.backupSuffix , null, ''); jQuery.each(hashes, function(h, name) { if (item.name == name) { hash = h; return false; } }); if (! hash) { hash = fm.fileByName(item.name, target).hash; } fm.lockfiles({files : [hash]}); dfd = fm.request({ data : {cmd : 'rename', target : hash, name : bak}, notify : {type : 'rename', cnt : 1} }) .fail(function() { item._remove = true; fm.sync(); }) .always(function() { fm.unlockfiles({files : [hash]}); }); dfds.push(dfd); } } return !item._remove? true : false; }); } jQuery.when.apply($, dfds).done(function(){ var notifyto, msg, id = +new Date(); if (items.length > 0) { msg = fm.escape(items[0].name); if (items.length > 1) { msg += ' ... ' + items.length + fm.i18n('items'); } notifyto = setTimeout(function() { fm.notify({ type : 'readdir', id : id, cnt : 1, hideCnt: true, msg : fm.i18n('ntfreaddir') + ' (' + msg + ')', cancel: function() { cancel = true; } }); }, fm.options.notifyDelay); doScan(items).done(function() { notifyto && clearTimeout(notifyto); fm.notify({type : 'readdir', id: id, cnt : -1}); if (cancel) { dfrd.reject(); } else { dfrd.resolve([files, paths, renames, hashes, mkdirs]); } }).fail(function() { dfrd.reject(); }); } else { dfrd.reject(); } }); }); return dfrd.promise(); } else { return dfrd.reject(); } } else { var ret = []; var check = []; var str = data.files[0]; if (data.type == 'html') { var tmp = jQuery("<html/>").append(jQuery.parseHTML(str.replace(/ src=/ig, ' _elfsrc='))), atag; jQuery('img[_elfsrc]', tmp).each(function(){ var url, purl, self = jQuery(this), pa = self.closest('a'); if (pa && pa.attr('href') && pa.attr('href').match(/\.(?:jpe?g|gif|bmp|png)/i)) { purl = pa.attr('href'); } url = self.attr('_elfsrc'); if (url) { if (purl) { jQuery.inArray(purl, ret) == -1 && ret.push(purl); jQuery.inArray(url, check) == -1 && check.push(url); } else { jQuery.inArray(url, ret) == -1 && ret.push(url); } } // Probably it's clipboard data if (ret.length === 1 && ret[0].match(/^data:image\/png/)) { data.clipdata = true; } }); atag = jQuery('a[href]', tmp); atag.each(function(){ var text, loc, parseUrl = function(url) { var a = document.createElement('a'); a.href = url; return a; }; if (text = jQuery(this).text()) { loc = parseUrl(jQuery(this).attr('href')); if (loc.href && loc.href.match(/^(?:ht|f)tp/i) && (atag.length === 1 || ! loc.pathname.match(/(?:\.html?|\/[^\/.]*)$/i) || jQuery.trim(text).match(/\.[a-z0-9-]{1,10}$/i))) { if (jQuery.inArray(loc.href, ret) == -1 && jQuery.inArray(loc.href, check) == -1) ret.push(loc.href); } } }); } else { var regex, m, url; regex = /((?:ht|f)tps?:\/\/[-_.!~*\'()a-z0-9;/?:\@&=+\$,%#\*\[\]]+)/ig; while (m = regex.exec(str)) { url = m[1].replace(/&/g, '&'); if (jQuery.inArray(url, ret) == -1) ret.push(url); } } return ret; } }, // upload transport using XMLHttpRequest xhr : function(data, fm) { var self = fm ? fm : this, node = self.getUI(), xhr = new XMLHttpRequest(), notifyto = null, notifyto2 = null, dataChecked = data.checked, isDataType = (data.isDataType || data.type == 'data'), target = (data.target || self.cwd().hash), dropEvt = (data.dropEvt || null), extraData = data.extraData || null, chunkEnable = (self.option('uploadMaxConn', target) != -1), multiMax = Math.min(5, Math.max(1, self.option('uploadMaxConn', target))), retryWait = 10000, // 10 sec retryMax = 30, // 10 sec * 30 = 300 secs (Max 5 mins) retry = 0, getFile = function(files) { var dfd = jQuery.Deferred(), file; if (files.promise) { files.always(function(f) { dfd.resolve(Array.isArray(f) && f.length? (isDataType? f[0][0] : f[0]) : {}); }); } else { dfd.resolve(files.length? (isDataType? files[0][0] : files[0]) : {}); } return dfd; }, dfrd = jQuery.Deferred() .fail(function(err) { var error = self.parseError(err), userAbort; if (error === 'userabort') { userAbort = true; error = void 0; } if (files && (self.uploads.xhrUploading || userAbort)) { // send request om fail getFile(files).done(function(file) { if (!userAbort) { triggerError(error, file); } if (! file._cid) { // send sync request self.uploads.failSyncTm && clearTimeout(self.uploads.failSyncTm); self.uploads.failSyncTm = setTimeout(function() { self.sync(target); }, 1000); } else if (! self.uploads.chunkfailReq[file._cid]) { // send chunkfail request self.uploads.chunkfailReq[file._cid] = true; setTimeout(function() { fm.request({ data : { cmd: 'upload', target: target, chunk: file._chunk, cid: file._cid, upload: ['chunkfail'], mimes: 'chunkfail' }, options : { type: 'post', url: self.uploadURL }, preventDefault: true }).always(function() { delete self.uploads.chunkfailReq[file._chunk]; }); }, 1000); } }); } else { triggerError(error); } !userAbort && self.sync(); self.uploads.xhrUploading = false; files = null; }) .done(function(data) { self.uploads.xhrUploading = false; files = null; if (data) { self.currentReqCmd = 'upload'; data.warning && triggerError(data.warning); self.updateCache(data); data.removed && data.removed.length && self.remove(data); data.added && data.added.length && self.add(data); data.changed && data.changed.length && self.change(data); self.trigger('upload', data, false); self.trigger('uploaddone'); if (data.toasts && Array.isArray(data.toasts)) { jQuery.each(data.toasts, function() { this.msg && self.toast(this); }); } data.sync && self.sync(); data.debug && fm.debug('backend-debug', data); } }) .always(function() { self.abortXHR(xhr); // unregist fnAbort function node.off('uploadabort', fnAbort); jQuery(window).off('unload', fnAbort); notifyto && clearTimeout(notifyto); notifyto2 && clearTimeout(notifyto2); dataChecked && !data.multiupload && checkNotify() && self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0}); chunkMerge && notifyElm.children('.elfinder-notify-chunkmerge').length && self.notify({type : 'chunkmerge', cnt : -1}); }), formData = new FormData(), files = data.input ? data.input.files : self.uploads.checkFile(data, self, target), cnt = data.checked? (isDataType? files[0].length : files.length) : files.length, loaded = 0, prev = 0, filesize = 0, notify = false, notifyElm = self.ui.notify, cancelBtn = true, abort = false, checkNotify = function() { if (!notify && (ntfUpload = notifyElm.children('.elfinder-notify-upload')).length) { notify = true; } return notify; }, fnAbort = function(e, error) { abort = true; self.abortXHR(xhr, { quiet: true, abort: true }); dfrd.reject(error); if (checkNotify()) { self.notify({type : 'upload', cnt : ntfUpload.data('cnt') * -1, progress : 0, size : 0}); } }, cancelToggle = function(show) { ntfUpload.children('.elfinder-notify-cancel')[show? 'show':'hide'](); }, startNotify = function(size) { if (!size) size = filesize; return setTimeout(function() { notify = true; self.notify({type : 'upload', cnt : cnt, progress : loaded - prev, size : size, cancel: function() { node.trigger('uploadabort', 'userabort'); } }); ntfUpload = notifyElm.children('.elfinder-notify-upload'); prev = loaded; if (data.multiupload) { cancelBtn && cancelToggle(true); } else { cancelToggle(cancelBtn && loaded < size); } }, self.options.notifyDelay); }, doRetry = function() { if (retry++ <= retryMax) { if (checkNotify() && prev) { self.notify({type : 'upload', cnt : 0, progress : 0, size : prev}); } self.abortXHR(xhr, { quiet: true }); prev = loaded = 0; setTimeout(function() { var reqId; if (! abort) { xhr.open('POST', self.uploadURL, true); if (self.api >= 2.1029) { reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16); (typeof formData['delete'] === 'function') && formData['delete']('reqid'); formData.append('reqid', reqId); xhr._requestId = reqId; } xhr.send(formData); } }, retryWait); } else { node.trigger('uploadabort', ['errAbort', 'errTimeout']); } }, progress = function() { var node; if (notify) { dfrd.notifyWith(ntfUpload, [{ cnt: ntfUpload.data('cnt'), progress: ntfUpload.data('progress'), total: ntfUpload.data('total') }]); } }, triggerError = function(err, file, unite) { err && self.trigger('xhruploadfail', { error: err, file: file }); if (unite) { if (err) { if (errCnt < self.options.maxErrorDialogs) { if (Array.isArray(err)) { errors = errors.concat(err); } else { errors.push(err); } } errCnt++; } } else { if (err) { self.error(err); } else { if (errors.length) { if (errCnt >= self.options.maxErrorDialogs) { errors = errors.concat('moreErrors', errCnt - self.options.maxErrorDialogs); } self.error(errors); } errors = []; errCnt = 0; } } }, errors = [], errCnt = 0, renames = (data.renames || null), hashes = (data.hashes || null), chunkMerge = false, ntfUpload = jQuery(); // regist fnAbort function node.one('uploadabort', fnAbort); jQuery(window).one('unload.' + fm.namespace, fnAbort); !chunkMerge && (prev = loaded); if (!isDataType && !cnt) { return dfrd.reject(['errUploadNoFiles']); } xhr.addEventListener('error', function() { if (xhr.status == 0) { if (abort) { dfrd.reject(); } else { // ff bug while send zero sized file // for safari - send directory if (!isDataType && data.files && jQuery.grep(data.files, function(f){return ! f.type && f.size === (self.UA.Safari? 1802 : 0)? true : false;}).length) { dfrd.reject(['errAbort', 'errFolderUpload']); } else if (data.input && jQuery.grep(data.input.files, function(f){return ! f.type && f.size === (self.UA.Safari? 1802 : 0)? true : false;}).length) { dfrd.reject(['errUploadNoFiles']); } else { doRetry(); } } } else { node.trigger('uploadabort', 'errConnect'); } }, false); xhr.addEventListener('load', function(e) { var status = xhr.status, res, curr = 0, error = '', errData, errObj; if (status >= 400) { if (status > 500) { error = 'errResponse'; } else { error = ['errResponse', 'errServerError']; } } else { if (!xhr.responseText) { error = ['errResponse', 'errDataEmpty']; } } if (error) { node.trigger('uploadabort'); getFile(files).done(function(file) { return dfrd.reject(file._cid? null : error); }); } loaded = filesize; if (checkNotify() && (curr = loaded - prev)) { self.notify({type : 'upload', cnt : 0, progress : curr, size : 0}); progress(); } res = self.parseUploadData(xhr.responseText); // chunked upload commit if (res._chunkmerged) { formData = new FormData(); var _file = [{_chunkmerged: res._chunkmerged, _name: res._name, _mtime: res._mtime}]; chunkMerge = true; node.off('uploadabort', fnAbort); notifyto2 = setTimeout(function() { self.notify({type : 'chunkmerge', cnt : 1}); }, self.options.notifyDelay); isDataType? send(_file, files[1]) : send(_file); return; } res._multiupload = data.multiupload? true : false; if (res.error) { errData = { cmd: 'upload', err: res, xhr: xhr, rc: xhr.status }; self.trigger('uploadfail', res); // trigger "requestError" event self.trigger('requestError', errData); if (errData._event && errData._event.isDefaultPrevented()) { res.error = ''; } if (res._chunkfailure || res._multiupload) { abort = true; self.uploads.xhrUploading = false; notifyto && clearTimeout(notifyto); if (ntfUpload.length) { self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0}); dfrd.reject(res); } else { // for multi connection dfrd.reject(); } } else { dfrd.reject(res); } } else { dfrd.resolve(res); } }, false); xhr.upload.addEventListener('loadstart', function(e) { if (!chunkMerge && e.lengthComputable) { loaded = e.loaded; retry && (loaded = 0); filesize = e.total; if (!loaded) { loaded = parseInt(filesize * 0.05); } if (checkNotify()) { self.notify({type : 'upload', cnt : 0, progress : loaded - prev, size : data.multiupload? 0 : filesize}); prev = loaded; progress(); } } }, false); xhr.upload.addEventListener('progress', function(e) { var curr; if (e.lengthComputable && !chunkMerge && xhr.readyState < 2) { loaded = e.loaded; // to avoid strange bug in safari (not in chrome) with drag&drop. // bug: macos finder opened in any folder, // reset safari cache (option+command+e), reload elfinder page, // drop file from finder // on first attempt request starts (progress callback called ones) but never ends. // any next drop - successfull. if (!data.checked && loaded > 0 && !notifyto) { notifyto = startNotify(xhr._totalSize - loaded); } if (!filesize) { filesize = e.total; if (!loaded) { loaded = parseInt(filesize * 0.05); } } curr = loaded - prev; if (checkNotify() && (curr/e.total) >= 0.05) { self.notify({type : 'upload', cnt : 0, progress : curr, size : 0}); prev = loaded; progress(); } if (! data.multiupload && loaded >= filesize) { cancelBtn = false; cancelToggle(false); } } }, false); var send = function(files, paths){ var size = 0, fcnt = 1, sfiles = [], c = 0, total = cnt, maxFileSize, totalSize = 0, chunked = [], chunkID = new Date().getTime().toString().substr(-9), // for take care of the 32bit backend system BYTES_PER_CHUNK = Math.min((fm.uplMaxSize? fm.uplMaxSize : 2097152) - 8190, fm.options.uploadMaxChunkSize), // uplMaxSize margin 8kb or options.uploadMaxChunkSize blobSlice = chunkEnable? false : '', blobSize, blobMtime, i, start, end, chunks, blob, chunk, added, done, last, failChunk, multi = function(files, num){ var sfiles = [], cid, sfilesLen = 0, cancelChk; if (!abort) { while(files.length && sfiles.length < num) { sfiles.push(files.shift()); } sfilesLen = sfiles.length; if (sfilesLen) { cancelChk = sfilesLen; for (var i=0; i < sfilesLen; i++) { if (abort) { break; } cid = isDataType? (sfiles[i][0][0]._cid || null) : (sfiles[i][0]._cid || null); if (!!failChunk[cid]) { last--; continue; } fm.exec('upload', { type: data.type, isDataType: isDataType, files: sfiles[i], checked: true, target: target, dropEvt: dropEvt, renames: renames, hashes: hashes, multiupload: true, overwrite: data.overwrite === 0? 0 : void 0 }, void 0, target) .fail(function(error) { if (error && error === 'No such command') { abort = true; fm.error(['errUpload', 'errPerm']); } if (cid) { failChunk[cid] = true; } }) .always(function(e) { if (e && e.added) added = jQuery.merge(added, e.added); if (last <= ++done) { fm.trigger('multiupload', {added: added}); notifyto && clearTimeout(notifyto); if (checkNotify()) { self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0}); } } if (files.length) { multi(files, 1); // Next one } else { if (--cancelChk <= 1) { cancelBtn = false; cancelToggle(false); } } }); } } } if (sfiles.length < 1 || abort) { if (abort) { notifyto && clearTimeout(notifyto); if (cid) { failChunk[cid] = true; } dfrd.reject(); } else { dfrd.resolve(); self.uploads.xhrUploading = false; } } }, check = function(){ if (!self.uploads.xhrUploading) { self.uploads.xhrUploading = true; multi(sfiles, multiMax); // Max connection: 3 } else { setTimeout(check, 100); } }, reqId, err; if (! dataChecked && (isDataType || data.type == 'files')) { if (! (maxFileSize = fm.option('uploadMaxSize', target))) { maxFileSize = 0; } for (i=0; i < files.length; i++) { try { blob = files[i]; blobSize = blob.size; if (blobSlice === false) { blobSlice = ''; if (self.api >= 2.1) { if ('slice' in blob) { blobSlice = 'slice'; } else if ('mozSlice' in blob) { blobSlice = 'mozSlice'; } else if ('webkitSlice' in blob) { blobSlice = 'webkitSlice'; } } } } catch(e) { cnt--; total--; continue; } // file size check if ((maxFileSize && blobSize > maxFileSize) || (!blobSlice && fm.uplMaxSize && blobSize > fm.uplMaxSize)) { triggerError(['errUploadFile', blob.name, 'errUploadFileSize'], blob, true); cnt--; total--; continue; } // file mime check if (blob.type && ! self.uploadMimeCheck(blob.type, target)) { triggerError(['errUploadFile', blob.name, 'errUploadMime', '(' + blob.type + ')'], blob, true); cnt--; total--; continue; } if (blobSlice && blobSize > BYTES_PER_CHUNK) { start = 0; end = BYTES_PER_CHUNK; chunks = -1; total = Math.floor((blobSize - 1) / BYTES_PER_CHUNK); blobMtime = blob.lastModified? Math.round(blob.lastModified/1000) : 0; totalSize += blobSize; chunked[chunkID] = 0; while(start < blobSize) { chunk = blob[blobSlice](start, end); chunk._chunk = blob.name + '.' + (++chunks) + '_' + total + '.part'; chunk._cid = chunkID; chunk._range = start + ',' + chunk.size + ',' + blobSize; chunk._mtime = blobMtime; chunked[chunkID]++; if (size) { c++; } if (typeof sfiles[c] == 'undefined') { sfiles[c] = []; if (isDataType) { sfiles[c][0] = []; sfiles[c][1] = []; } } size = BYTES_PER_CHUNK; fcnt = 1; if (isDataType) { sfiles[c][0].push(chunk); sfiles[c][1].push(paths[i]); } else { sfiles[c].push(chunk); } start = end; end = start + BYTES_PER_CHUNK; } if (chunk == null) { triggerError(['errUploadFile', blob.name, 'errUploadFileSize'], blob, true); cnt--; total--; } else { total += chunks; size = 0; fcnt = 1; c++; } continue; } if ((fm.uplMaxSize && size + blobSize > fm.uplMaxSize) || fcnt > fm.uplMaxFile) { size = 0; fcnt = 1; c++; } if (typeof sfiles[c] == 'undefined') { sfiles[c] = []; if (isDataType) { sfiles[c][0] = []; sfiles[c][1] = []; } } if (isDataType) { sfiles[c][0].push(blob); sfiles[c][1].push(paths[i]); } else { sfiles[c].push(blob); } size += blobSize; totalSize += blobSize; fcnt++; } if (errors.length) { triggerError(); } if (sfiles.length == 0) { // no data data.checked = true; return false; } if (sfiles.length > 1) { // multi upload notifyto = startNotify(totalSize); added = []; done = 0; last = sfiles.length; failChunk = []; check(); return true; } // single upload if (isDataType) { files = sfiles[0][0]; paths = sfiles[0][1]; } else { files = sfiles[0]; } } if (!dataChecked) { if (!fm.UA.Safari || !data.files) { notifyto = startNotify(totalSize); } else { xhr._totalSize = totalSize; } } dataChecked = true; if (! files.length) { dfrd.reject(['errUploadNoFiles']); } xhr.open('POST', self.uploadURL, true); // set request headers if (fm.customHeaders) { jQuery.each(fm.customHeaders, function(key) { xhr.setRequestHeader(key, this); }); } // set xhrFields if (fm.xhrFields) { jQuery.each(fm.xhrFields, function(key) { if (key in xhr) { xhr[key] = this; } }); } if (self.api >= 2.1029) { // request ID reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16); formData.append('reqid', reqId); xhr._requestId = reqId; } formData.append('cmd', 'upload'); formData.append(self.newAPI ? 'target' : 'current', target); if (renames && renames.length) { jQuery.each(renames, function(i, v) { formData.append('renames[]', v); }); formData.append('suffix', fm.options.backupSuffix); } if (hashes) { jQuery.each(hashes, function(i, v) { formData.append('hashes['+ i +']', v); }); } jQuery.each(self.customData, function(key, val) { formData.append(key, val); }); jQuery.each(self.options.onlyMimes, function(i, mime) { formData.append('mimes[]', mime); }); jQuery.each(files, function(i, file) { if (file._chunkmerged) { formData.append('chunk', file._chunkmerged); formData.append('upload[]', file._name); formData.append('mtime[]', file._mtime); } else { if (file._chunkfail) { formData.append('upload[]', 'chunkfail'); formData.append('mimes', 'chunkfail'); } else { formData.append('upload[]', file); if (data.clipdata) { data.overwrite = 0; formData.append('name[]', fm.date(fm.nonameDateFormat) + '.png'); } if (file.name && fm.UA.iOS) { if (file.name.match(/^image\.jpe?g$/i)) { data.overwrite = 0; formData.append('name[]', fm.date(fm.nonameDateFormat) + '.jpg'); } else if (file.name.match(/^capturedvideo\.mov$/i)) { data.overwrite = 0; formData.append('name[]', fm.date(fm.nonameDateFormat) + '.mov'); } } } if (file._chunk) { formData.append('chunk', file._chunk); formData.append('cid' , file._cid); formData.append('range', file._range); formData.append('mtime[]', file._mtime); } else { formData.append('mtime[]', file.lastModified? Math.round(file.lastModified/1000) : 0); } } }); if (isDataType) { jQuery.each(paths, function(i, path) { formData.append('upload_path[]', path); }); } if (data.overwrite === 0) { formData.append('overwrite', 0); } // send int value that which meta key was pressed when dropped as `dropWith` if (dropEvt) { formData.append('dropWith', parseInt( (dropEvt.altKey ? '1' : '0')+ (dropEvt.ctrlKey ? '1' : '0')+ (dropEvt.metaKey ? '1' : '0')+ (dropEvt.shiftKey? '1' : '0'), 2)); } // set extraData on current request if (extraData) { jQuery.each(extraData, function(key, val) { formData.append(key, val); }); } xhr.send(formData); return true; }; if (! isDataType) { if (files.length > 0) { if (! data.clipdata && renames == null) { var mkdirs = [], paths = [], excludes = fm.options.folderUploadExclude[fm.OS] || null; jQuery.each(files, function(i, file) { var relPath = file.webkitRelativePath || file.relativePath || '', idx, rootDir; if (! relPath) { return false; } if (excludes && file.name.match(excludes)) { file._remove = true; relPath = void(0); } else { // add '/' as prefix to make same to folder uploading with DnD, see #2607 relPath = '/' + relPath.replace(/\/[^\/]*$/, '').replace(/^\//, ''); if (relPath && jQuery.inArray(relPath, mkdirs) === -1) { mkdirs.push(relPath); // checking the root directory to supports <input type="file" webkitdirectory> see #2378 idx = relPath.substr(1).indexOf('/'); if (idx !== -1 && (rootDir = relPath.substr(0, idx + 1)) && jQuery.inArray(rootDir, mkdirs) === -1) { mkdirs.unshift(rootDir); } } } paths.push(relPath); }); renames = []; hashes = {}; if (mkdirs.length) { (function() { var checkDirs = jQuery.map(mkdirs, function(name) { return name.substr(1).indexOf('/') === -1 ? {name: name.substr(1)} : null;}), cancelDirs = []; fm.uploads.checkExists(checkDirs, target, fm, true).done( function(res, res2) { var dfds = [], dfd, bak, hash; if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) { cancelDirs = jQuery.map(checkDirs, function(dir) { return dir._remove? dir.name : null ;} ); checkDirs = jQuery.grep(checkDirs, function(dir) { return !dir._remove? true : false ;} ); } if (cancelDirs.length) { jQuery.each(paths.concat(), function(i, path) { if (jQuery.inArray(path, cancelDirs) === 0) { files[i]._remove = true; paths[i] = void(0); } }); } files = jQuery.grep(files, function(file) { return file._remove? false : true; }); paths = jQuery.grep(paths, function(path) { return path === void 0 ? false : true; }); if (checkDirs.length) { dfd = jQuery.Deferred(); if (res.length) { jQuery.each(res, function(i, existName) { // backup bak = fm.uniqueName(existName + fm.options.backupSuffix , null, ''); jQuery.each(res2, function(h, name) { if (res[0] == name) { hash = h; return false; } }); if (! hash) { hash = fm.fileByName(res[0], target).hash; } fm.lockfiles({files : [hash]}); dfds.push( fm.request({ data : {cmd : 'rename', target : hash, name : bak}, notify : {type : 'rename', cnt : 1} }) .fail(function(error) { dfrd.reject(error); fm.sync(); }) .always(function() { fm.unlockfiles({files : [hash]}); }) ); }); } else { dfds.push(null); } jQuery.when.apply($, dfds).done(function() { // ensure directories fm.request({ data : {cmd : 'mkdir', target : target, dirs : mkdirs}, notify : {type : 'mkdir', cnt : mkdirs.length}, preventFail: true }) .fail(function(error) { error = error || ['errUnknown']; if (error[0] === 'errCmdParams') { multiMax = 1; } else { multiMax = 0; dfrd.reject(error); } }) .done(function(data) { var rm = false; if (!data.hashes) { data.hashes = {}; } paths = jQuery.map(paths.concat(), function(p, i) { if (p === '/') { return target; } else { if (data.hashes[p]) { return data.hashes[p]; } else { rm = true; files[i]._remove = true; return null; } } }); if (rm) { files = jQuery.grep(files, function(file) { return file._remove? false : true; }); } }) .always(function(data) { if (multiMax) { isDataType = true; if (! send(files, paths)) { dfrd.reject(); } } }); }); } else { dfrd.reject(); } } ); })(); } else { fm.uploads.checkExists(files, target, fm).done( function(res, res2){ if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) { hashes = res2; if (res === null) { data.overwrite = 0; } else { renames = res; } files = jQuery.grep(files, function(file){return !file._remove? true : false ;}); } cnt = files.length; if (cnt > 0) { if (! send(files)) { dfrd.reject(); } } else { dfrd.reject(); } } ); } } else { if (! send(files)) { dfrd.reject(); } } } else { dfrd.reject(); } } else { if (dataChecked) { send(files[0], files[1]); } else { files.done(function(result) { // result: [files, paths, renames, hashes, mkdirs] renames = []; cnt = result[0].length; if (cnt) { if (result[4] && result[4].length) { // ensure directories fm.request({ data : {cmd : 'mkdir', target : target, dirs : result[4]}, notify : {type : 'mkdir', cnt : result[4].length}, preventFail: true }) .fail(function(error) { error = error || ['errUnknown']; if (error[0] === 'errCmdParams') { multiMax = 1; } else { multiMax = 0; dfrd.reject(error); } }) .done(function(data) { var rm = false; if (!data.hashes) { data.hashes = {}; } result[1] = jQuery.map(result[1], function(p, i) { p = p.replace(/\/[^\/]*$/, ''); if (p === '') { return target; } else { if (data.hashes[p]) { return data.hashes[p]; } else { rm = true; result[0][i]._remove = true; return null; } } }); if (rm) { result[0] = jQuery.grep(result[0], function(file) { return file._remove? false : true; }); } }) .always(function(data) { if (multiMax) { renames = result[2]; hashes = result[3]; send(result[0], result[1]); } }); return; } else { result[1] = jQuery.map(result[1], function() { return target; }); } renames = result[2]; hashes = result[3]; send(result[0], result[1]); } else { dfrd.reject(['errUploadNoFiles']); } }).fail(function(){ dfrd.reject(); }); } } return dfrd; }, // upload transport using iframe iframe : function(data, fm) { var self = fm ? fm : this, input = data.input? data.input : false, files = !input ? self.uploads.checkFile(data, self) : false, dfrd = jQuery.Deferred() .fail(function(error) { error && self.error(error); }), name = 'iframe-'+fm.namespace+(++self.iframeCnt), form = jQuery('<form action="'+self.uploadURL+'" method="post" enctype="multipart/form-data" encoding="multipart/form-data" target="'+name+'" style="display:none"><input type="hidden" name="cmd" value="upload" /></form>'), msie = this.UA.IE, // clear timeouts, close notification dialog, remove form/iframe onload = function() { abortto && clearTimeout(abortto); notifyto && clearTimeout(notifyto); notify && self.notify({type : 'upload', cnt : -cnt}); setTimeout(function() { msie && jQuery('<iframe src="javascript:false;"/>').appendTo(form); form.remove(); iframe.remove(); }, 100); }, iframe = jQuery('<iframe src="'+(msie ? 'javascript:false;' : 'about:blank')+'" name="'+name+'" style="position:absolute;left:-1000px;top:-1000px" />') .on('load', function() { iframe.off('load') .on('load', function() { onload(); // data will be processed in callback response or window onmessage dfrd.resolve(); }); // notify dialog notifyto = setTimeout(function() { notify = true; self.notify({type : 'upload', cnt : cnt}); }, self.options.notifyDelay); // emulate abort on timeout if (self.options.iframeTimeout > 0) { abortto = setTimeout(function() { onload(); dfrd.reject(['errConnect', 'errTimeout']); }, self.options.iframeTimeout); } form.submit(); }), target = (data.target || self.cwd().hash), names = [], dfds = [], renames = [], hashes = {}, cnt, notify, notifyto, abortto; if (files && files.length) { jQuery.each(files, function(i, val) { form.append('<input type="hidden" name="upload[]" value="'+val+'"/>'); }); cnt = 1; } else if (input && jQuery(input).is(':file') && jQuery(input).val()) { if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) { names = input.files? input.files : [{ name: jQuery(input).val().replace(/^(?:.+[\\\/])?([^\\\/]+)$/, '$1') }]; //names = jQuery.map(names, function(file){return file.name? { name: file.name } : null ;}); dfds.push(self.uploads.checkExists(names, target, self).done( function(res, res2){ hashes = res2; if (res === null) { data.overwrite = 0; } else{ renames = res; cnt = jQuery.grep(names, function(file){return !file._remove? true : false ;}).length; if (cnt != names.length) { cnt = 0; } } } )); } cnt = input.files ? input.files.length : 1; form.append(input); } else { return dfrd.reject(); } jQuery.when.apply($, dfds).done(function() { if (cnt < 1) { return dfrd.reject(); } form.append('<input type="hidden" name="'+(self.newAPI ? 'target' : 'current')+'" value="'+target+'"/>') .append('<input type="hidden" name="html" value="1"/>') .append('<input type="hidden" name="node" value="'+self.id+'"/>') .append(jQuery(input).attr('name', 'upload[]')); if (renames.length > 0) { jQuery.each(renames, function(i, rename) { form.append('<input type="hidden" name="renames[]" value="'+self.escape(rename)+'"/>'); }); form.append('<input type="hidden" name="suffix" value="'+fm.options.backupSuffix+'"/>'); } if (hashes) { jQuery.each(renames, function(i, v) { form.append('<input type="hidden" name="['+i+']" value="'+self.escape(v)+'"/>'); }); } if (data.overwrite === 0) { form.append('<input type="hidden" name="overwrite" value="0"/>'); } jQuery.each(self.options.onlyMimes||[], function(i, mime) { form.append('<input type="hidden" name="mimes[]" value="'+self.escape(mime)+'"/>'); }); jQuery.each(self.customData, function(key, val) { form.append('<input type="hidden" name="'+key+'" value="'+self.escape(val)+'"/>'); }); form.appendTo('body'); iframe.appendTo('body'); }); return dfrd; } }, /** * Bind callback to event(s) The callback is executed at most once per event. * To bind to multiply events at once, separate events names by space * * @param String event name * @param Function callback * @param Boolan priority first * @return elFinder */ one : function(ev, callback, priorityFirst) { var self = this, event = ev.toLowerCase(), h = function(e, f) { if (!self.toUnbindEvents[event]) { self.toUnbindEvents[event] = []; } self.toUnbindEvents[event].push({ type: event, callback: h }); return (callback.done? callback.done : callback).apply(this, arguments); }; if (callback.done) { h = {done: h}; } return this.bind(event, h, priorityFirst); }, /** * Set/get data into/from localStorage * * @param String key * @param String|void value * @return String|null */ localStorage : function(key, val) { var self = this, s = window.localStorage, oldkey = 'elfinder-'+(key || '')+this.id, // old key of elFinder < 2.1.6 prefix = window.location.pathname+'-elfinder-', suffix = this.id, clrs = [], retval, oldval, t, precnt, sufcnt; // reset this node data if (typeof(key) === 'undefined') { precnt = prefix.length; sufcnt = suffix.length * -1; jQuery.each(s, function(key) { if (key.substr(0, precnt) === prefix && key.substr(sufcnt) === suffix) { clrs.push(key); } }); jQuery.each(clrs, function(i, key) { s.removeItem(key); }); return true; } // new key of elFinder >= 2.1.6 key = prefix+key+suffix; if (val === null) { return s.removeItem(key); } if (val === void(0) && !(retval = s.getItem(key)) && (oldval = s.getItem(oldkey))) { val = oldval; s.removeItem(oldkey); } if (val !== void(0)) { t = typeof val; if (t !== 'string' && t !== 'number') { val = JSON.stringify(val); } try { s.setItem(key, val); } catch (e) { try { s.clear(); s.setItem(key, val); } catch (e) { self.debug('error', e.toString()); } } retval = s.getItem(key); } if (retval && (retval.substr(0,1) === '{' || retval.substr(0,1) === '[')) { try { return JSON.parse(retval); } catch(e) {} } return retval; }, /** * Get/set cookie * * @param String cookie name * @param String|void cookie value * @return String|null */ cookie : function(name, value) { var d, o, c, i, retval, t; name = 'elfinder-'+name+this.id; if (value === void(0)) { if (document.cookie && document.cookie != '') { c = document.cookie.split(';'); name += '='; for (i=0; i<c.length; i++) { c[i] = jQuery.trim(c[i]); if (c[i].substring(0, name.length) == name) { retval = decodeURIComponent(c[i].substring(name.length)); if (retval.substr(0,1) === '{' || retval.substr(0,1) === '[') { try { return JSON.parse(retval); } catch(e) {} } return retval; } } } return null; } o = Object.assign({}, this.options.cookie); if (value === null) { value = ''; o.expires = -1; } else { t = typeof value; if (t !== 'string' && t !== 'number') { value = JSON.stringify(value); } } if (typeof(o.expires) == 'number') { d = new Date(); d.setTime(d.getTime()+(o.expires * 86400000)); o.expires = d; } document.cookie = name+'='+encodeURIComponent(value)+'; expires='+o.expires.toUTCString()+(o.path ? '; path='+o.path : '')+(o.domain ? '; domain='+o.domain : '')+(o.secure ? '; secure' : ''); if (value && (value.substr(0,1) === '{' || value.substr(0,1) === '[')) { try { return JSON.parse(value); } catch(e) {} } return value; }, /** * Get start directory (by location.hash or last opened directory) * * @return String */ startDir : function() { var locHash = window.location.hash; if (locHash && locHash.match(/^#elf_/)) { return locHash.replace(/^#elf_/, ''); } else if (this.options.startPathHash) { return this.options.startPathHash; } else { return this.lastDir(); } }, /** * Get/set last opened directory * * @param String|undefined dir hash * @return String */ lastDir : function(hash) { return this.options.rememberLastDir ? this.storage('lastdir', hash) : ''; }, /** * Node for escape html entities in texts * * @type jQuery */ _node : jQuery('<span/>'), /** * Replace not html-safe symbols to html entities * * @param String text to escape * @return String */ escape : function(name) { return this._node.text(name).html().replace(/"/g, '"').replace(/'/g, '''); }, /** * Cleanup ajax data. * For old api convert data into new api format * * @param String command name * @param Object data from backend * @return Object */ normalize : function(data) { var self = this, fileFilter = (function() { var func, filter; if (filter = self.options.fileFilter) { if (typeof filter === 'function') { func = function(file) { return filter.call(self, file); }; } else if (filter instanceof RegExp) { func = function(file) { return filter.test(file.name); }; } } return func? func : null; })(), chkCmdMap = function(opts) { // Disable command to replace with other command var disabled; if (opts.uiCmdMap) { if (jQuery.isPlainObject(opts.uiCmdMap) && Object.keys(opts.uiCmdMap).length) { if (!opts.disabledFlip) { opts.disabledFlip = {}; } disabled = opts.disabledFlip; jQuery.each(opts.uiCmdMap, function(f, t) { if (t === 'hidden' && !disabled[f]) { opts.disabled.push(f); opts.disabledFlip[f] = true; } }); } else { delete opts.uiCmdMap; } } }, normalizeOptions = function(opts) { var getType = function(v) { var type = typeof v; if (type === 'object' && Array.isArray(v)) { type = 'array'; } return type; }; jQuery.each(self.optionProperties, function(k, empty) { if (empty !== void(0)) { if (opts[k] && getType(opts[k]) !== getType(empty)) { opts[k] = empty; } } }); if (opts['disabled']) { opts['disabledFlip'] = self.arrayFlip(opts['disabled'], true); } else { opts['disabledFlip'] = {}; } return opts; }, filter = function(file, asMap, type) { var res = asMap? file : true, ign = asMap? null : false, vid, targetOptions, isRoot, rootNames; if (file && file.hash && file.name && file.mime) { if (file.mime === 'application/x-empty') { file.mime = 'text/plain'; } isRoot = self.isRoot(file); if (isRoot && ! file.volumeid) { self.debug('warning', 'The volume root statuses requires `volumeid` property.'); } if (isRoot || file.mime === 'directory') { // Prevention of circular reference if (file.phash) { if (file.phash === file.hash) { error = error.concat(['Parent folder of "$1" is itself.', file.name]); return ign; } if (isRoot && file.volumeid && file.phash.indexOf(file.volumeid) === 0) { error = error.concat(['Parent folder of "$1" is inner itself.', file.name]); return ign; } } // set options, tmbUrls for each volume if (file.volumeid) { vid = file.volumeid; if (isRoot) { // make or update of leaf roots cache if (file.phash) { if (! self.leafRoots[file.phash]) { self.leafRoots[file.phash] = [ file.hash ]; } else { if (jQuery.inArray(file.hash, self.leafRoots[file.phash]) === -1) { self.leafRoots[file.phash].push(file.hash); } } } self.hasVolOptions = true; if (! self.volOptions[vid]) { self.volOptions[vid] = { // set dispInlineRegex dispInlineRegex: self.options.dispInlineRegex }; } targetOptions = self.volOptions[vid]; if (file.options) { // >= v.2.1.14 has file.options Object.assign(targetOptions, file.options); } // for compat <= v2.1.13 if (file.disabled) { targetOptions.disabled = file.disabled; targetOptions.disabledFlip = self.arrayFlip(file.disabled, true); } if (file.tmbUrl) { targetOptions.tmbUrl = file.tmbUrl; } // '/' required at the end of url if (targetOptions.url && targetOptions.url.substr(-1) !== '/') { targetOptions.url += '/'; } // check uiCmdMap chkCmdMap(targetOptions); // check trash bin hash if (targetOptions.trashHash) { if (self.trashes[targetOptions.trashHash] === false) { delete targetOptions.trashHash; } else { self.trashes[targetOptions.trashHash] = file.hash; } } // set immediate properties jQuery.each(self.optionProperties, function(k) { if (targetOptions[k]) { file[k] = targetOptions[k]; } }); // regist fm.roots if (type !== 'cwd') { self.roots[vid] = file.hash; } // regist fm.volumeExpires if (file.expires) { self.volumeExpires[vid] = file.expires; } } if (prevId !== vid) { prevId = vid; i18nFolderName = self.option('i18nFolderName', vid); } } // volume root i18n name if (isRoot && ! file.i18) { name = 'volume_' + file.name, i18 = self.i18n(false, name); if (name !== i18) { file.i18 = i18; } } // i18nFolderName if (i18nFolderName && ! file.i18) { name = 'folder_' + file.name, i18 = self.i18n(false, name); if (name !== i18) { file.i18 = i18; } } if (isRoot) { if (rootNames = self.storage('rootNames')) { if (rootNames[file.hash]) { file._name = file.name; file._i18 = file.i18; file.name = rootNames[file.hash] = rootNames[file.hash]; delete file.i18; } self.storage('rootNames', rootNames); } } // lock trash bins holder if (self.trashes[file.hash]) { file.locked = true; } } else { if (fileFilter) { try { if (! fileFilter(file)) { return ign; } } catch(e) { self.debug(e); } } if (file.size == 0) { file.mime = self.getMimetype(file.name, file.mime); } } if (file.options) { self.optionsByHashes[file.hash] = normalizeOptions(file.options); } delete file.options; return res; } return ign; }, getDescendants = function(hashes) { var res = []; jQuery.each(self.files(), function(h, f) { jQuery.each(self.parents(h), function(i, ph) { if (jQuery.inArray(ph, hashes) !== -1 && jQuery.inArray(h, hashes) === -1) { res.push(h); return false; } }); }); return res; }, applyLeafRootStats = function(dataArr, type) { jQuery.each(dataArr, function(i, f) { var pfile, done; if (self.leafRoots[f.hash]) { self.applyLeafRootStats(f); } // update leaf root parent stat if (type !== 'change' && f.phash && self.isRoot(f) && (pfile = self.file(f.phash))) { self.applyLeafRootStats(pfile); // add to data.changed if (!data.changed) { data.changed = [pfile]; } else { jQuery.each(data.changed, function(i, f) { if (f.hash === pfile.hash) { data.changed[i] = pfile; done = true; return false; } }); if (!done) { data.changed.push(pfile); } } } }); }, error = [], name, i18, i18nFolderName, prevId, cData; // set cunstom data if (data.customData && data.customData !== self.prevCustomData) { self.prevCustomData = data.customData; try { cData = JSON.parse(data.customData); if (jQuery.isPlainObject(cData)) { self.prevCustomData = cData; jQuery.each(Object.keys(cData), function(i, key) { if (cData[key] === null) { delete cData[key]; delete self.optsCustomData[key]; } }); self.customData = Object.assign({}, self.optsCustomData, cData); } } catch(e) {} } if (data.options) { normalizeOptions(data.options); } if (data.cwd) { if (data.cwd.volumeid && data.options && Object.keys(data.options).length && self.isRoot(data.cwd)) { self.hasVolOptions = true; self.volOptions[data.cwd.volumeid] = data.options; } data.cwd = filter(data.cwd, true, 'cwd'); } if (data.files) { data.files = jQuery.grep(data.files, filter); } if (data.tree) { data.tree = jQuery.grep(data.tree, filter); } if (data.added) { data.added = jQuery.grep(data.added, filter); } if (data.changed) { data.changed = jQuery.grep(data.changed, filter); } if (data.removed && data.removed.length && self.searchStatus.state === 2) { data.removed = data.removed.concat(getDescendants(data.removed)); } if (data.api) { data.init = true; } if (Object.keys(self.leafRoots).length) { data.files && applyLeafRootStats(data.files); data.tree && applyLeafRootStats(data.tree); data.added && applyLeafRootStats(data.added); data.changed && applyLeafRootStats(data.changed, 'change'); } // merge options that apply only to cwd if (data.cwd && data.cwd.options && data.options) { Object.assign(data.options, normalizeOptions(data.cwd.options)); } // '/' required at the end of url if (data.options && data.options.url && data.options.url.substr(-1) !== '/') { data.options.url += '/'; } // check error if (error.length) { data.norError = ['errResponse'].concat(error); } return data; }, /** * Update sort options * * @param {String} sort type * @param {String} sort order * @param {Boolean} show folder first */ setSort : function(type, order, stickFolders, alsoTreeview) { this.storage('sortType', (this.sortType = this.sortRules[type] ? type : 'name')); this.storage('sortOrder', (this.sortOrder = /asc|desc/.test(order) ? order : 'asc')); this.storage('sortStickFolders', (this.sortStickFolders = !!stickFolders) ? 1 : ''); this.storage('sortAlsoTreeview', (this.sortAlsoTreeview = !!alsoTreeview) ? 1 : ''); this.trigger('sortchange'); }, _sortRules : { name : function(file1, file2) { return elFinder.prototype.naturalCompare(file1.i18 || file1.name, file2.i18 || file2.name); }, size : function(file1, file2) { var size1 = parseInt(file1.size) || 0, size2 = parseInt(file2.size) || 0; return size1 === size2 ? 0 : size1 > size2 ? 1 : -1; }, kind : function(file1, file2) { return elFinder.prototype.naturalCompare(file1.mime, file2.mime); }, date : function(file1, file2) { var date1 = file1.ts || file1.date || 0, date2 = file2.ts || file2.date || 0; return date1 === date2 ? 0 : date1 > date2 ? 1 : -1; }, perm : function(file1, file2) { var val = function(file) { return (file.write? 2 : 0) + (file.read? 1 : 0); }, v1 = val(file1), v2 = val(file2); return v1 === v2 ? 0 : v1 > v2 ? 1 : -1; }, mode : function(file1, file2) { var v1 = file1.mode || (file1.perm || ''), v2 = file2.mode || (file2.perm || ''); return elFinder.prototype.naturalCompare(v1, v2); }, owner : function(file1, file2) { var v1 = file1.owner || '', v2 = file2.owner || ''; return elFinder.prototype.naturalCompare(v1, v2); }, group : function(file1, file2) { var v1 = file1.group || '', v2 = file2.group || ''; return elFinder.prototype.naturalCompare(v1, v2); } }, /** * Valid sort rule names * * @type Object */ sorters : {}, /** * Compare strings for natural sort * * @param String * @param String * @return Number */ naturalCompare : function(a, b) { var self = elFinder.prototype.naturalCompare; if (typeof self.loc == 'undefined') { self.loc = (navigator.userLanguage || navigator.browserLanguage || navigator.language || 'en-US'); } if (typeof self.sort == 'undefined') { if ('11'.localeCompare('2', self.loc, {numeric: true}) > 0) { // Native support if (window.Intl && window.Intl.Collator) { self.sort = new Intl.Collator(self.loc, {numeric: true}).compare; } else { self.sort = function(a, b) { return a.localeCompare(b, self.loc, {numeric: true}); }; } } else { /* * Edited for elFinder (emulates localeCompare() by numeric) by Naoki Sawada aka nao-pon */ /* * Huddle/javascript-natural-sort (https://github.com/Huddle/javascript-natural-sort) */ /* * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license * Author: Jim Palmer (based on chunking idea from Dave Koelle) * http://opensource.org/licenses/mit-license.php */ self.sort = function(a, b) { var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi, sre = /(^[ ]*|[ ]*$)/g, dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, hre = /^0x[0-9a-f]+$/i, ore = /^0/, syre = /^[\x01\x21-\x2f\x3a-\x40\x5b-\x60\x7b-\x7e]/, // symbol first - (Naoki Sawada) i = function(s) { return self.sort.insensitive && (''+s).toLowerCase() || ''+s; }, // convert all to strings strip whitespace // first character is "_", it's smallest - (Naoki Sawada) x = i(a).replace(sre, '').replace(/^_/, "\x01") || '', y = i(b).replace(sre, '').replace(/^_/, "\x01") || '', // chunk/tokenize xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), // numeric, hex or date detection xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)), yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null, oFxNcL, oFyNcL, locRes = 0; // first try and sort Hex codes or Dates if (yD) { if ( xD < yD ) return -1; else if ( xD > yD ) return 1; } // natural sorting through split numeric strings and default strings for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) { // find floats not starting with '0', string or 0 if not defined (Clint Priest) oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0; oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0; // handle numeric vs string comparison - number < string - (Kyle Adams) // but symbol first < number - (Naoki Sawada) if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { if (isNaN(oFxNcL) && (typeof oFxNcL !== 'string' || ! oFxNcL.match(syre))) { return 1; } else if (typeof oFyNcL !== 'string' || ! oFyNcL.match(syre)) { return -1; } } // use decimal number comparison if either value is string zero if (parseInt(oFxNcL, 10) === 0) oFxNcL = 0; if (parseInt(oFyNcL, 10) === 0) oFyNcL = 0; // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' if (typeof oFxNcL !== typeof oFyNcL) { oFxNcL += ''; oFyNcL += ''; } // use locale sensitive sort for strings when case insensitive // note: localeCompare interleaves uppercase with lowercase (e.g. A,a,B,b) if (self.sort.insensitive && typeof oFxNcL === 'string' && typeof oFyNcL === 'string') { locRes = oFxNcL.localeCompare(oFyNcL, self.loc); if (locRes !== 0) return locRes; } if (oFxNcL < oFyNcL) return -1; if (oFxNcL > oFyNcL) return 1; } return 0; }; self.sort.insensitive = true; } } return self.sort(a, b); }, /** * Compare files based on elFinder.sort * * @param Object file * @param Object file * @return Number */ compare : function(file1, file2) { var self = this, type = self.sortType, asc = self.sortOrder == 'asc', stick = self.sortStickFolders, rules = self.sortRules, sort = rules[type], d1 = file1.mime == 'directory', d2 = file2.mime == 'directory', res; if (stick) { if (d1 && !d2) { return -1; } else if (!d1 && d2) { return 1; } } res = asc ? sort(file1, file2) : sort(file2, file1); return type !== 'name' && res === 0 ? res = asc ? rules.name(file1, file2) : rules.name(file2, file1) : res; }, /** * Sort files based on config * * @param Array files * @return Array */ sortFiles : function(files) { return files.sort(this.compare); }, /** * Open notification dialog * and append/update message for required notification type. * * @param Object options * @example * this.notify({ * type : 'copy', * msg : 'Copy files', // not required for known types @see this.notifyType * cnt : 3, * hideCnt : false, // true for not show count * progress : 10, // progress bar percents (use cnt : 0 to update progress bar) * cancel : callback // callback function for cancel button * }) * @return elFinder */ notify : function(opts) { var type = opts.type, id = opts.id? 'elfinder-notify-'+opts.id : '', msg = this.i18n((typeof opts.msg !== 'undefined')? opts.msg : (this.messages['ntf'+type] ? 'ntf'+type : 'ntfsmth')), ndialog = this.ui.notify, notify = ndialog.children('.elfinder-notify-'+type+(id? ('.'+id) : '')), button = notify.children('div.elfinder-notify-cancel').children('button'), ntpl = '<div class="elfinder-notify elfinder-notify-{type}'+(id? (' '+id) : '')+'"><span class="elfinder-dialog-icon elfinder-dialog-icon-{type}"/><span class="elfinder-notify-msg">{msg}</span> <span class="elfinder-notify-cnt"/><div class="elfinder-notify-progressbar"><div class="elfinder-notify-progress"/></div><div class="elfinder-notify-cancel"/></div>', delta = opts.cnt, size = (typeof opts.size != 'undefined')? parseInt(opts.size) : null, progress = (typeof opts.progress != 'undefined' && opts.progress >= 0) ? opts.progress : null, cancel = opts.cancel, clhover = 'ui-state-hover', close = function() { notify._esc && jQuery(document).off('keydown', notify._esc); notify.remove(); !ndialog.children().length && ndialog.elfinderdialog('close'); }, cnt, total, prc; if (!type) { return this; } if (!notify.length) { notify = jQuery(ntpl.replace(/\{type\}/g, type).replace(/\{msg\}/g, msg)) .appendTo(ndialog) .data('cnt', 0); if (progress != null) { notify.data({progress : 0, total : 0}); } if (cancel) { button = jQuery('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">'+this.i18n('btnCancel')+'</span></button>') .on('mouseenter mouseleave', function(e) { jQuery(this).toggleClass(clhover, e.type === 'mouseenter'); }); notify.children('div.elfinder-notify-cancel').append(button); } } else if (typeof opts.msg !== 'undefined') { notify.children('span.elfinder-notify-msg').html(msg); } cnt = delta + parseInt(notify.data('cnt')); if (cnt > 0) { if (cancel && button.length) { if (jQuery.isFunction(cancel) || (typeof cancel === 'object' && cancel.promise)) { notify._esc = function(e) { if (e.type == 'keydown' && e.keyCode != jQuery.ui.keyCode.ESCAPE) { return; } e.preventDefault(); e.stopPropagation(); close(); if (cancel.promise) { cancel.reject(0); // 0 is canceling flag } else { cancel(e); } }; button.on('click', function(e) { notify._esc(e); }); jQuery(document).on('keydown.' + this.namespace, notify._esc); } } !opts.hideCnt && notify.children('.elfinder-notify-cnt').text('('+cnt+')'); ndialog.is(':hidden') && ndialog.elfinderdialog('open', this).height('auto'); notify.data('cnt', cnt); if ((progress != null) && (total = notify.data('total')) >= 0 && (prc = notify.data('progress')) >= 0) { total += size != null? size : delta; prc += progress; (size == null && delta < 0) && (prc += delta * 100); notify.data({progress : prc, total : total}); if (size != null) { prc *= 100; total = Math.max(1, total); } progress = parseInt(prc/total); notify.find('.elfinder-notify-progress') .animate({ width : (progress < 100 ? progress : 100)+'%' }, 20); } } else { close(); } return this; }, /** * Open confirmation dialog * * @param Object options * @example * this.confirm({ * cssClass : 'elfinder-confirm-mydialog', * title : 'Remove files', * text : 'Here is question text', * accept : { // accept callback - required * label : 'Continue', * callback : function(applyToAll) { fm.log('Ok') } * }, * cancel : { // cancel callback - required * label : 'Cancel', * callback : function() { fm.log('Cancel')} * }, * reject : { // reject callback - optionally * label : 'No', * callback : function(applyToAll) { fm.log('No')} * }, * buttons : [ // additional buttons callback - optionally * { * label : 'Btn1', * callback : function(applyToAll) { fm.log('Btn1')} * } * ], * all : true // display checkbox "Apply to all" * }) * @return elFinder */ confirm : function(opts) { var self = this, complete = false, options = { cssClass : 'elfinder-dialog-confirm', modal : true, resizable : false, title : this.i18n(opts.title || 'confirmReq'), buttons : {}, close : function() { !complete && opts.cancel.callback(); jQuery(this).elfinderdialog('destroy'); } }, apply = this.i18n('apllyAll'), label, checkbox, btnNum; if (opts.cssClass) { options.cssClass += ' ' + opts.cssClass; } options.buttons[this.i18n(opts.accept.label)] = function() { opts.accept.callback(!!(checkbox && checkbox.prop('checked'))); complete = true; jQuery(this).elfinderdialog('close'); }; options.buttons[this.i18n(opts.accept.label)]._cssClass = 'elfinder-confirm-accept'; if (opts.reject) { options.buttons[this.i18n(opts.reject.label)] = function() { opts.reject.callback(!!(checkbox && checkbox.prop('checked'))); complete = true; jQuery(this).elfinderdialog('close'); }; options.buttons[this.i18n(opts.reject.label)]._cssClass = 'elfinder-confirm-reject'; } if (opts.buttons && opts.buttons.length > 0) { btnNum = 1; jQuery.each(opts.buttons, function(i, v){ options.buttons[self.i18n(v.label)] = function() { v.callback(!!(checkbox && checkbox.prop('checked'))); complete = true; jQuery(this).elfinderdialog('close'); }; options.buttons[self.i18n(v.label)]._cssClass = 'elfinder-confirm-extbtn' + (btnNum++); if (v.cssClass) { options.buttons[self.i18n(v.label)]._cssClass += ' ' + v.cssClass; } }); } options.buttons[this.i18n(opts.cancel.label)] = function() { jQuery(this).elfinderdialog('close'); }; options.buttons[this.i18n(opts.cancel.label)]._cssClass = 'elfinder-confirm-cancel'; if (opts.all) { options.create = function() { var base = jQuery('<div class="elfinder-dialog-confirm-applyall"/>'); checkbox = jQuery('<input type="checkbox" />'); jQuery(this).next().find('.ui-dialog-buttonset') .prepend(base.append(jQuery('<label>'+apply+'</label>').prepend(checkbox))); }; } if (opts.optionsCallback && jQuery.isFunction(opts.optionsCallback)) { opts.optionsCallback(options); } return this.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-confirm"/>' + this.i18n(opts.text), options); }, /** * Create unique file name in required dir * * @param String file name * @param String parent dir hash * @param String glue * @return String */ uniqueName : function(prefix, phash, glue) { var i = 0, ext = '', p, name; prefix = this.i18n(false, prefix); phash = phash || this.cwd().hash; glue = (typeof glue === 'undefined')? ' ' : glue; if (p = prefix.match(/^(.+)(\.[^.]+)$/)) { ext = p[2]; prefix = p[1]; } name = prefix+ext; if (!this.fileByName(name, phash)) { return name; } while (i < 10000) { name = prefix + glue + (++i) + ext; if (!this.fileByName(name, phash)) { return name; } } return prefix + Math.random() + ext; }, /** * Return message translated onto current language * Allowed accept HTML element that was wrapped in jQuery object * To be careful to XSS vulnerability of HTML element Ex. You should use `fm.escape(file.name)` * * @param String|Array message[s]|Object jQuery * @return String **/ i18n : function() { var self = this, messages = this.messages, input = [], ignore = [], message = function(m) { var file; if (m.indexOf('#') === 0) { if ((file = self.file(m.substr(1)))) { return file.name; } } return m; }, i, j, m, escFunc, start = 0, isErr; if (arguments.length && arguments[0] === false) { escFunc = function(m){ return m; }; start = 1; } for (i = start; i< arguments.length; i++) { m = arguments[i]; if (Array.isArray(m)) { for (j = 0; j < m.length; j++) { if (m[j] instanceof jQuery) { // jQuery object is HTML element input.push(m[j]); } else if (typeof m[j] !== 'undefined'){ input.push(message('' + m[j])); } } } else if (m instanceof jQuery) { // jQuery object is HTML element input.push(m[j]); } else if (typeof m !== 'undefined'){ input.push(message('' + m)); } } for (i = 0; i < input.length; i++) { // dont translate placeholders if (jQuery.inArray(i, ignore) !== -1) { continue; } m = input[i]; if (typeof m == 'string') { isErr = !!(messages[m] && m.match(/^err/)); // translate message m = messages[m] || (escFunc? escFunc(m) : self.escape(m)); // replace placeholders in message m = m.replace(/\$(\d+)/g, function(match, placeholder) { var res; placeholder = i + parseInt(placeholder); if (placeholder > 0 && input[placeholder]) { ignore.push(placeholder); } res = escFunc? escFunc(input[placeholder]) : self.escape(input[placeholder]); if (isErr) { res = '<span class="elfinder-err-var elfinder-err-var' + placeholder + '">' + res + '</span>'; } return res; }); } else { // get HTML from jQuery object m = m.get(0).outerHTML; } input[i] = m; } return jQuery.grep(input, function(m, i) { return jQuery.inArray(i, ignore) === -1 ? true : false; }).join('<br>'); }, /** * Get icon style from file.icon * * @param Object elFinder file object * @return String|Object */ getIconStyle : function(file, asObject) { var self = this, template = { 'background' : 'url(\'{url}\') 0 0 no-repeat', 'background-size' : 'contain' }, style = '', cssObj = {}, i = 0; if (file.icon) { style = 'style="'; jQuery.each(template, function(k, v) { if (i++ === 0) { v = v.replace('{url}', self.escape(file.icon)); } if (asObject) { cssObj[k] = v; } else { style += k+':'+v+';'; } }); style += '"'; } return asObject? cssObj : style; }, /** * Convert mimetype into css classes * * @param String file mimetype * @return String */ mime2class : function(mimeType) { var prefix = 'elfinder-cwd-icon-', mime = mimeType.toLowerCase(), isText = this.textMimes[mime]; mime = mime.split('/'); if (isText) { mime[0] += ' ' + prefix + 'text'; } else if (mime[1] && mime[1].match(/\+xml$/)) { mime[0] += ' ' + prefix + 'xml'; } return prefix + mime[0] + (mime[1] ? ' ' + prefix + mime[1].replace(/(\.|\+)/g, '-') : ''); }, /** * Return localized kind of file * * @param Object|String file or file mimetype * @return String */ mime2kind : function(f) { var isObj = typeof(f) == 'object' ? true : false, mime = isObj ? f.mime : f, kind; if (isObj && f.alias && mime != 'symlink-broken') { kind = 'Alias'; } else if (this.kinds[mime]) { if (isObj && mime === 'directory' && (! f.phash || f.isroot)) { kind = 'Root'; } else { kind = this.kinds[mime]; } } if (! kind) { if (mime.indexOf('text') === 0) { kind = 'Text'; } else if (mime.indexOf('image') === 0) { kind = 'Image'; } else if (mime.indexOf('audio') === 0) { kind = 'Audio'; } else if (mime.indexOf('video') === 0) { kind = 'Video'; } else if (mime.indexOf('application') === 0) { kind = 'App'; } else { kind = mime; } } return this.messages['kind'+kind] ? this.i18n('kind'+kind) : mime; }, /** * Return boolean Is mime-type text file * * @param String mime-type * @return Boolean */ mimeIsText : function(mime) { return (this.textMimes[mime.toLowerCase()] || (mime.indexOf('text/') === 0 && mime.substr(5, 3) !== 'rtf') || mime.match(/^application\/.+\+xml$/))? true : false; }, /** * Returns a date string formatted according to the given format string * * @param String format string * @param Object Date object * @return String */ date : function(format, date) { var self = this, output, d, dw, m, y, h, g, i, s; if (! date) { date = new Date(); } h = date[self.getHours](); g = h > 12 ? h - 12 : h; i = date[self.getMinutes](); s = date[self.getSeconds](); d = date[self.getDate](); dw = date[self.getDay](); m = date[self.getMonth]() + 1; y = date[self.getFullYear](); output = format.replace(/[a-z]/gi, function(val) { switch (val) { case 'd': return d > 9 ? d : '0'+d; case 'j': return d; case 'D': return self.i18n(self.i18.daysShort[dw]); case 'l': return self.i18n(self.i18.days[dw]); case 'm': return m > 9 ? m : '0'+m; case 'n': return m; case 'M': return self.i18n(self.i18.monthsShort[m-1]); case 'F': return self.i18n(self.i18.months[m-1]); case 'Y': return y; case 'y': return (''+y).substr(2); case 'H': return h > 9 ? h : '0'+h; case 'G': return h; case 'g': return g; case 'h': return g > 9 ? g : '0'+g; case 'a': return h >= 12 ? 'pm' : 'am'; case 'A': return h >= 12 ? 'PM' : 'AM'; case 'i': return i > 9 ? i : '0'+i; case 's': return s > 9 ? s : '0'+s; } return val; }); return output; }, /** * Return localized date * * @param Object file object * @return String */ formatDate : function(file, t) { var self = this, ts = t || file.ts, i18 = self.i18, date, format, output, d, dw, m, y, h, g, i, s; if (self.options.clientFormatDate && ts > 0) { date = new Date(ts*1000); format = ts >= this.yesterday ? this.fancyFormat : this.dateFormat; output = self.date(format, date); return ts >= this.yesterday ? output.replace('$1', this.i18n(ts >= this.today ? 'Today' : 'Yesterday')) : output; } else if (file.date) { return file.date.replace(/([a-z]+)\s/i, function(a1, a2) { return self.i18n(a2)+' '; }); } return self.i18n('dateUnknown'); }, /** * Return localized number string * * @param Number * @return String */ toLocaleString : function(num) { var v = new Number(num); if (v) { if (v.toLocaleString) { return v.toLocaleString(); } else { return String(num).replace( /(\d)(?=(\d\d\d)+(?!\d))/g, '$1,'); } } return num; }, /** * Return css class marks file permissions * * @param Object file * @return String */ perms2class : function(o) { var c = ''; if (!o.read && !o.write) { c = 'elfinder-na'; } else if (!o.read) { c = 'elfinder-wo'; } else if (!o.write) { c = 'elfinder-ro'; } if (o.type) { c += ' elfinder-' + this.escape(o.type); } return c; }, /** * Return localized string with file permissions * * @param Object file * @return String */ formatPermissions : function(f) { var p = []; f.read && p.push(this.i18n('read')); f.write && p.push(this.i18n('write')); return p.length ? p.join(' '+this.i18n('and')+' ') : this.i18n('noaccess'); }, /** * Return formated file size * * @param Number file size * @return String */ formatSize : function(s) { var n = 1, u = 'b'; if (s == 'unknown') { return this.i18n('unknown'); } if (s > 1073741824) { n = 1073741824; u = 'GB'; } else if (s > 1048576) { n = 1048576; u = 'MB'; } else if (s > 1024) { n = 1024; u = 'KB'; } s = s/n; return (s > 0 ? n >= 1048576 ? s.toFixed(2) : Math.round(s) : 0) +' '+u; }, /** * Return formated file mode by options.fileModeStyle * * @param String file mode * @param String format style * @return String */ formatFileMode : function(p, style) { var i, o, s, b, sticy, suid, sgid, str, oct; if (!style) { style = this.options.fileModeStyle.toLowerCase(); } p = jQuery.trim(p); if (p.match(/[rwxs-]{9}$/i)) { str = p = p.substr(-9); if (style == 'string') { return str; } oct = ''; s = 0; for (i=0; i<7; i=i+3) { o = p.substr(i, 3); b = 0; if (o.match(/[r]/i)) { b += 4; } if (o.match(/[w]/i)) { b += 2; } if (o.match(/[xs]/i)) { if (o.match(/[xs]/)) { b += 1; } if (o.match(/[s]/i)) { if (i == 0) { s += 4; } else if (i == 3) { s += 2; } } } oct += b.toString(8); } if (s) { oct = s.toString(8) + oct; } } else { p = parseInt(p, 8); oct = p? p.toString(8) : ''; if (!p || style == 'octal') { return oct; } o = p.toString(8); s = 0; if (o.length > 3) { o = o.substr(-4); s = parseInt(o.substr(0, 1), 8); o = o.substr(1); } sticy = ((s & 1) == 1); // not support sgid = ((s & 2) == 2); suid = ((s & 4) == 4); str = ''; for(i=0; i<3; i++) { if ((parseInt(o.substr(i, 1), 8) & 4) == 4) { str += 'r'; } else { str += '-'; } if ((parseInt(o.substr(i, 1), 8) & 2) == 2) { str += 'w'; } else { str += '-'; } if ((parseInt(o.substr(i, 1), 8) & 1) == 1) { str += ((i==0 && suid)||(i==1 && sgid))? 's' : 'x'; } else { str += '-'; } } } if (style == 'both') { return str + ' (' + oct + ')'; } else if (style == 'string') { return str; } else { return oct; } }, /** * Regist this.decodeRawString function * * @return void */ registRawStringDecoder : function(rawStringDecoder) { if (jQuery.isFunction(rawStringDecoder)) { this.decodeRawString = this.options.rawStringDecoder = rawStringDecoder; } }, /** * Return boolean that uploadable MIME type into target folder * * @param String mime MIME type * @param String target target folder hash * @return Bool */ uploadMimeCheck : function(mime, target) { target = target || this.cwd().hash; var res = true, // default is allow mimeChecker = this.option('uploadMime', target), allow, deny, check = function(checker) { var ret = false; if (typeof checker === 'string' && checker.toLowerCase() === 'all') { ret = true; } else if (Array.isArray(checker) && checker.length) { jQuery.each(checker, function(i, v) { v = v.toLowerCase(); if (v === 'all' || mime.indexOf(v) === 0) { ret = true; return false; } }); } return ret; }; if (mime && jQuery.isPlainObject(mimeChecker)) { mime = mime.toLowerCase(); allow = check(mimeChecker.allow); deny = check(mimeChecker.deny); if (mimeChecker.firstOrder === 'allow') { res = false; // default is deny if (! deny && allow === true) { // match only allow res = true; } } else { res = true; // default is allow if (deny === true && ! allow) { // match only deny res = false; } } } return res; }, /** * call chained sequence of async deferred functions * * @param Array tasks async functions * @return Object jQuery.Deferred */ sequence : function(tasks) { var l = tasks.length, chain = function(task, idx) { ++idx; if (tasks[idx]) { return chain(task.then(tasks[idx]), idx); } else { return task; } }; if (l > 1) { return chain(tasks[0](), 0); } else { return tasks[0](); } }, /** * Reload contents of target URL for clear browser cache * * @param String url target URL * @return Object jQuery.Deferred */ reloadContents : function(url) { var dfd = jQuery.Deferred(), ifm; try { ifm = jQuery('<iframe width="1" height="1" scrolling="no" frameborder="no" style="position:absolute; top:-1px; left:-1px" crossorigin="use-credentials">') .attr('src', url) .one('load', function() { var ifm = jQuery(this); try { this.contentDocument.location.reload(true); ifm.one('load', function() { ifm.remove(); dfd.resolve(); }); } catch(e) { ifm.attr('src', '').attr('src', url).one('load', function() { ifm.remove(); dfd.resolve(); }); } }) .appendTo('body'); } catch(e) { ifm && ifm.remove(); dfd.reject(); } return dfd; }, /** * Make netmount option for OAuth2 * * @param String protocol * @param String name * @param String host * @param Object opts Default {noOffline: false, root: 'root', pathI18n: 'folderId', folders: true} } * * @return Object */ makeNetmountOptionOauth : function(protocol, name, host, opt) { var noOffline = typeof opt === 'boolean'? opt : null, // for backward compat opts = Object.assign({ noOffline : false, root : 'root', pathI18n : 'folderId', folders : true }, (noOffline === null? (opt || {}) : {noOffline : noOffline})), addFolders = function(fm, bro, folders) { var self = this, cnt = Object.keys(jQuery.isPlainObject(folders)? folders : {}).length, select; bro.next().remove(); if (cnt) { select = jQuery('<select class="ui-corner-all elfinder-tabstop" style="max-width:200px;">').append( jQuery(jQuery.map(folders, function(n,i){return '<option value="'+fm.escape((i+'').trim())+'">'+fm.escape(n)+'</option>';}).join('')) ).on('change click', function(e){ var node = jQuery(this), path = node.val(), spn; self.inputs.path.val(path); if (opts.folders && (e.type === 'change' || node.data('current') !== path)) { node.next().remove(); node.data('current', path); if (path != opts.root) { spn = spinner(); if (xhr && xhr.state() === 'pending') { fm.abortXHR(xhr, { quiet: true , abort: true }); } node.after(spn); xhr = fm.request({ data : {cmd : 'netmount', protocol: protocol, host: host, user: 'init', path: path, pass: 'folders'}, preventDefault : true }).done(function(data){ addFolders.call(self, fm, node, data.folders); }).always(function() { fm.abortXHR(xhr, { quiet: true }); spn.remove(); }).xhr; } } }); bro.after(jQuery('<div/>').append(select)) .closest('.ui-dialog').trigger('tabstopsInit'); select.trigger('focus'); } }, spinner = function() { return jQuery('<div class="elfinder-netmount-spinner"/>').append('<span class="elfinder-spinner"/>'); }, xhr; return { vars : {}, name : name, inputs: { offline : jQuery('<input type="checkbox"/>').on('change', function() { jQuery(this).parents('table.elfinder-netmount-tb').find('select:first').trigger('change', 'reset'); }), host : jQuery('<span><span class="elfinder-spinner"/></span><input type="hidden"/>'), path : jQuery('<input type="text" value="'+opts.root+'"/>'), user : jQuery('<input type="hidden"/>'), pass : jQuery('<input type="hidden"/>') }, select: function(fm, ev, d){ var f = this.inputs, oline = f.offline, f0 = jQuery(f.host[0]), data = d || null; this.vars.mbtn = f.host.closest('.ui-dialog').children('.ui-dialog-buttonpane:first').find('button.elfinder-btncnt-0'); if (! f0.data('inrequest') && (f0.find('span.elfinder-spinner').length || data === 'reset' || (data === 'winfocus' && ! f0.siblings('span.elfinder-button-icon-reload').length)) ) { if (oline.parent().children().length === 1) { f.path.parent().prev().html(fm.i18n(opts.pathI18n)); oline.attr('title', fm.i18n('offlineAccess')); oline.uniqueId().after(jQuery('<label/>').attr('for', oline.attr('id')).html(' '+fm.i18n('offlineAccess'))); } f0.data('inrequest', true).empty().addClass('elfinder-spinner') .parent().find('span.elfinder-button-icon').remove(); fm.request({ data : {cmd : 'netmount', protocol: protocol, host: host, user: 'init', options: {id: fm.id, offline: oline.prop('checked')? 1:0, pass: f.host[1].value}}, preventDefault : true }).done(function(data){ f0.removeClass("elfinder-spinner").html(data.body.replace(/\{msg:([^}]+)\}/g, function(whole,s1){return fm.i18n(s1, host);})); }); opts.noOffline && oline.closest('tr').hide(); } else { oline.closest('tr')[(opts.noOffline || f.user.val())? 'hide':'show'](); f0.data('funcexpup') && f0.data('funcexpup')(); } this.vars.mbtn[jQuery(f.host[1]).val()? 'show':'hide'](); }, done: function(fm, data){ var f = this.inputs, p = this.protocol, f0 = jQuery(f.host[0]), f1 = jQuery(f.host[1]), expires = ' '; opts.noOffline && f.offline.closest('tr').hide(); if (data.mode == 'makebtn') { f0.removeClass('elfinder-spinner').removeData('expires').removeData('funcexpup'); f.host.find('input').on('mouseenter mouseleave', function(){jQuery(this).toggleClass('ui-state-hover');}); f1.val(''); f.path.val(opts.root).next().remove(); f.user.val(''); f.pass.val(''); ! opts.noOffline && f.offline.closest('tr').show(); this.vars.mbtn.hide(); } else if (data.mode == 'folders') { if (data.folders) { addFolders.call(this, fm, f.path.nextAll(':last'), data.folders); } } else { if (data.expires) { expires = '()'; f0.data('expires', data.expires); } f0.html(host + expires).removeClass('elfinder-spinner'); if (data.expires) { f0.data('funcexpup', function() { var rem = Math.floor((f0.data('expires') - (+new Date()) / 1000) / 60); if (rem < 3) { f0.parent().children('.elfinder-button-icon-reload').click(); } else { f0.text(f0.text().replace(/\(.*\)/, '('+fm.i18n(['minsLeft', rem])+')')); setTimeout(function() { if (f0.is(':visible')) { f0.data('funcexpup')(); } }, 60000); } }); f0.data('funcexpup')(); } if (data.reset) { p.trigger('change', 'reset'); return; } f0.parent().append(jQuery('<span class="elfinder-button-icon elfinder-button-icon-reload" title="'+fm.i18n('reAuth')+'">') .on('click', function() { f1.val('reauth'); p.trigger('change', 'reset'); })); f1.val(protocol); this.vars.mbtn.show(); if (data.folders) { addFolders.call(this, fm, f.path, data.folders); } f.user.val('done'); f.pass.val('done'); f.offline.closest('tr').hide(); } f0.removeData('inrequest'); }, fail: function(fm, err){ jQuery(this.inputs.host[0]).removeData('inrequest'); this.protocol.trigger('change', 'reset'); }, integrateInfo: opts.integrate }; }, /** * Find cwd's nodes from files * * @param Array files * @param Object opts {firstOnly: true|false} */ findCwdNodes : function(files, opts) { var self = this, cwd = this.getUI('cwd'), cwdHash = this.cwd().hash, newItem = jQuery(); opts = opts || {}; jQuery.each(files, function(i, f) { if (f.phash === cwdHash || self.searchStatus.state > 1) { newItem = newItem.add(self.cwdHash2Elm(f.hash)); if (opts.firstOnly) { return false; } } }); return newItem; }, /** * Convert from relative URL to abstract URL based on current URL * * @param String URL * @return String */ convAbsUrl : function(url) { if (url.match(/^http/i)) { return url; } if (url.substr(0,2) === '//') { return window.location.protocol + url; } var root = window.location.protocol + '//' + window.location.host, reg = /[^\/]+\/\.\.\//, ret; if (url.substr(0, 1) === '/') { ret = root + url; } else { ret = root + window.location.pathname.replace(/\/[^\/]+$/, '/') + url; } ret = ret.replace('/./', '/'); while(reg.test(ret)) { ret = ret.replace(reg, ''); } return ret; }, /** * Is same origin to current site * * @param String check url * @return Boolean */ isSameOrigin : function (checkUrl) { var url; checkUrl = this.convAbsUrl(checkUrl); if (location.origin && window.URL) { try { url = new URL(checkUrl); return location.origin === url.origin; } catch(e) {} } url = document.createElement('a'); url.href = checkUrl; return location.protocol === url.protocol && location.host === url.host && location.port && url.port; }, navHash2Id : function(hash) { return this.navPrefix + hash; }, navId2Hash : function(id) { return typeof(id) == 'string' ? id.substr(this.navPrefix.length) : false; }, cwdHash2Id : function(hash) { return this.cwdPrefix + hash; }, cwdId2Hash : function(id) { return typeof(id) == 'string' ? id.substr(this.cwdPrefix.length) : false; }, /** * navHash to jQuery element object * * @param String hash nav hash * @return Object jQuery element object */ navHash2Elm : function(hash) { return jQuery(document.getElementById(this.navHash2Id(hash))); }, /** * cwdHash to jQuery element object * * @param String hash cwd hash * @return Object jQuery element object */ cwdHash2Elm : function(hash) { return jQuery(document.getElementById(this.cwdHash2Id(hash))); }, isInWindow : function(elem, nochkHide) { var elm, rect; if (! (elm = elem.get(0))) { return false; } if (! nochkHide && elm.offsetParent === null) { return false; } rect = elm.getBoundingClientRect(); return document.elementFromPoint(rect.left, rect.top)? true : false; }, /** * calculate elFinder node z-index * * @return void */ zIndexCalc : function() { var self = this, node = this.getUI(), ni = node.css('z-index'); if (ni && ni !== 'auto' && ni !== 'inherit') { self.zIndex = ni; } else { node.parents().each(function(i, n) { var z = jQuery(n).css('z-index'); if (z !== 'auto' && z !== 'inherit' && (z = parseInt(z))) { self.zIndex = z; return false; } }); } }, /** * Load JavaScript files * * @param Array urls to load JavaScript file URLs * @param Function callback call back function on script loaded * @param Object opts Additional options to jQuery.ajax OR {loadType: 'tag'} to load by script tag * @param Object check { obj: (Object)ParentObject, name: (String)"Attribute name", timeout: (Integer)milliseconds } * @return elFinder */ loadScript : function(urls, callback, opts, check) { var defOpts = { dataType : 'script', cache : true }, success, cnt, scripts = {}, results = {}; opts = opts || {}; if (opts.tryRequire && this.hasRequire) { require(urls, callback, opts.error); } else { success = function() { var cnt, fi, hasError; jQuery.each(results, function(i, status) { if (status !== 'success' && status !== 'notmodified') { hasError = true; return false; } }); if (!hasError) { if (jQuery.isFunction(callback)) { if (check) { if (typeof check.obj[check.name] === 'undefined') { cnt = check.timeout? (check.timeout / 10) : 1; fi = setInterval(function() { if (--cnt < 0 || typeof check.obj[check.name] !== 'undefined') { clearInterval(fi); callback(); } }, 10); } else { callback(); } } else { callback(); } } } else { if (opts.error && jQuery.isFunction(opts.error)) { opts.error({ loadResults: results }); } } }; if (opts.loadType === 'tag') { jQuery('head > script').each(function() { scripts[this.src] = this; }); cnt = urls.length; jQuery.each(urls, function(i, url) { var done = false, script; if (scripts[url]) { results[i] = scripts[url]._error || 'success'; (--cnt < 1) && success(); } else { script = document.createElement('script'); script.charset = opts.charset || 'UTF-8'; jQuery('head').append(script); script.onload = script.onreadystatechange = function() { if ( !done && (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') ) { done = true; results[i] = 'success'; (--cnt < 1) && success(); } }; script.onerror = function(err) { results[i] = script._error = (err && err.type)? err.type : 'error'; (--cnt < 1) && success(); }; script.src = url; } }); } else { opts = jQuery.isPlainObject(opts)? Object.assign(defOpts, opts) : defOpts; cnt = 0; (function appendScript(d, status) { if (d !== void(0)) { results[cnt++] = status; } if (urls.length) { jQuery.ajax(Object.assign({}, opts, { url: urls.shift(), success: appendScript, error: appendScript })); } else { success(); } })(); } } return this; }, /** * Load CSS files * * @param Array to load CSS file URLs * @param Object options * @return elFinder */ loadCss : function(urls, opts) { var self = this, clName, dfds; if (typeof urls === 'string') { urls = [ urls ]; } if (opts) { if (opts.className) { clName = opts.className; } if (opts.dfd && opts.dfd.promise) { dfds = []; } } jQuery.each(urls, function(i, url) { var link, df; url = self.convAbsUrl(url).replace(/^https?:/i, ''); if (dfds) { dfds[i] = jQuery.Deferred(); } if (! jQuery("head > link[href='+url+']").length) { link = document.createElement('link'); link.type = 'text/css'; link.rel = 'stylesheet'; link.href = url; if (clName) { link.className = clName; } if (dfds) { link.onload = function() { dfds[i].resolve(); }; link.onerror = function() { dfds[i].reject(); }; } jQuery('head').append(link); } else { dfds && dfds[i].resolve(); } }); if (dfds) { jQuery.when.apply(null, dfds).done(function() { opts.dfd.resolve(); }).fail(function() { opts.dfd.reject(); }); } return this; }, /** * Abortable async job performer * * @param func Function * @param arr Array * @param opts Object * * @return Object jQuery.Deferred that has an extended method _abort() */ asyncJob : function(func, arr, opts) { var dfrd = jQuery.Deferred(), abortFlg = false, parms = Object.assign({ interval : 0, numPerOnce : 1 }, opts || {}), resArr = [], vars =[], curVars = [], exec, tm; dfrd._abort = function(resolve) { tm && clearTimeout(tm); vars = []; abortFlg = true; if (dfrd.state() === 'pending') { dfrd[resolve? 'resolve' : 'reject'](resArr); } }; dfrd.fail(function() { dfrd._abort(); }).always(function() { dfrd._abort = function() {}; }); if (typeof func === 'function' && Array.isArray(arr)) { vars = arr.concat(); exec = function() { var i, len, res; if (abortFlg) { return; } curVars = vars.splice(0, parms.numPerOnce); len = curVars.length; for (i = 0; i < len; i++) { if (abortFlg) { break; } res = func(curVars[i]); (res !== null) && resArr.push(res); } if (abortFlg) { return; } if (vars.length) { tm = setTimeout(exec, parms.interval); } else { dfrd.resolve(resArr); } }; if (vars.length) { tm = setTimeout(exec, 0); } else { dfrd.resolve(resArr); } } else { dfrd.reject(); } return dfrd; }, getSize : function(targets) { var self = this, reqs = [], tgtlen = targets.length, dfrd = jQuery.Deferred().fail(function() { jQuery.each(reqs, function(i, req) { if (req) { req.syncOnFail && req.syncOnFail(false); req.reject(); } }); }), getLeafRoots = function(file) { var targets = []; if (file.mime === 'directory') { jQuery.each(self.leafRoots, function(hash, roots) { var phash; if (hash === file.hash) { targets.push.apply(targets, roots); } else { phash = (self.file(hash) || {}).phash; while(phash) { if (phash === file.hash) { targets.push.apply(targets, roots); } phash = (self.file(phash) || {}).phash; } } }); } return targets; }, checkPhash = function(hash) { var dfd = jQuery.Deferred(), dir = self.file(hash), target = dir? dir.phash : hash; if (target && ! self.file(target)) { self.request({ data : { cmd : 'parents', target : target }, preventFail : true }).done(function() { self.one('parentsdone', function() { dfd.resolve(); }); }).fail(function() { dfd.resolve(); }); } else { dfd.resolve(); } return dfd; }, cache = function() { var dfd = jQuery.Deferred(), cnt = Object.keys(self.leafRoots).length; if (cnt > 0) { jQuery.each(self.leafRoots, function(hash) { checkPhash(hash).done(function() { --cnt; if (cnt < 1) { dfd.resolve(); } }); }); } else { dfd.resolve(); } return dfd; }; self.autoSync('stop'); cache().done(function() { var files = [], grps = {}, dfds = [], cache = [], singles = {}; jQuery.each(targets, function() { files.push.apply(files, getLeafRoots(self.file(this))); }); targets.push.apply(targets, files); jQuery.each(targets, function() { var root = self.root(this), file = self.file(this); if (file && (file.sizeInfo || file.mime !== 'directory')) { cache.push(jQuery.Deferred().resolve(file.sizeInfo? file.sizeInfo : {size: file.size, dirCnt: 0, fileCnt : 1})); } else { if (! grps[root]) { grps[root] = [ this ]; } else { grps[root].push(this); } } }); jQuery.each(grps, function() { var idx = dfds.length; if (this.length === 1) { singles[idx] = this[0]; } dfds.push(self.request({ data : {cmd : 'size', targets : this}, preventDefault : true })); }); reqs.push.apply(reqs, dfds); dfds.push.apply(dfds, cache); jQuery.when.apply($, dfds).fail(function() { dfrd.reject(); }).done(function() { var cache = function(h, data) { var file; if (file = self.file(h)) { file.sizeInfo = { isCache: true }; jQuery.each(['size', 'dirCnt', 'fileCnt'], function() { file.sizeInfo[this] = data[this] || 0; }); file.size = parseInt(file.sizeInfo.size); changed.push(file); } }, size = 0, fileCnt = 0, dirCnt = 0, argLen = arguments.length, cnts = [], cntsTxt = '', changed = [], i, file, data; for (i = 0; i < argLen; i++) { data = arguments[i]; file = null; if (!data.isCache) { if (singles[i] && (file = self.file(singles[i]))) { cache(singles[i], data); } else if (data.sizes && jQuery.isPlainObject(data.sizes)) { jQuery.each(data.sizes, function(h, sizeInfo) { cache(h, sizeInfo); }); } } size += parseInt(data.size); if (fileCnt !== false) { if (typeof data.fileCnt === 'undefined') { fileCnt = false; } fileCnt += parseInt(data.fileCnt || 0); } if (dirCnt !== false) { if (typeof data.dirCnt === 'undefined') { dirCnt = false; } dirCnt += parseInt(data.dirCnt || 0); } } changed.length && self.change({changed: changed}); if (dirCnt !== false){ cnts.push(self.i18n('folders') + ': ' + (dirCnt - (tgtlen > 1? 0 : 1))); } if (fileCnt !== false){ cnts.push(self.i18n('files') + ': ' + fileCnt); } if (cnts.length) { cntsTxt = '<br>' + cnts.join(', '); } dfrd.resolve({ size: size, fileCnt: fileCnt, dirCnt: dirCnt, formated: (size >= 0 ? self.formatSize(size) : self.i18n('unknown')) + cntsTxt }); }); self.autoSync(); }); return dfrd; }, /** * Gets the theme object by settings of options.themes * * @param String themeid The themeid * @return Object jQuery.Deferred */ getTheme : function(themeid) { var self = this, dfd = jQuery.Deferred(), absUrl = function(url, base) { if (!base) { base = self.convAbsUrl(self.baseUrl); } if (Array.isArray(url)) { return jQuery.map(url, function(v) { return absUrl(v, base); }); } else { return url.match(/^(?:http|\/\/)/i)? url : base + url.replace(/^(?:\.\/|\/)/, ''); } }, themeObj, m; if (themeid && (themeObj = self.options.themes[themeid])) { if (typeof themeObj === 'string') { url = absUrl(themeObj); if (m = url.match(/^(.+\/)[^/]+\.json$/i)) { jQuery.getJSON(url).done(function(data) { themeObj = data; themeObj.id = themeid; if (themeObj.cssurls) { themeObj.cssurls = absUrl(themeObj.cssurls, m[1]); } dfd.resolve(themeObj); }).fail(function() { dfd.reject(); }); } else { dfd.resolve({ id: themeid, name: themeid, cssurls: [url] }); } } else if (jQuery.isPlainObject(themeObj) && themeObj.cssurls) { themeObj.id = themeid; themeObj.cssurls = absUrl(themeObj.cssurls); if (!Array.isArray(themeObj.cssurls)) { themeObj.cssurls = [themeObj.cssurls]; } if (!themeObj.name) { themeObj.name = themeid; } dfd.resolve(themeObj); } else { dfd.reject(); } } else { dfd.reject(); } return dfd; }, /** * Change current theme * * @param String themeid The themeid * @return Object this elFinder instance */ changeTheme : function(themeid) { var self = this; if (themeid) { if (self.options.themes[themeid] && (!self.theme || self.theme.id !== themeid)) { self.getTheme(themeid).done(function(themeObj) { if (themeObj.cssurls) { jQuery('head>link.elfinder-theme-ext').remove(); self.loadCss(themeObj.cssurls, { className: 'elfinder-theme-ext', dfd: jQuery.Deferred().done(function() { self.theme = themeObj; self.trigger && self.trigger('themechange'); }) }); } }); } else if (themeid === 'default' && self.theme) { jQuery('head>link.elfinder-theme-ext').remove(); self.theme = null; self.trigger && self.trigger('themechange'); } } return this; }, /** * Apply leaf root stats to target directory * * @param object dir object of target directory * @param boolean update is force update * * @return boolean dir object was chenged */ applyLeafRootStats : function(dir, update) { var self = this, prev = update? dir : (self.file(dir.hash) || dir), prevTs = prev.ts, change = false; // backup original stats if (update || !dir._realStats) { dir._realStats = { locked: dir.locked || 0, dirs: dir.dirs || 0, ts: dir.ts }; } // set lock dir.locked = 1; if (!prev.locked) { change = true; } // has leaf root to `dirs: 1` dir.dirs = 1; if (!prev.dirs) { change = true; } // set ts jQuery.each(self.leafRoots[dir.hash], function() { var f = self.file(this); if (f && f.ts && (dir.ts || 0) < f.ts) { dir.ts = f.ts; } }); if (prevTs !== dir.ts) { change = true; } return change; }, /** * To aborted XHR object * * @param Object xhr * @param Object opts * * @return void */ abortXHR : function(xhr, o) { var opts = o || {}; if (xhr) { opts.quiet && (xhr.quiet = true); if (opts.abort && xhr._requestId) { this.request({ data: { cmd: 'abort', id: xhr._requestId }, preventDefault: true }); } xhr.abort(); xhr = void 0; } }, /** * Gets the request identifier * * @return String The request identifier. */ getRequestId : function() { return (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16); }, /** * Flip key and value of array or object * * @param Array | Object { a: 1, b: 1, c: 2 } * @param Mixed Static value * @return Object { 1: "b", 2: "c" } */ arrayFlip : function (trans, val) { var key, tmpArr = {}, isArr = jQuery.isArray(trans); for (key in trans) { if (isArr || trans.hasOwnProperty(key)) { tmpArr[trans[key]] = val || key; } } return tmpArr; }, /** * Return array ["name without extention", "extention"] * * @param String name * * @return Array * */ splitFileExtention : function(name) { var m; if (m = name.match(/^(.+?)?\.((?:tar\.(?:gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(?:gz|bz2)|[a-z0-9]{1,10})$/i)) { if (typeof m[1] === 'undefined') { m[1] = ''; } return [m[1], m[2]]; } else { return [name, '']; } }, /** * Slice the ArrayBuffer by sliceSize * * @param arraybuffer arrayBuffer The array buffer * @param Number sliceSize The slice size * @return Array Array of sleced arraybuffer */ sliceArrayBuffer : function(arrayBuffer, sliceSize) { var segments= [], fi = 0; while(fi * sliceSize < arrayBuffer.byteLength){ segments.push(arrayBuffer.slice(fi * sliceSize, (fi + 1) * sliceSize)); fi++; } return segments; }, arrayBufferToBase64 : function(ab) { if (!window.btoa) { return ''; } var dView = new Uint8Array(ab), // Get a byte view arr = Array.prototype.slice.call(dView), // Create a normal array arr1 = arr.map(function(item) { return String.fromCharCode(item); // Convert }); return window.btoa(arr1.join('')); // Form a string }, log : function(m) { window.console && window.console.log && window.console.log(m); return this; }, debug : function(type, m) { var d = this.options.debug; if (d && (d === 'all' || d[type])) { window.console && window.console.log && window.console.log('elfinder debug: ['+type+'] ['+this.id+']', m); } if (type === 'backend-error') { if (! this.cwd().hash || (d && (d === 'all' || d['backend-error']))) { m = Array.isArray(m)? m : [ m ]; this.error(m); } } else if (type === 'backend-debug') { this.trigger('backenddebug', m); } return this; }, time : function(l) { window.console && window.console.time && window.console.time(l); }, timeEnd : function(l) { window.console && window.console.timeEnd && window.console.timeEnd(l); } }; /** * for conpat ex. ie8... * * Object.keys() - JavaScript | MDN * https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys */ if (!Object.keys) { Object.keys = (function () { var hasOwnProperty = Object.prototype.hasOwnProperty, hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), dontEnums = [ 'toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor' ], dontEnumsLength = dontEnums.length; return function (obj) { if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object'); var result = []; for (var prop in obj) { if (hasOwnProperty.call(obj, prop)) result.push(prop); } if (hasDontEnumBug) { for (var i=0; i < dontEnumsLength; i++) { if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]); } } return result; }; })(); } // Array.isArray if (!Array.isArray) { Array.isArray = function(arr) { return jQuery.isArray(arr); }; } // Object.assign if (!Object.assign) { Object.assign = function() { return jQuery.extend.apply(null, arguments); }; } // String.repeat if (!String.prototype.repeat) { String.prototype.repeat = function(count) { 'use strict'; if (this == null) { throw new TypeError('can\'t convert ' + this + ' to object'); } var str = '' + this; count = +count; if (count != count) { count = 0; } if (count < 0) { throw new RangeError('repeat count must be non-negative'); } if (count == Infinity) { throw new RangeError('repeat count must be less than infinity'); } count = Math.floor(count); if (str.length == 0 || count == 0) { return ''; } // Ensuring count is a 31-bit integer allows us to heavily optimize the // main part. But anyway, most current (August 2014) browsers can't handle // strings 1 << 28 chars or longer, so: if (str.length * count >= 1 << 28) { throw new RangeError('repeat count must not overflow maximum string size'); } var rpt = ''; for (var i = 0; i < count; i++) { rpt += str; } return rpt; }; } // String.trim if (!String.prototype.trim) { String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }; } // Array.apply (function () { try { Array.apply(null, {}); return; } catch (e) { } var toString = Object.prototype.toString, arrayType = '[object Array]', _apply = Function.prototype.apply, slice = /*@cc_on @if (@_jscript_version <= 5.8) function () { var a = [], i = this.length; while (i-- > 0) a[i] = this[i]; return a; }@else@*/Array.prototype.slice/*@end@*/; Function.prototype.apply = function apply(thisArg, argArray) { return _apply.call(this, thisArg, toString.call(argArray) === arrayType ? argArray : slice.call(argArray)); }; })(); // Array.from if (!Array.from) { Array.from = function(obj) { return obj.length === 1 ? [obj[0]] : Array.apply(null, obj); }; } // window.requestAnimationFrame and window.cancelAnimationFrame if (!window.cancelAnimationFrame) { // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel // MIT license (function() { var lastTime = 0; var vendors = ['ms', 'moz', 'webkit', 'o']; for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; } if (!window.requestAnimationFrame) window.requestAnimationFrame = function(callback, element) { var currTime = new Date().getTime(); var timeToCall = Math.max(0, 16 - (currTime - lastTime)); var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; }; if (!window.cancelAnimationFrame) window.cancelAnimationFrame = function(id) { clearTimeout(id); }; }()); } /* * File: /js/elFinder.version.js */ /** * Application version * * @type String **/ elFinder.prototype.version = '2.1.49'; /* * File: /js/jquery.elfinder.js */ /*** jQuery UI droppable performance tune for elFinder ***/ (function(){ if (jQuery.ui) { if (jQuery.ui.ddmanager) { var origin = jQuery.ui.ddmanager.prepareOffsets; jQuery.ui.ddmanager.prepareOffsets = function( t, event ) { var isOutView = function(elem) { if (elem.is(':hidden')) { return true; } var rect = elem[0].getBoundingClientRect(); return document.elementFromPoint(rect.left, rect.top) || document.elementFromPoint(rect.left + rect.width, rect.top + rect.height)? false : true; }; if (event.type === 'mousedown' || t.options.elfRefresh) { var i, d, m = jQuery.ui.ddmanager.droppables[ t.options.scope ] || [], l = m.length; for ( i = 0; i < l; i++ ) { d = m[ i ]; if (d.options.autoDisable && (!d.options.disabled || d.options.autoDisable > 1)) { d.options.disabled = isOutView(d.element); d.options.autoDisable = d.options.disabled? 2 : 1; } } } // call origin function return origin( t, event ); }; } } })(); /** * * jquery.binarytransport.js * * @description. jQuery ajax transport for making binary data type requests. * @version 1.0 * @author Henry Algus <henryalgus@gmail.com> * */ // use this transport for "binary" data type jQuery.ajaxTransport('+binary', function(options, originalOptions, jqXHR) { // check for conditions and support for blob / arraybuffer response type if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob))))) { var xhr; return { // create new XMLHttpRequest send: function(headers, callback){ // setup all variables xhr = new XMLHttpRequest(); var url = options.url, type = options.type, async = options.async || true, // blob or arraybuffer. Default is blob dataType = options.responseType || 'blob', data = options.data || null, username = options.username, password = options.password; xhr.addEventListener('load', function(){ var data = {}; data[options.dataType] = xhr.response; // make callback and send data callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders()); }); xhr.open(type, url, async, username, password); // setup custom headers for (var i in headers ) { xhr.setRequestHeader(i, headers[i] ); } // setuo xhrFields if (options.xhrFields) { for (var key in options.xhrFields) { if (key in xhr) { xhr[key] = options.xhrFields[key]; } } } xhr.responseType = dataType; xhr.send(data); }, abort: function(){ xhr.abort(); } }; } }); /*! * jQuery UI Touch Punch 0.2.3 * * Copyright 2011–2014, Dave Furfero * Dual licensed under the MIT or GPL Version 2 licenses. * * Depends: * jquery.ui.widget.js * jquery.ui.mouse.js */ (function ($) { // Detect touch support jQuery.support.touch = 'ontouchend' in document; // Ignore browsers without touch support if (!jQuery.support.touch) { return; } var mouseProto = jQuery.ui.mouse.prototype, _mouseInit = mouseProto._mouseInit, _mouseDestroy = mouseProto._mouseDestroy, touchHandled, posX, posY; /** * Simulate a mouse event based on a corresponding touch event * @param {Object} event A touch event * @param {String} simulatedType The corresponding mouse event */ function simulateMouseEvent (event, simulatedType) { // Ignore multi-touch events if (event.originalEvent.touches.length > 1) { return; } if (! jQuery(event.currentTarget).hasClass('touch-punch-keep-default')) { event.preventDefault(); } var touch = event.originalEvent.changedTouches[0], simulatedEvent = document.createEvent('MouseEvents'); // Initialize the simulated mouse event using the touch event's coordinates simulatedEvent.initMouseEvent( simulatedType, // type true, // bubbles true, // cancelable window, // view 1, // detail touch.screenX, // screenX touch.screenY, // screenY touch.clientX, // clientX touch.clientY, // clientY false, // ctrlKey false, // altKey false, // shiftKey false, // metaKey 0, // button null // relatedTarget ); // Dispatch the simulated event to the target element event.target.dispatchEvent(simulatedEvent); } /** * Handle the jQuery UI widget's touchstart events * @param {Object} event The widget element's touchstart event */ mouseProto._touchStart = function (event) { var self = this; // Ignore the event if another widget is already being handled if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) { return; } // Track element position to avoid "false" move posX = event.originalEvent.changedTouches[0].screenX.toFixed(0); posY = event.originalEvent.changedTouches[0].screenY.toFixed(0); // Set the flag to prevent other widgets from inheriting the touch event touchHandled = true; // Track movement to determine if interaction was a click self._touchMoved = false; // Simulate the mouseover event simulateMouseEvent(event, 'mouseover'); // Simulate the mousemove event simulateMouseEvent(event, 'mousemove'); // Simulate the mousedown event simulateMouseEvent(event, 'mousedown'); }; /** * Handle the jQuery UI widget's touchmove events * @param {Object} event The document's touchmove event */ mouseProto._touchMove = function (event) { // Ignore event if not handled if (!touchHandled) { return; } // Ignore if it's a "false" move (position not changed) var x = event.originalEvent.changedTouches[0].screenX.toFixed(0); var y = event.originalEvent.changedTouches[0].screenY.toFixed(0); // Ignore if it's a "false" move (position not changed) if (Math.abs(posX - x) <= 4 && Math.abs(posY - y) <= 4) { return; } // Interaction was not a click this._touchMoved = true; // Simulate the mousemove event simulateMouseEvent(event, 'mousemove'); }; /** * Handle the jQuery UI widget's touchend events * @param {Object} event The document's touchend event */ mouseProto._touchEnd = function (event) { // Ignore event if not handled if (!touchHandled) { return; } // Simulate the mouseup event simulateMouseEvent(event, 'mouseup'); // Simulate the mouseout event simulateMouseEvent(event, 'mouseout'); // If the touch interaction did not move, it should trigger a click if (!this._touchMoved) { // Simulate the click event simulateMouseEvent(event, 'click'); } // Unset the flag to allow other widgets to inherit the touch event touchHandled = false; this._touchMoved = false; }; /** * A duck punch of the jQuery.ui.mouse _mouseInit method to support touch events. * This method extends the widget with bound touch event handlers that * translate touch events to mouse events and pass them to the widget's * original mouse event handling methods. */ mouseProto._mouseInit = function () { var self = this; if (self.element.hasClass('touch-punch')) { // Delegate the touch handlers to the widget's element self.element.on({ touchstart: jQuery.proxy(self, '_touchStart'), touchmove: jQuery.proxy(self, '_touchMove'), touchend: jQuery.proxy(self, '_touchEnd') }); } // Call the original jQuery.ui.mouse init method _mouseInit.call(self); }; /** * Remove the touch event handlers */ mouseProto._mouseDestroy = function () { var self = this; if (self.element.hasClass('touch-punch')) { // Delegate the touch handlers to the widget's element self.element.off({ touchstart: jQuery.proxy(self, '_touchStart'), touchmove: jQuery.proxy(self, '_touchMove'), touchend: jQuery.proxy(self, '_touchEnd') }); } // Call the original jQuery.ui.mouse destroy method _mouseDestroy.call(self); }; })(jQuery); jQuery.fn.elfinder = function(o, o2) { if (o === 'instance') { return this.getElFinder(); } return this.each(function() { var cmd = typeof o === 'string' ? o : '', bootCallback = typeof o2 === 'function'? o2 : void(0), opts; if (!this.elfinder) { if (jQuery.isPlainObject(o)) { new elFinder(this, o, bootCallback); } } else { switch(cmd) { case 'close': case 'hide': this.elfinder.hide(); break; case 'open': case 'show': this.elfinder.show(); break; case 'destroy': this.elfinder.destroy(); break; case 'reload': case 'restart': if (this.elfinder) { opts = this.elfinder.options; bootCallback = this.elfinder.bootCallback; this.elfinder.destroy(); new elFinder(this, jQuery.extend(true, opts, jQuery.isPlainObject(o2)? o2 : {}), bootCallback); } break; } } }); }; jQuery.fn.getElFinder = function() { var instance; this.each(function() { if (this.elfinder) { instance = this.elfinder; return false; } }); return instance; }; jQuery.fn.elfUiWidgetInstance = function(name) { try { return this[name]('instance'); } catch(e) { // fallback for jQuery UI < 1.11 var data = this.data('ui-' + name); if (data && typeof data === 'object' && data.widgetFullName === 'ui-' + name) { return data; } return null; } }; // function scrollRight if (! jQuery.fn.scrollRight) { jQuery.fn.extend({ scrollRight: function (val) { var node = this.get(0); if (val === undefined) { return Math.max(0, node.scrollWidth - (node.scrollLeft + node.clientWidth)); } return this.scrollLeft(node.scrollWidth - node.clientWidth - val); } }); } // function scrollBottom if (! jQuery.fn.scrollBottom) { jQuery.fn.extend({ scrollBottom: function(val) { var node = this.get(0); if (val === undefined) { return Math.max(0, node.scrollHeight - (node.scrollTop + node.clientHeight)); } return this.scrollTop(node.scrollHeight - node.clientHeight - val); } }); } /* * File: /js/elFinder.mimetypes.js */ elFinder.prototype.mimeTypes = {"application\/x-executable":"exe","application\/x-jar":"jar","application\/x-gzip":"gz","application\/x-bzip2":"tbz","application\/x-rar":"rar","text\/x-php":"php","text\/javascript":"js","application\/rtfd":"rtfd","text\/x-python":"py","text\/x-ruby":"rb","text\/x-shellscript":"sh","text\/x-perl":"pl","text\/xml":"xml","text\/x-csrc":"c","text\/x-chdr":"h","text\/x-c++src":"cpp","text\/x-c++hdr":"hh","text\/x-markdown":"md","text\/x-yaml":"yml","image\/x-ms-bmp":"bmp","image\/x-targa":"tga","image\/xbm":"xbm","image\/pxm":"pxm","audio\/wav":"wav","video\/x-dv":"dv","video\/x-ms-wmv":"wm","video\/ogg":"ogm","video\/MP2T":"m2ts","application\/x-mpegURL":"m3u8","application\/dash+xml":"mpd","application\/andrew-inset":"ez","application\/applixware":"aw","application\/atom+xml":"atom","application\/atomcat+xml":"atomcat","application\/atomsvc+xml":"atomsvc","application\/ccxml+xml":"ccxml","application\/cdmi-capability":"cdmia","application\/cdmi-container":"cdmic","application\/cdmi-domain":"cdmid","application\/cdmi-object":"cdmio","application\/cdmi-queue":"cdmiq","application\/cu-seeme":"cu","application\/davmount+xml":"davmount","application\/docbook+xml":"dbk","application\/dssc+der":"dssc","application\/dssc+xml":"xdssc","application\/ecmascript":"ecma","application\/emma+xml":"emma","application\/epub+zip":"epub","application\/exi":"exi","application\/font-tdpfr":"pfr","application\/gml+xml":"gml","application\/gpx+xml":"gpx","application\/gxf":"gxf","application\/hyperstudio":"stk","application\/inkml+xml":"ink","application\/ipfix":"ipfix","application\/java-serialized-object":"ser","application\/java-vm":"class","application\/json":"json","application\/jsonml+json":"jsonml","application\/lost+xml":"lostxml","application\/mac-binhex40":"hqx","application\/mac-compactpro":"cpt","application\/mads+xml":"mads","application\/marc":"mrc","application\/marcxml+xml":"mrcx","application\/mathematica":"ma","application\/mathml+xml":"mathml","application\/mbox":"mbox","application\/mediaservercontrol+xml":"mscml","application\/metalink+xml":"metalink","application\/metalink4+xml":"meta4","application\/mets+xml":"mets","application\/mods+xml":"mods","application\/mp21":"m21","application\/mp4":"mp4s","application\/msword":"doc","application\/mxf":"mxf","application\/octet-stream":"bin","application\/oda":"oda","application\/oebps-package+xml":"opf","application\/ogg":"ogx","application\/omdoc+xml":"omdoc","application\/onenote":"onetoc","application\/oxps":"oxps","application\/patch-ops-error+xml":"xer","application\/pdf":"pdf","application\/pgp-encrypted":"pgp","application\/pgp-signature":"asc","application\/pics-rules":"prf","application\/pkcs10":"p10","application\/pkcs7-mime":"p7m","application\/pkcs7-signature":"p7s","application\/pkcs8":"p8","application\/pkix-attr-cert":"ac","application\/pkix-cert":"cer","application\/pkix-crl":"crl","application\/pkix-pkipath":"pkipath","application\/pkixcmp":"pki","application\/pls+xml":"pls","application\/postscript":"ai","application\/prs.cww":"cww","application\/pskc+xml":"pskcxml","application\/rdf+xml":"rdf","application\/reginfo+xml":"rif","application\/relax-ng-compact-syntax":"rnc","application\/resource-lists+xml":"rl","application\/resource-lists-diff+xml":"rld","application\/rls-services+xml":"rs","application\/rpki-ghostbusters":"gbr","application\/rpki-manifest":"mft","application\/rpki-roa":"roa","application\/rsd+xml":"rsd","application\/rss+xml":"rss","application\/rtf":"rtf","application\/sbml+xml":"sbml","application\/scvp-cv-request":"scq","application\/scvp-cv-response":"scs","application\/scvp-vp-request":"spq","application\/scvp-vp-response":"spp","application\/sdp":"sdp","application\/set-payment-initiation":"setpay","application\/set-registration-initiation":"setreg","application\/shf+xml":"shf","application\/smil+xml":"smi","application\/sparql-query":"rq","application\/sparql-results+xml":"srx","application\/srgs":"gram","application\/srgs+xml":"grxml","application\/sru+xml":"sru","application\/ssdl+xml":"ssdl","application\/ssml+xml":"ssml","application\/tei+xml":"tei","application\/thraud+xml":"tfi","application\/timestamped-data":"tsd","application\/vnd.3gpp.pic-bw-large":"plb","application\/vnd.3gpp.pic-bw-small":"psb","application\/vnd.3gpp.pic-bw-var":"pvb","application\/vnd.3gpp2.tcap":"tcap","application\/vnd.3m.post-it-notes":"pwn","application\/vnd.accpac.simply.aso":"aso","application\/vnd.accpac.simply.imp":"imp","application\/vnd.acucobol":"acu","application\/vnd.acucorp":"atc","application\/vnd.adobe.air-application-installer-package+zip":"air","application\/vnd.adobe.formscentral.fcdt":"fcdt","application\/vnd.adobe.fxp":"fxp","application\/vnd.adobe.xdp+xml":"xdp","application\/vnd.adobe.xfdf":"xfdf","application\/vnd.ahead.space":"ahead","application\/vnd.airzip.filesecure.azf":"azf","application\/vnd.airzip.filesecure.azs":"azs","application\/vnd.amazon.ebook":"azw","application\/vnd.americandynamics.acc":"acc","application\/vnd.amiga.ami":"ami","application\/vnd.android.package-archive":"apk","application\/vnd.anser-web-certificate-issue-initiation":"cii","application\/vnd.anser-web-funds-transfer-initiation":"fti","application\/vnd.antix.game-component":"atx","application\/vnd.apple.installer+xml":"mpkg","application\/vnd.aristanetworks.swi":"swi","application\/vnd.astraea-software.iota":"iota","application\/vnd.audiograph":"aep","application\/vnd.blueice.multipass":"mpm","application\/vnd.bmi":"bmi","application\/vnd.businessobjects":"rep","application\/vnd.chemdraw+xml":"cdxml","application\/vnd.chipnuts.karaoke-mmd":"mmd","application\/vnd.cinderella":"cdy","application\/vnd.claymore":"cla","application\/vnd.cloanto.rp9":"rp9","application\/vnd.clonk.c4group":"c4g","application\/vnd.cluetrust.cartomobile-config":"c11amc","application\/vnd.cluetrust.cartomobile-config-pkg":"c11amz","application\/vnd.commonspace":"csp","application\/vnd.contact.cmsg":"cdbcmsg","application\/vnd.cosmocaller":"cmc","application\/vnd.crick.clicker":"clkx","application\/vnd.crick.clicker.keyboard":"clkk","application\/vnd.crick.clicker.palette":"clkp","application\/vnd.crick.clicker.template":"clkt","application\/vnd.crick.clicker.wordbank":"clkw","application\/vnd.criticaltools.wbs+xml":"wbs","application\/vnd.ctc-posml":"pml","application\/vnd.cups-ppd":"ppd","application\/vnd.curl.car":"car","application\/vnd.curl.pcurl":"pcurl","application\/vnd.dart":"dart","application\/vnd.data-vision.rdz":"rdz","application\/vnd.dece.data":"uvf","application\/vnd.dece.ttml+xml":"uvt","application\/vnd.dece.unspecified":"uvx","application\/vnd.dece.zip":"uvz","application\/vnd.denovo.fcselayout-link":"fe_launch","application\/vnd.dna":"dna","application\/vnd.dolby.mlp":"mlp","application\/vnd.dpgraph":"dpg","application\/vnd.dreamfactory":"dfac","application\/vnd.ds-keypoint":"kpxx","application\/vnd.dvb.ait":"ait","application\/vnd.dvb.service":"svc","application\/vnd.dynageo":"geo","application\/vnd.ecowin.chart":"mag","application\/vnd.enliven":"nml","application\/vnd.epson.esf":"esf","application\/vnd.epson.msf":"msf","application\/vnd.epson.quickanime":"qam","application\/vnd.epson.salt":"slt","application\/vnd.epson.ssf":"ssf","application\/vnd.eszigno3+xml":"es3","application\/vnd.ezpix-album":"ez2","application\/vnd.ezpix-package":"ez3","application\/vnd.fdf":"fdf","application\/vnd.fdsn.mseed":"mseed","application\/vnd.fdsn.seed":"seed","application\/vnd.flographit":"gph","application\/vnd.fluxtime.clip":"ftc","application\/vnd.framemaker":"fm","application\/vnd.frogans.fnc":"fnc","application\/vnd.frogans.ltf":"ltf","application\/vnd.fsc.weblaunch":"fsc","application\/vnd.fujitsu.oasys":"oas","application\/vnd.fujitsu.oasys2":"oa2","application\/vnd.fujitsu.oasys3":"oa3","application\/vnd.fujitsu.oasysgp":"fg5","application\/vnd.fujitsu.oasysprs":"bh2","application\/vnd.fujixerox.ddd":"ddd","application\/vnd.fujixerox.docuworks":"xdw","application\/vnd.fujixerox.docuworks.binder":"xbd","application\/vnd.fuzzysheet":"fzs","application\/vnd.genomatix.tuxedo":"txd","application\/vnd.geogebra.file":"ggb","application\/vnd.geogebra.tool":"ggt","application\/vnd.geometry-explorer":"gex","application\/vnd.geonext":"gxt","application\/vnd.geoplan":"g2w","application\/vnd.geospace":"g3w","application\/vnd.gmx":"gmx","application\/vnd.google-earth.kml+xml":"kml","application\/vnd.google-earth.kmz":"kmz","application\/vnd.grafeq":"gqf","application\/vnd.groove-account":"gac","application\/vnd.groove-help":"ghf","application\/vnd.groove-identity-message":"gim","application\/vnd.groove-injector":"grv","application\/vnd.groove-tool-message":"gtm","application\/vnd.groove-tool-template":"tpl","application\/vnd.groove-vcard":"vcg","application\/vnd.hal+xml":"hal","application\/vnd.handheld-entertainment+xml":"zmm","application\/vnd.hbci":"hbci","application\/vnd.hhe.lesson-player":"les","application\/vnd.hp-hpgl":"hpgl","application\/vnd.hp-hpid":"hpid","application\/vnd.hp-hps":"hps","application\/vnd.hp-jlyt":"jlt","application\/vnd.hp-pcl":"pcl","application\/vnd.hp-pclxl":"pclxl","application\/vnd.hydrostatix.sof-data":"sfd-hdstx","application\/vnd.ibm.minipay":"mpy","application\/vnd.ibm.modcap":"afp","application\/vnd.ibm.rights-management":"irm","application\/vnd.ibm.secure-container":"sc","application\/vnd.iccprofile":"icc","application\/vnd.igloader":"igl","application\/vnd.immervision-ivp":"ivp","application\/vnd.immervision-ivu":"ivu","application\/vnd.insors.igm":"igm","application\/vnd.intercon.formnet":"xpw","application\/vnd.intergeo":"i2g","application\/vnd.intu.qbo":"qbo","application\/vnd.intu.qfx":"qfx","application\/vnd.ipunplugged.rcprofile":"rcprofile","application\/vnd.irepository.package+xml":"irp","application\/vnd.is-xpr":"xpr","application\/vnd.isac.fcs":"fcs","application\/vnd.jam":"jam","application\/vnd.jcp.javame.midlet-rms":"rms","application\/vnd.jisp":"jisp","application\/vnd.joost.joda-archive":"joda","application\/vnd.kahootz":"ktz","application\/vnd.kde.karbon":"karbon","application\/vnd.kde.kchart":"chrt","application\/vnd.kde.kformula":"kfo","application\/vnd.kde.kivio":"flw","application\/vnd.kde.kontour":"kon","application\/vnd.kde.kpresenter":"kpr","application\/vnd.kde.kspread":"ksp","application\/vnd.kde.kword":"kwd","application\/vnd.kenameaapp":"htke","application\/vnd.kidspiration":"kia","application\/vnd.kinar":"kne","application\/vnd.koan":"skp","application\/vnd.kodak-descriptor":"sse","application\/vnd.las.las+xml":"lasxml","application\/vnd.llamagraphics.life-balance.desktop":"lbd","application\/vnd.llamagraphics.life-balance.exchange+xml":"lbe","application\/vnd.lotus-1-2-3":123,"application\/vnd.lotus-approach":"apr","application\/vnd.lotus-freelance":"pre","application\/vnd.lotus-notes":"nsf","application\/vnd.lotus-organizer":"org","application\/vnd.lotus-screencam":"scm","application\/vnd.lotus-wordpro":"lwp","application\/vnd.macports.portpkg":"portpkg","application\/vnd.mcd":"mcd","application\/vnd.medcalcdata":"mc1","application\/vnd.mediastation.cdkey":"cdkey","application\/vnd.mfer":"mwf","application\/vnd.mfmp":"mfm","application\/vnd.micrografx.flo":"flo","application\/vnd.micrografx.igx":"igx","application\/vnd.mif":"mif","application\/vnd.mobius.daf":"daf","application\/vnd.mobius.dis":"dis","application\/vnd.mobius.mbk":"mbk","application\/vnd.mobius.mqy":"mqy","application\/vnd.mobius.msl":"msl","application\/vnd.mobius.plc":"plc","application\/vnd.mobius.txf":"txf","application\/vnd.mophun.application":"mpn","application\/vnd.mophun.certificate":"mpc","application\/vnd.mozilla.xul+xml":"xul","application\/vnd.ms-artgalry":"cil","application\/vnd.ms-cab-compressed":"cab","application\/vnd.ms-excel":"xls","application\/vnd.ms-excel.addin.macroenabled.12":"xlam","application\/vnd.ms-excel.sheet.binary.macroenabled.12":"xlsb","application\/vnd.ms-excel.sheet.macroenabled.12":"xlsm","application\/vnd.ms-excel.template.macroenabled.12":"xltm","application\/vnd.ms-fontobject":"eot","application\/vnd.ms-htmlhelp":"chm","application\/vnd.ms-ims":"ims","application\/vnd.ms-lrm":"lrm","application\/vnd.ms-officetheme":"thmx","application\/vnd.ms-pki.seccat":"cat","application\/vnd.ms-pki.stl":"stl","application\/vnd.ms-powerpoint":"ppt","application\/vnd.ms-powerpoint.addin.macroenabled.12":"ppam","application\/vnd.ms-powerpoint.presentation.macroenabled.12":"pptm","application\/vnd.ms-powerpoint.slide.macroenabled.12":"sldm","application\/vnd.ms-powerpoint.slideshow.macroenabled.12":"ppsm","application\/vnd.ms-powerpoint.template.macroenabled.12":"potm","application\/vnd.ms-project":"mpp","application\/vnd.ms-word.document.macroenabled.12":"docm","application\/vnd.ms-word.template.macroenabled.12":"dotm","application\/vnd.ms-works":"wps","application\/vnd.ms-wpl":"wpl","application\/vnd.ms-xpsdocument":"xps","application\/vnd.mseq":"mseq","application\/vnd.musician":"mus","application\/vnd.muvee.style":"msty","application\/vnd.mynfc":"taglet","application\/vnd.neurolanguage.nlu":"nlu","application\/vnd.nitf":"ntf","application\/vnd.noblenet-directory":"nnd","application\/vnd.noblenet-sealer":"nns","application\/vnd.noblenet-web":"nnw","application\/vnd.nokia.n-gage.data":"ngdat","application\/vnd.nokia.n-gage.symbian.install":"n-gage","application\/vnd.nokia.radio-preset":"rpst","application\/vnd.nokia.radio-presets":"rpss","application\/vnd.novadigm.edm":"edm","application\/vnd.novadigm.edx":"edx","application\/vnd.novadigm.ext":"ext","application\/vnd.oasis.opendocument.chart":"odc","application\/vnd.oasis.opendocument.chart-template":"otc","application\/vnd.oasis.opendocument.database":"odb","application\/vnd.oasis.opendocument.formula":"odf","application\/vnd.oasis.opendocument.formula-template":"odft","application\/vnd.oasis.opendocument.graphics":"odg","application\/vnd.oasis.opendocument.graphics-template":"otg","application\/vnd.oasis.opendocument.image":"odi","application\/vnd.oasis.opendocument.image-template":"oti","application\/vnd.oasis.opendocument.presentation":"odp","application\/vnd.oasis.opendocument.presentation-template":"otp","application\/vnd.oasis.opendocument.spreadsheet":"ods","application\/vnd.oasis.opendocument.spreadsheet-template":"ots","application\/vnd.oasis.opendocument.text":"odt","application\/vnd.oasis.opendocument.text-master":"odm","application\/vnd.oasis.opendocument.text-template":"ott","application\/vnd.oasis.opendocument.text-web":"oth","application\/vnd.olpc-sugar":"xo","application\/vnd.oma.dd2+xml":"dd2","application\/vnd.openofficeorg.extension":"oxt","application\/vnd.openxmlformats-officedocument.presentationml.presentation":"pptx","application\/vnd.openxmlformats-officedocument.presentationml.slide":"sldx","application\/vnd.openxmlformats-officedocument.presentationml.slideshow":"ppsx","application\/vnd.openxmlformats-officedocument.presentationml.template":"potx","application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet":"xlsx","application\/vnd.openxmlformats-officedocument.spreadsheetml.template":"xltx","application\/vnd.openxmlformats-officedocument.wordprocessingml.document":"docx","application\/vnd.openxmlformats-officedocument.wordprocessingml.template":"dotx","application\/vnd.osgeo.mapguide.package":"mgp","application\/vnd.osgi.dp":"dp","application\/vnd.osgi.subsystem":"esa","application\/vnd.palm":"pdb","application\/vnd.pawaafile":"paw","application\/vnd.pg.format":"str","application\/vnd.pg.osasli":"ei6","application\/vnd.picsel":"efif","application\/vnd.pmi.widget":"wg","application\/vnd.pocketlearn":"plf","application\/vnd.powerbuilder6":"pbd","application\/vnd.previewsystems.box":"box","application\/vnd.proteus.magazine":"mgz","application\/vnd.publishare-delta-tree":"qps","application\/vnd.pvi.ptid1":"ptid","application\/vnd.quark.quarkxpress":"qxd","application\/vnd.realvnc.bed":"bed","application\/vnd.recordare.musicxml":"mxl","application\/vnd.recordare.musicxml+xml":"musicxml","application\/vnd.rig.cryptonote":"cryptonote","application\/vnd.rim.cod":"cod","application\/vnd.rn-realmedia":"rm","application\/vnd.rn-realmedia-vbr":"rmvb","application\/vnd.route66.link66+xml":"link66","application\/vnd.sailingtracker.track":"st","application\/vnd.seemail":"see","application\/vnd.sema":"sema","application\/vnd.semd":"semd","application\/vnd.semf":"semf","application\/vnd.shana.informed.formdata":"ifm","application\/vnd.shana.informed.formtemplate":"itp","application\/vnd.shana.informed.interchange":"iif","application\/vnd.shana.informed.package":"ipk","application\/vnd.simtech-mindmapper":"twd","application\/vnd.smaf":"mmf","application\/vnd.smart.teacher":"teacher","application\/vnd.solent.sdkm+xml":"sdkm","application\/vnd.spotfire.dxp":"dxp","application\/vnd.spotfire.sfs":"sfs","application\/vnd.stardivision.calc":"sdc","application\/vnd.stardivision.draw":"sda","application\/vnd.stardivision.impress":"sdd","application\/vnd.stardivision.math":"smf","application\/vnd.stardivision.writer":"sdw","application\/vnd.stardivision.writer-global":"sgl","application\/vnd.stepmania.package":"smzip","application\/vnd.stepmania.stepchart":"sm","application\/vnd.sun.xml.calc":"sxc","application\/vnd.sun.xml.calc.template":"stc","application\/vnd.sun.xml.draw":"sxd","application\/vnd.sun.xml.draw.template":"std","application\/vnd.sun.xml.impress":"sxi","application\/vnd.sun.xml.impress.template":"sti","application\/vnd.sun.xml.math":"sxm","application\/vnd.sun.xml.writer":"sxw","application\/vnd.sun.xml.writer.global":"sxg","application\/vnd.sun.xml.writer.template":"stw","application\/vnd.sus-calendar":"sus","application\/vnd.svd":"svd","application\/vnd.symbian.install":"sis","application\/vnd.syncml+xml":"xsm","application\/vnd.syncml.dm+wbxml":"bdm","application\/vnd.syncml.dm+xml":"xdm","application\/vnd.tao.intent-module-archive":"tao","application\/vnd.tcpdump.pcap":"pcap","application\/vnd.tmobile-livetv":"tmo","application\/vnd.trid.tpt":"tpt","application\/vnd.triscape.mxs":"mxs","application\/vnd.trueapp":"tra","application\/vnd.ufdl":"ufd","application\/vnd.uiq.theme":"utz","application\/vnd.umajin":"umj","application\/vnd.unity":"unityweb","application\/vnd.uoml+xml":"uoml","application\/vnd.vcx":"vcx","application\/vnd.visio":"vsd","application\/vnd.visionary":"vis","application\/vnd.vsf":"vsf","application\/vnd.wap.wbxml":"wbxml","application\/vnd.wap.wmlc":"wmlc","application\/vnd.wap.wmlscriptc":"wmlsc","application\/vnd.webturbo":"wtb","application\/vnd.wolfram.player":"nbp","application\/vnd.wordperfect":"wpd","application\/vnd.wqd":"wqd","application\/vnd.wt.stf":"stf","application\/vnd.xara":"xar","application\/vnd.xfdl":"xfdl","application\/vnd.yamaha.hv-dic":"hvd","application\/vnd.yamaha.hv-script":"hvs","application\/vnd.yamaha.hv-voice":"hvp","application\/vnd.yamaha.openscoreformat":"osf","application\/vnd.yamaha.openscoreformat.osfpvg+xml":"osfpvg","application\/vnd.yamaha.smaf-audio":"saf","application\/vnd.yamaha.smaf-phrase":"spf","application\/vnd.yellowriver-custom-menu":"cmp","application\/vnd.zul":"zir","application\/vnd.zzazz.deck+xml":"zaz","application\/voicexml+xml":"vxml","application\/widget":"wgt","application\/winhlp":"hlp","application\/wsdl+xml":"wsdl","application\/wspolicy+xml":"wspolicy","application\/x-7z-compressed":"7z","application\/x-abiword":"abw","application\/x-ace-compressed":"ace","application\/x-apple-diskimage":"dmg","application\/x-authorware-bin":"aab","application\/x-authorware-map":"aam","application\/x-authorware-seg":"aas","application\/x-bcpio":"bcpio","application\/x-bittorrent":"torrent","application\/x-blorb":"blb","application\/x-bzip":"bz","application\/x-cbr":"cbr","application\/x-cdlink":"vcd","application\/x-cfs-compressed":"cfs","application\/x-chat":"chat","application\/x-chess-pgn":"pgn","application\/x-conference":"nsc","application\/x-cpio":"cpio","application\/x-csh":"csh","application\/x-debian-package":"deb","application\/x-dgc-compressed":"dgc","application\/x-director":"dir","application\/x-doom":"wad","application\/x-dtbncx+xml":"ncx","application\/x-dtbook+xml":"dtb","application\/x-dtbresource+xml":"res","application\/x-dvi":"dvi","application\/x-envoy":"evy","application\/x-eva":"eva","application\/x-font-bdf":"bdf","application\/x-font-ghostscript":"gsf","application\/x-font-linux-psf":"psf","application\/x-font-pcf":"pcf","application\/x-font-snf":"snf","application\/x-font-type1":"pfa","application\/x-freearc":"arc","application\/x-futuresplash":"spl","application\/x-gca-compressed":"gca","application\/x-glulx":"ulx","application\/x-gnumeric":"gnumeric","application\/x-gramps-xml":"gramps","application\/x-gtar":"gtar","application\/x-hdf":"hdf","application\/x-install-instructions":"install","application\/x-iso9660-image":"iso","application\/x-java-jnlp-file":"jnlp","application\/x-latex":"latex","application\/x-lzh-compressed":"lzh","application\/x-mie":"mie","application\/x-mobipocket-ebook":"prc","application\/x-ms-application":"application","application\/x-ms-shortcut":"lnk","application\/x-ms-wmd":"wmd","application\/x-ms-wmz":"wmz","application\/x-ms-xbap":"xbap","application\/x-msaccess":"mdb","application\/x-msbinder":"obd","application\/x-mscardfile":"crd","application\/x-msclip":"clp","application\/x-msdownload":"dll","application\/x-msmediaview":"mvb","application\/x-msmetafile":"wmf","application\/x-msmoney":"mny","application\/x-mspublisher":"pub","application\/x-msschedule":"scd","application\/x-msterminal":"trm","application\/x-mswrite":"wri","application\/x-netcdf":"nc","application\/x-nzb":"nzb","application\/x-pkcs12":"p12","application\/x-pkcs7-certificates":"p7b","application\/x-pkcs7-certreqresp":"p7r","application\/x-research-info-systems":"ris","application\/x-shar":"shar","application\/x-shockwave-flash":"swf","application\/x-silverlight-app":"xap","application\/x-sql":"sql","application\/x-stuffit":"sit","application\/x-stuffitx":"sitx","application\/x-subrip":"srt","application\/x-sv4cpio":"sv4cpio","application\/x-sv4crc":"sv4crc","application\/x-t3vm-image":"t3","application\/x-tads":"gam","application\/x-tar":"tar","application\/x-tcl":"tcl","application\/x-tex":"tex","application\/x-tex-tfm":"tfm","application\/x-texinfo":"texinfo","application\/x-tgif":"obj","application\/x-ustar":"ustar","application\/x-wais-source":"src","application\/x-x509-ca-cert":"der","application\/x-xfig":"fig","application\/x-xliff+xml":"xlf","application\/x-xpinstall":"xpi","application\/x-xz":"xz","application\/x-zmachine":"z1","application\/xaml+xml":"xaml","application\/xcap-diff+xml":"xdf","application\/xenc+xml":"xenc","application\/xhtml+xml":"xhtml","application\/xml":"xsl","application\/xml-dtd":"dtd","application\/xop+xml":"xop","application\/xproc+xml":"xpl","application\/xslt+xml":"xslt","application\/xspf+xml":"xspf","application\/xv+xml":"mxml","application\/yang":"yang","application\/yin+xml":"yin","application\/zip":"zip","audio\/adpcm":"adp","audio\/basic":"au","audio\/midi":"mid","audio\/mp4":"m4a","audio\/mpeg":"mpga","audio\/ogg":"oga","audio\/s3m":"s3m","audio\/silk":"sil","audio\/vnd.dece.audio":"uva","audio\/vnd.digital-winds":"eol","audio\/vnd.dra":"dra","audio\/vnd.dts":"dts","audio\/vnd.dts.hd":"dtshd","audio\/vnd.lucent.voice":"lvp","audio\/vnd.ms-playready.media.pya":"pya","audio\/vnd.nuera.ecelp4800":"ecelp4800","audio\/vnd.nuera.ecelp7470":"ecelp7470","audio\/vnd.nuera.ecelp9600":"ecelp9600","audio\/vnd.rip":"rip","audio\/webm":"weba","audio\/x-aac":"aac","audio\/x-aiff":"aif","audio\/x-caf":"caf","audio\/x-flac":"flac","audio\/x-matroska":"mka","audio\/x-mpegurl":"m3u","audio\/x-ms-wax":"wax","audio\/x-ms-wma":"wma","audio\/x-pn-realaudio":"ram","audio\/x-pn-realaudio-plugin":"rmp","audio\/xm":"xm","chemical\/x-cdx":"cdx","chemical\/x-cif":"cif","chemical\/x-cmdf":"cmdf","chemical\/x-cml":"cml","chemical\/x-csml":"csml","chemical\/x-xyz":"xyz","font\/collection":"ttc","font\/otf":"otf","font\/ttf":"ttf","font\/woff":"woff","font\/woff2":"woff2","image\/cgm":"cgm","image\/g3fax":"g3","image\/gif":"gif","image\/ief":"ief","image\/jpeg":"jpeg","image\/ktx":"ktx","image\/png":"png","image\/prs.btif":"btif","image\/sgi":"sgi","image\/svg+xml":"svg","image\/tiff":"tiff","image\/vnd.adobe.photoshop":"psd","image\/vnd.dece.graphic":"uvi","image\/vnd.djvu":"djvu","image\/vnd.dvb.subtitle":"sub","image\/vnd.dwg":"dwg","image\/vnd.dxf":"dxf","image\/vnd.fastbidsheet":"fbs","image\/vnd.fpx":"fpx","image\/vnd.fst":"fst","image\/vnd.fujixerox.edmics-mmr":"mmr","image\/vnd.fujixerox.edmics-rlc":"rlc","image\/vnd.ms-modi":"mdi","image\/vnd.ms-photo":"wdp","image\/vnd.net-fpx":"npx","image\/vnd.wap.wbmp":"wbmp","image\/vnd.xiff":"xif","image\/webp":"webp","image\/x-3ds":"3ds","image\/x-cmu-raster":"ras","image\/x-cmx":"cmx","image\/x-freehand":"fh","image\/x-icon":"ico","image\/x-mrsid-image":"sid","image\/x-pcx":"pcx","image\/x-pict":"pic","image\/x-portable-anymap":"pnm","image\/x-portable-bitmap":"pbm","image\/x-portable-graymap":"pgm","image\/x-portable-pixmap":"ppm","image\/x-rgb":"rgb","image\/x-xpixmap":"xpm","image\/x-xwindowdump":"xwd","message\/rfc822":"eml","model\/iges":"igs","model\/mesh":"msh","model\/vnd.collada+xml":"dae","model\/vnd.dwf":"dwf","model\/vnd.gdl":"gdl","model\/vnd.gtw":"gtw","model\/vnd.vtu":"vtu","model\/vrml":"wrl","model\/x3d+binary":"x3db","model\/x3d+vrml":"x3dv","model\/x3d+xml":"x3d","text\/cache-manifest":"appcache","text\/calendar":"ics","text\/css":"css","text\/csv":"csv","text\/html":"html","text\/n3":"n3","text\/plain":"txt","text\/prs.lines.tag":"dsc","text\/richtext":"rtx","text\/sgml":"sgml","text\/tab-separated-values":"tsv","text\/troff":"t","text\/turtle":"ttl","text\/uri-list":"uri","text\/vcard":"vcard","text\/vnd.curl":"curl","text\/vnd.curl.dcurl":"dcurl","text\/vnd.curl.mcurl":"mcurl","text\/vnd.curl.scurl":"scurl","text\/vnd.fly":"fly","text\/vnd.fmi.flexstor":"flx","text\/vnd.graphviz":"gv","text\/vnd.in3d.3dml":"3dml","text\/vnd.in3d.spot":"spot","text\/vnd.sun.j2me.app-descriptor":"jad","text\/vnd.wap.wml":"wml","text\/vnd.wap.wmlscript":"wmls","text\/x-asm":"s","text\/x-c":"cc","text\/x-fortran":"f","text\/x-java-source":"java","text\/x-nfo":"nfo","text\/x-opml":"opml","text\/x-pascal":"p","text\/x-setext":"etx","text\/x-sfv":"sfv","text\/x-uuencode":"uu","text\/x-vcalendar":"vcs","text\/x-vcard":"vcf","video\/3gpp":"3gp","video\/3gpp2":"3g2","video\/h261":"h261","video\/h263":"h263","video\/h264":"h264","video\/jpeg":"jpgv","video\/jpm":"jpm","video\/mj2":"mj2","video\/mp4":"mp4","video\/mpeg":"mpeg","video\/quicktime":"qt","video\/vnd.dece.hd":"uvh","video\/vnd.dece.mobile":"uvm","video\/vnd.dece.pd":"uvp","video\/vnd.dece.sd":"uvs","video\/vnd.dece.video":"uvv","video\/vnd.dvb.file":"dvb","video\/vnd.fvt":"fvt","video\/vnd.mpegurl":"mxu","video\/vnd.ms-playready.media.pyv":"pyv","video\/vnd.uvvu.mp4":"uvu","video\/vnd.vivo":"viv","video\/webm":"webm","video\/x-f4v":"f4v","video\/x-fli":"fli","video\/x-flv":"flv","video\/x-m4v":"m4v","video\/x-matroska":"mkv","video\/x-mng":"mng","video\/x-ms-asf":"asf","video\/x-ms-vob":"vob","video\/x-ms-wmx":"wmx","video\/x-ms-wvx":"wvx","video\/x-msvideo":"avi","video\/x-sgi-movie":"movie","video\/x-smv":"smv","x-conference\/x-cooltalk":"ice","text\/x-sql":"sql","image\/x-pixlr-data":"pxd","image\/x-adobe-dng":"dng","image\/x-sketch":"sketch","image\/x-xcf":"xcf","audio\/amr":"amr","application\/plt":"plt","application\/sat":"sat","application\/step":"step","text\/x-httpd-cgi":"cgi","text\/x-asap":"asp","text\/x-jsp":"jsp"}; /* * File: /js/elFinder.options.js */ /** * Default elFinder config * * @type Object * @autor Dmitry (dio) Levashov */ elFinder.prototype._options = { /** * URLs of 3rd party libraries CDN * * @type Object */ cdns : { // for editor etc. ace : 'https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.1', codemirror : 'https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.40.2', ckeditor : 'https://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.10.0', ckeditor5 : 'https://cdn.ckeditor.com/ckeditor5/11.1.1', tinymce : 'https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.8.3', simplemde : 'https://cdnjs.cloudflare.com/ajax/libs/simplemde/1.11.2', fabric16 : 'https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.7', tui : 'https://uicdn.toast.com', // for quicklook etc. hls : 'https://cdnjs.cloudflare.com/ajax/libs/hls.js/0.10.1/hls.min.js', dash : 'https://cdnjs.cloudflare.com/ajax/libs/dashjs/2.9.1/dash.all.min.js', flv : 'https://cdnjs.cloudflare.com/ajax/libs/flv.js/1.4.2/flv.min.js', prettify : 'https://cdn.jsdelivr.net/gh/google/code-prettify@453bd5f51e61245339b738b1bbdd42d7848722ba/loader/run_prettify.js', psd : 'https://cdnjs.cloudflare.com/ajax/libs/psd.js/3.2.0/psd.min.js', rar : 'https://cdn.jsdelivr.net/gh/nao-pon/rar.js@6cef13ec66dd67992fc7f3ea22f132d770ebaf8b/rar.min.js', zlibUnzip : 'https://cdn.jsdelivr.net/gh/imaya/zlib.js@0.3.1/bin/unzip.min.js', // need check unzipFiles() in quicklook.plugins.js when update zlibGunzip : 'https://cdn.jsdelivr.net/gh/imaya/zlib.js@0.3.1/bin/gunzip.min.js', marked : 'https://cdnjs.cloudflare.com/ajax/libs/marked/0.5.1/marked.min.js', sparkmd5 : 'https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.0/spark-md5.min.js', jssha : 'https://cdnjs.cloudflare.com/ajax/libs/jsSHA/2.3.1/sha.js', amr : 'https://cdn.jsdelivr.net/gh/yxl/opencore-amr-js@dcf3d2b5f384a1d9ded2a54e4c137a81747b222b/js/amrnb.js' }, /** * Connector url. Required! * * @type String */ url : '', /** * Ajax request type. * * @type String * @default "get" */ requestType : 'get', /** * Use CORS to connector url * * @type Boolean|null true|false|null(Auto detect) */ cors : null, /** * Maximum number of concurrent connections on request * * @type Number * @default 3 */ requestMaxConn : 3, /** * Transport to send request to backend. * Required for future extensions using websockets/webdav etc. * Must be an object with "send" method. * transport.send must return jQuery.Deferred() object * * @type Object * @default null * @example * transport : { * init : function(elfinderInstance) { }, * send : function(options) { * var dfrd = jQuery.Deferred(); * // connect to backend ... * return dfrd; * }, * upload : function(data) { * var dfrd = jQuery.Deferred(); * // upload ... * return dfrd; * } * * } **/ transport : {}, /** * URL to upload file to. * If not set - connector URL will be used * * @type String * @default '' */ urlUpload : '', /** * Allow to drag and drop to upload files * * @type Boolean|String * @default 'auto' */ dragUploadAllow : 'auto', /** * Confirmation dialog displayed at the time of overwriting upload * * @type Boolean * @default true */ overwriteUploadConfirm : true, /** * Max size of chunked data of file upload * * @type Number * @default 10485760(10MB) */ uploadMaxChunkSize : 10485760, /** * Regular expression of file name to exclude when uploading folder * * @type Object * @default { win: /^(?:desktop\.ini|thumbs\.db)$/i, mac: /^\.ds_store$/i } */ folderUploadExclude : { win: /^(?:desktop\.ini|thumbs\.db)$/i, mac: /^\.ds_store$/i }, /** * Timeout for upload using iframe * * @type Number * @default 0 - no timeout */ iframeTimeout : 0, /** * Data to append to all requests and to upload files * * @type Object * @default {} */ customData : {}, /** * Event listeners to bind on elFinder init * * @type Object * @default {} */ handlers : {}, /** * Any custom headers to send across every ajax request * * @type Object * @default {} */ customHeaders : {}, /** * Any custom xhrFields to send across every ajax request * * @type Object * @default {} */ xhrFields : {}, /** * Interface language * * @type String * @default "en" */ lang : 'en', /** * Base URL of elfFinder library starting from Manager HTML * Auto detect when empty value * * @type String * @default "" */ baseUrl : '', /** * Base URL of i18n js files * baseUrl + "js/i18n/" when empty value * * @type String * @default "" */ i18nBaseUrl : '', /** * Auto load required CSS * `false` to disable this function or * CSS URL Array to load additional CSS files * * @type Boolean|Array * @default true */ cssAutoLoad : true, /** * Theme to load * {"themeid" : "Theme CSS URL"} or * {"themeid" : "Theme manifesto.json URL"} or * Theme manifesto.json Object * { * "themeid" : { * "name":"Theme Name", * "cssurls":"Theme CSS URL", * "author":"Author Name", * "email":"Author Email", * "license":"License", * "link":"Web Site URL", * "image":"Screen Shot URL", * "description":"Description" * } * } * * @type Object */ themes : {}, /** * Theme id to initial theme * * @type String|Null */ theme : null, /** * Maximum value of error dialog open at the same time * * @type Number */ maxErrorDialogs : 5, /** * Additional css class for filemanager node. * * @type String */ cssClass : '', /** * Active commands list. '*' means all of the commands that have been load. * If some required commands will be missed here, elFinder will add its * * @type Array */ commands : ['*'], // Available commands list //commands : [ // 'archive', 'back', 'chmod', 'colwidth', 'copy', 'cut', 'download', 'duplicate', 'edit', 'extract', // 'forward', 'fullscreen', 'getfile', 'help', 'home', 'info', 'mkdir', 'mkfile', 'netmount', 'netunmount', // 'open', 'opendir', 'paste', 'places', 'quicklook', 'reload', 'rename', 'resize', 'restore', 'rm', // 'search', 'sort', 'up', 'upload', 'view', 'zipdl' //], /** * Commands options. * * @type Object **/ commandsOptions : { // // configure shortcuts of any command // // add `shortcuts` property into each command // any_command_name : { // shortcuts : [] // for disable this command's shortcuts // }, // any_command_name : { // shortcuts : function(fm, shortcuts) { // // for add `CTRL + E` for this command action // shortcuts[0]['pattern'] += ' ctrl+e'; // return shortcuts; // } // }, // any_command_name : { // shortcuts : function(fm, shortcuts) { // // for full customize of this command's shortcuts // return [ { pattern: 'ctrl+e ctrl+down numpad_enter' + (fm.OS != 'mac' && ' enter') } ]; // } // }, // "getfile" command options. getfile : { onlyURL : false, // allow to return multiple files info multiple : false, // allow to return filers info folders : false, // action after callback (""/"close"/"destroy") oncomplete : '', // action when callback is fail (""/"close"/"destroy") onerror : '', // get path before callback call getPath : true, // get image sizes before callback call getImgSize : false }, open : { // HTTP method that request to the connector when item URL is not valid URL. // If you set to "get" will be displayed request parameter in the browser's location field // so if you want to conceal its parameters should be given "post". // Nevertheless, please specify "get" if you want to enable the partial request by HTTP Range header. method : 'post', // Where to open into : 'window'(default), 'tab' or 'tabs' // 'tabs' opens in each tabs into : 'window', // Default command list of action when select file // String value that is 'Command Name' or 'Command Name1/CommandName2...' selectAction : 'open' }, opennew : { // URL of to open elFinder manager // Default '' : Origin URL url : '', // Use search query of origin URL useOriginQuery : true }, // "upload" command options. upload : { // Open elFinder upload dialog: 'button' OR Open system OS upload dialog: 'uploadbutton' ui : 'button' }, // "download" command options. download : { // max request to download files when zipdl disabled maxRequests : 10, // minimum count of files to use zipdl minFilesZipdl : 2 }, // "quicklook" command options. quicklook : { autoplay : true, width : 450, height : 300, // ControlsList of HTML5 audio/video preview // see https://googlechrome.github.io/samples/media/controlslist.html mediaControlsList : '', // e.g. 'nodownload nofullscreen noremoteplayback' // Show toolbar of PDF preview (with <embed> tag) pdfToolbar : true, // Maximum characters length to preview textMaxlen : 2000, // quicklook window must be contained in elFinder node on window open (true|false) contain : false, // preview window into NavDock (0 : undocked | 1 : docked(show) | 2 : docked(hide)) docked : 0, // Docked preview height ('auto' or Number of pixel) 'auto' is setted to the Navbar width dockHeight : 'auto', // media auto play when docked dockAutoplay : false, // Google Maps API key (Require Maps JavaScript API) googleMapsApiKey : '', // Google Maps API Options googleMapsOpts : { maps : {}, kml : { suppressInfoWindows : false, preserveViewport : false } }, // ViewerJS (https://viewerjs.org/) Options // To enable this you need to place ViewerJS on the same server as elFinder and specify that URL in `url`. viewerjs : { url: '', // Example '/ViewerJS/index.html' mimes: ['application/pdf', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.presentation'] }, // MIME types to CAD-Files and 3D-Models online viewer on sharecad.org // Example ['image/vnd.dwg', 'image/vnd.dxf', 'model/vnd.dwf', 'application/vnd.hp-hpgl', 'application/plt', 'application/step', 'model/iges', 'application/vnd.ms-pki.stl', 'application/sat', 'image/cgm', 'application/x-msmetafile'] sharecadMimes : [], // MIME types to use Google Docs online viewer // Example ['application/pdf', 'image/tiff', 'application/vnd.ms-office', 'application/msword', 'application/vnd.ms-word', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/postscript', 'application/rtf'] googleDocsMimes : [], // MIME types to use Microsoft Office Online viewer // Example ['application/msword', 'application/vnd.ms-word', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.presentation'] // These MIME types override "googleDocsMimes" officeOnlineMimes : [], // File size (byte) threshold when using the dim command for obtain the image size necessary to image preview getDimThreshold : 200000, // MIME-Type regular expression that does not check empty files mimeRegexNotEmptyCheck : /^application\/vnd\.google-apps\./ }, // "quicklook" command options. edit : { // dialog width, integer(px) or integer+'%' (example: 650, '80%' ...) dialogWidth : void(0), // list of allowed mimetypes to edit of text files // if empty - any text files can be edited mimes : [], // MIME-types of text file to make as empty files makeTextMimes : ['text/plain', 'text/css', 'text/html'], // Use the editor stored in the browser // This value allowd overwrite with user preferences useStoredEditor : false, // Open the maximized editor window // This value allowd overwrite with user preferences editorMaximized : false, // edit files in wysisyg's editors : [ // { // /** // * editor info // * @type Object // */ // info : { name: 'Editor Name' }, // /** // * files mimetypes allowed to edit in current wysisyg // * @type Array // */ // mimes : ['text/html'], // /** // * HTML element for editing area (optional for text editor) // * @type String // */ // html : '<textarea></textarea>', // /** // * Initialize editing area node (optional for text editor) // * // * @param String dialog DOM id // * @param Object target file object // * @param String target file content (text or Data URI Scheme(binary file)) // * @param Object elFinder instance // * @type Function // */ // init : function(id, file, content, fm) { // jQuery(this).attr('id', id + '-text').val(content); // }, // /** // * Get edited contents (optional for text editor) // * @type Function // */ // getContent : function() { // return jQuery(this).val(); // }, // /** // * Called when "edit" dialog loaded. // * Place to init wysisyg. // * Can return wysisyg instance // * // * @param DOMElement textarea node // * @return Object editor instance|jQuery.Deferred(return instance on resolve()) // */ // load : function(textarea) { }, // /** // * Called before "edit" dialog closed. // * Place to destroy wysisyg instance. // * // * @param DOMElement textarea node // * @param Object wysisyg instance (if was returned by "load" callback) // * @return void // */ // close : function(textarea, instance) { }, // /** // * Called before file content send to backend. // * Place to update textarea content if needed. // * // * @param DOMElement textarea node // * @param Object wysisyg instance (if was returned by "load" callback) // * @return void // */ // save : function(textarea, instance) {}, // /** // * Called after load() or save(). // * Set focus to wysisyg editor. // * // * @param DOMElement textarea node // * @param Object wysisyg instance (if was returned by "load" callback) // * @return void // */ // focus : function(textarea, instance) {} // /** // * Called after dialog resized.. // * // * @param DOMElement textarea node // * @param Object wysisyg instance (if was returned by "load" callback) // * @param Object resize event object // * @param Object data object // * @return void // */ // resize : function(textarea, instance, event, data) {} // // } ], // Character encodings of select box encodings : ['Big5', 'Big5-HKSCS', 'Cp437', 'Cp737', 'Cp775', 'Cp850', 'Cp852', 'Cp855', 'Cp857', 'Cp858', 'Cp862', 'Cp866', 'Cp874', 'EUC-CN', 'EUC-JP', 'EUC-KR', 'GB18030', 'ISO-2022-CN', 'ISO-2022-JP', 'ISO-2022-KR', 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4', 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9', 'ISO-8859-13', 'ISO-8859-15', 'KOI8-R', 'KOI8-U', 'Shift-JIS', 'Windows-1250', 'Windows-1251', 'Windows-1252', 'Windows-1253', 'Windows-1254', 'Windows-1257'], // options for extra editors extraOptions : { // TUI Image Editor's options tuiImgEditOpts : { // Path prefix of icon-a.svg, icon-b.svg, icon-c.svg and icon-d.svg in the Theme. // `iconsPath` MUST follow the same origin policy. iconsPath : void(0), // default is "./img/tui-" // Theme object theme : {} }, // Pixo image editor constructor options - https://pixoeditor.com/ // Require 'apikey' to enable it pixo: { apikey: '' }, // Specify the Creative Cloud API key when using Creative SDK image editor of Creative Cloud. // You can get the API key at https://console.adobe.io/. creativeCloudApiKey : '', // Browsing manager URL for CKEditor, TinyMCE // Uses self location with the empty value or not defined. //managerUrl : 'elfinder.html' managerUrl : null, // CKEditor5' builds mode - 'classic', 'inline' or 'balloon' ckeditor5Mode : 'balloon', // Setting for Online-Convert.com onlineConvert : { maxSize : 100, // (MB) Max 100MB on free account showLink : true // It must be enabled with free account } } }, search : { // Incremental search from the current view incsearch : { enable : true, // is enable true or false minlen : 1, // minimum number of characters wait : 500 // wait milliseconds }, // Additional search types searchTypes : { // "SearchMime" is implemented in default SearchMime : { // The key is search type that send to the connector name : 'btnMime', // Button text to be processed in i18n() title : 'searchMime' // Button title to be processed in i18n() } } }, // "info" command options. info : { // If the URL of the Directory is null, // it is assumed that the link destination is a URL to open the folder in elFinder nullUrlDirLinkSelf : true, // Information items to be hidden by default // These name are 'size', 'aliasfor', 'path', 'link', 'dim', 'modify', 'perms', 'locked', 'owner', 'group', 'perm' and your custom info items label hideItems : [], // Maximum file size (byte) to get file contents hash (md5, sha256 ...) showHashMaxsize : 104857600, // 100 MB // Array of hash algorisms to show on info dialog // These name are 'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'shake128' and 'shake256' showHashAlgorisms : ['md5', 'sha256'], custom : { // /** // * Example of custom info `desc` // */ // desc : { // /** // * Lable (require) // * It is filtered by the `fm.i18n()` // * // * @type String // */ // label : 'Description', // // /** // * Template (require) // * `{id}` is replaced in dialog.id // * // * @type String // */ // tpl : '<div class="elfinder-info-desc"><span class="elfinder-spinner"></span></div>', // // /** // * Restricts to mimetypes (optional) // * Exact match or category match // * // * @type Array // */ // mimes : ['text', 'image/jpeg', 'directory'], // // /** // * Restricts to file.hash (optional) // * // * @ type Regex // */ // hashRegex : /^l\d+_/, // // /** // * Request that asks for the description and sets the field (optional) // * // * @type Function // */ // action : function(file, fm, dialog) { // fm.request({ // data : { cmd : 'desc', target: file.hash }, // preventDefault: true, // }) // .fail(function() { // dialog.find('div.elfinder-info-desc').html(fm.i18n('unknown')); // }) // .done(function(data) { // dialog.find('div.elfinder-info-desc').html(data.desc); // }); // } // } } }, mkdir: { // Enable automatic switching function ["New Folder" / "Into New Folder"] of toolbar buttton intoNewFolderToolbtn: false }, resize: { // defalt status of snap to 8px grid of the jpeg image ("enable" or "disable") grid8px : 'disable', // Preset size array [width, height] presetSize : [[320, 240], [400, 400], [640, 480], [800,600]], // File size (bytes) threshold when using the `dim` command for obtain the image size necessary to start editing getDimThreshold : 204800, // File size (bytes) to request to get substitute image (400px) with the `dim` command dimSubImgSize : 307200 }, rm: { // If trash is valid, items moves immediately to the trash holder without confirm. quickTrash : true, // Maximum wait seconds when checking the number of items to into the trash infoCheckWait : 10, // Maximum number of items that can be placed into the Trash at one time toTrashMaxItems : 1000 }, help : { // Tabs to show view : ['about', 'shortcuts', 'help', 'integrations', 'debug'], // HTML source URL of the heip tab helpSource : '' }, preference : { // dialog width width: 600, // dialog height height: 400, // tabs setting see preference.js : build() categories: null, // preference setting see preference.js : build() prefs: null, // language setting see preference.js : build() langs: null, // Command list of action when select file // Array value are 'Command Name' or 'Command Name1/CommandName2...' selectActions : ['open', 'edit/download', 'resize/edit/download', 'download', 'quicklook'] } }, /** * Callback for prepare boot up * * - The this object in the function is an elFinder node * - The first parameter is elFinder Instance * - The second parameter is an object of other parameters * For now it can use `dfrdsBeforeBootup` Array * * @type Function * @default null * @return void */ bootCallback : null, /** * Callback for "getfile" commands. * Required to use elFinder with WYSIWYG editors etc.. * * @type Function * @default null (command not active) */ getFileCallback : null, /** * Default directory view. icons/list * * @type String * @default "icons" */ defaultView : 'icons', /** * Hash of default directory path to open * * NOTE: This setting will be disabled if the target folder is specified in location.hash. * * If you want to find the hash in Javascript * can be obtained with the following code. (In the case of a standard hashing method) * * var volumeId = 'l1_'; // volume id * var path = 'path/to/target'; // without root path * //var path = 'path\\to\\target'; // use \ on windows server * var hash = volumeId + btoa(path).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '.').replace(/\.+$/, ''); * * @type String * @default "" */ startPathHash : '', /** * Emit a sound when a file is deleted * Sounds are in sounds/ folder * * @type Boolean * @default true */ sound : true, /** * UI plugins to load. * Current dir ui and dialogs loads always. * Here set not required plugins as folders tree/toolbar/statusbar etc. * * @type Array * @default ['toolbar', 'tree', 'path', 'stat'] * @full ['toolbar', 'places', 'tree', 'path', 'stat'] */ ui : ['toolbar', 'tree', 'path', 'stat'], /** * Some UI plugins options. * @type Object */ uiOptions : { // toolbar configuration toolbar : [ ['home', 'back', 'forward', 'up', 'reload'], ['netmount'], ['mkdir', 'mkfile', 'upload'], ['open', 'download', 'getfile'], ['undo', 'redo'], ['copy', 'cut', 'paste', 'rm', 'empty', 'hide'], ['duplicate', 'rename', 'edit', 'resize', 'chmod'], ['selectall', 'selectnone', 'selectinvert'], ['quicklook', 'info'], ['extract', 'archive'], ['search'], ['view', 'sort'], ['help'], ['fullscreen'] ], // toolbar extra options toolbarExtra : { // also displays the text label on the button (true / false / 'none') displayTextLabel: false, // Exclude `displayTextLabel` setting UA type labelExcludeUA: ['Mobile'], // auto hide on initial open autoHideUA: ['Mobile'], // Initial setting value of hide button in toolbar setting defaultHides: ['home', 'reload'], // show Preference button ('none', 'auto', 'always') // If you do not include 'preference' in the context menu you should specify 'auto' or 'always' showPreferenceButton: 'none', // show Preference button into contextmenu of the toolbar (true / false) preferenceInContextmenu: true }, // directories tree options tree : { // expand current root on init openRootOnLoad : true, // expand current work directory on open openCwdOnOpen : true, // auto loading current directory parents and do expand their node. syncTree : true, // Maximum number of display of each child trees // The tree of directories with children exceeding this number will be split subTreeMax : 100, // Numbar of max connctions of subdirs request subdirsMaxConn : 2, // Number of max simultaneous processing directory of subdirs subdirsAtOnce : 5, // Durations of each animations durations : { slideUpDown : 'fast', autoScroll : 'fast' } // , // /** // * Add CSS class name to navbar directories (optional) // * see: https://github.com/Studio-42/elFinder/pull/1061, // * https://github.com/Studio-42/elFinder/issues/1231 // * // * @type Function // */ // getClass: function(dir) { // // e.g. This adds the directory's name (lowercase) with prefix as a CSS class // return 'elfinder-tree-' + dir.name.replace(/[ "]/g, '').toLowerCase(); // } }, // navbar options navbar : { minWidth : 150, maxWidth : 500, // auto hide on initial open autoHideUA: [] // e.g. ['Mobile'] }, navdock : { // disabled navdock ui disabled : false, // percentage of initial maximum height to work zone initMaxHeight : '50%', // percentage of maximum height to work zone by user resize action maxHeight : '90%' }, cwd : { // display parent folder with ".." name :) oldSchool : false, // fm.UA types array to show item select checkboxes e.g. ['All'] or ['Mobile'] etc. default: ['Touch'] showSelectCheckboxUA : ['Touch'], // file info columns displayed listView : { // name is always displayed, cols are ordered // e.g. ['perm', 'date', 'size', 'kind', 'owner', 'group', 'mode'] // mode: 'mode'(by `fileModeStyle` setting), 'modestr'(rwxr-xr-x) , 'modeoct'(755), 'modeboth'(rwxr-xr-x (755)) // 'owner', 'group' and 'mode', It's necessary set volume driver option "statOwner" to `true` // for custom, characters that can be used in the name is `a-z0-9_` columns : ['perm', 'date', 'size', 'kind'], // override this if you want custom columns name // example // columnsCustomName : { // date : 'Last modification', // kind : 'Mime type' // } columnsCustomName : {}, // fixed list header colmun fixedHeader : true }, // icons view setting iconsView : { // default icon size (0-3 in default CSS (cwd.css - elfinder-cwd-size[number])) size: 0, // number of maximum size (3 in default CSS (cwd.css - elfinder-cwd-size[number])) // uses in preference.js sizeMax: 3, // Name of each size sizeNames: { 0: 'viewSmall', 1: 'viewMedium', 2: 'viewLarge', 3: 'viewExtraLarge' } }, // /** // * Add CSS class name to cwd directories (optional) // * see: https://github.com/Studio-42/elFinder/pull/1061, // * https://github.com/Studio-42/elFinder/issues/1231 // * // * @type Function // */ // , // getClass: function(file) { // // e.g. This adds the directory's name (lowercase) with prefix as a CSS class // return 'elfinder-cwd-' + file.name.replace(/[ "]/g, '').toLowerCase(); //} //, //// Template placeholders replacement rules for overwrite. see ui/cwd.js replacement //replacement : { // tooltip : function(f, fm) { // var list = fm.viewType == 'list', // current view type // query = fm.searchStatus.state == 2, // is in search results // title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''), // info = ''; // if (query && f.path) { // info = fm.escape(f.path.replace(/\/[^\/]*$/, '')); // } else { // info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, ' ') : ''; // } // if (list) { // info += (info? ' ' : '') + fm.escape(f.name); // } // return info? info + ' ' + title : title; // } //} }, path : { // Move to head of work zone without UI navbar toWorkzoneWithoutNavbar : true }, dialog : { // Enable to auto focusing on mouse over in the target form element focusOnMouseOver : true }, toast : { animate : { // to show showMethod: 'fadeIn', // fadeIn, slideDown, and show are built into jQuery showDuration: 300, // milliseconds showEasing: 'swing', // swing and linear are built into jQuery // timeout to hide timeOut: 3000, // to hide hideMethod: 'fadeOut', hideDuration: 1500, hideEasing: 'swing' } } }, /** * MIME regex of send HTTP header "Content-Disposition: inline" or allow preview in quicklook * This option will overwrite by connector configuration * * @type String * @default '^(?:(?:image|video|audio)|text/plain|application/pdf$)' * @example * dispInlineRegex : '.', // is allow inline of all of MIME types * dispInlineRegex : '$^', // is not allow inline of all of MIME types */ dispInlineRegex : '^(?:(?:image|video|audio)|application/(?:x-mpegURL|dash\+xml)|(?:text/plain|application/pdf)$)', /** * Display only required files by types * * @type Array * @default [] * @example * onlyMimes : ["image"] - display all images * onlyMimes : ["image/png", "application/x-shockwave-flash"] - display png and flash */ onlyMimes : [], /** * Custom files sort rules. * All default rules (name/size/kind/date/perm/mode/owner/group) set in elFinder._sortRules * * @type {Object} * @example * sortRules : { * name : function(file1, file2) { return file1.name.toLowerCase().localeCompare(file2.name.toLowerCase()); } * } */ sortRules : {}, /** * Default sort type. * * @type {String} */ sortType : 'name', /** * Default sort order. * * @type {String} * @default "asc" */ sortOrder : 'asc', /** * Display folders first? * * @type {Boolean} * @default true */ sortStickFolders : true, /** * Sort also applies to the treeview (null: disable this feature) * * @type Boolean|null * @default false */ sortAlsoTreeview : false, /** * If true - elFinder will formating dates itself, * otherwise - backend date will be used. * * @type Boolean */ clientFormatDate : true, /** * Show UTC dates. * Required set clientFormatDate to true * * @type Boolean */ UTCDate : false, /** * File modification datetime format. * Value from selected language data is used by default. * Set format here to overwrite it. * * @type String * @default "" */ dateFormat : '', /** * File modification datetime format in form "Yesterday 12:23:01". * Value from selected language data is used by default. * Set format here to overwrite it. * Use $1 for "Today"/"Yesterday" placeholder * * @type String * @default "" * @example "$1 H:m:i" */ fancyDateFormat : '', /** * Style of file mode at cwd-list, info dialog * 'string' (ex. rwxr-xr-x) or 'octal' (ex. 755) or 'both' (ex. rwxr-xr-x (755)) * * @type {String} * @default 'both' */ fileModeStyle : 'both', /** * elFinder width * * @type String|Number * @default "auto" */ width : 'auto', /** * elFinder node height * Number: pixcel or String: Number + "%" * * @type Number | String * @default 400 */ height : 400, /** * Base node object or selector * Element which is the reference of the height percentage * * @type Object|String * @default null | jQuery(window) (if height is percentage) **/ heightBase : null, /** * Make elFinder resizable if jquery ui resizable available * * @type Boolean * @default true */ resizable : true, /** * Timeout before open notifications dialogs * * @type Number * @default 500 (.5 sec) */ notifyDelay : 500, /** * Position CSS, Width of notifications dialogs * * @type Object * @default {position: {}, width : null} - Apply CSS definition * position: CSS object | null (null: position center & middle) */ notifyDialog : {position: {}, width : null}, /** * Dialog contained in the elFinder node * * @type Boolean * @default false */ dialogContained : false, /** * Allow shortcuts * * @type Boolean * @default true */ allowShortcuts : true, /** * Remeber last opened dir to open it after reload or in next session * * @type Boolean * @default true */ rememberLastDir : true, /** * Clear historys(elFinder) on reload(not browser) function * Historys was cleared on Reload function on elFinder 2.0 (value is true) * * @type Boolean * @default false */ reloadClearHistory : false, /** * Use browser native history with supported browsers * * @type Boolean * @default true */ useBrowserHistory : true, /** * Lazy load config. * How many files display at once? * * @type Number * @default 50 */ showFiles : 50, /** * Lazy load config. * Distance in px to cwd bottom edge to start display files * * @type Number * @default 50 */ showThreshold : 50, /** * Additional rule to valid new file name. * By default not allowed empty names or '..' * This setting does not have a sense of security. * * @type false|RegExp|function * @default false * @example * disable names with spaces: * validName : /^[^\s]+$/, */ validName : false, /** * Additional rule to filtering for browsing. * This setting does not have a sense of security. * * The object `this` is elFinder instance object in this function * * @type false|RegExp|function * @default false * @example * show only png and jpg files: * fileFilter : /.*\.(png|jpg)$/i, * * show only image type files: * fileFilter : function(file) { return file.mime && file.mime.match(/^image\//i); }, */ fileFilter : false, /** * Backup name suffix. * * @type String * @default "~" */ backupSuffix : '~', /** * Sync content interval * * @type Number * @default 0 (do not sync) */ sync : 0, /** * Sync start on load if sync value >= 1000 * * @type Bool * @default true */ syncStart : true, /** * How many thumbnails create in one request * * @type Number * @default 5 */ loadTmbs : 5, /** * Cookie option for browsersdoes not suppot localStorage * * @type Object */ cookie : { expires : 30, domain : '', path : '/', secure : false }, /** * Contextmenu config * * @type Object */ contextmenu : { // navbarfolder menu navbar : ['open', 'opennew', 'download', '|', 'upload', 'mkdir', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', 'empty', 'hide', '|', 'rename', '|', 'archive', '|', 'places', 'info', 'chmod', 'netunmount'], // current directory menu cwd : ['undo', 'redo', '|', 'back', 'up', 'reload', '|', 'upload', 'mkdir', 'mkfile', 'paste', '|', 'empty', 'hide', '|', 'view', 'sort', 'selectall', 'colwidth', '|', 'places', 'info', 'chmod', 'netunmount', '|', 'fullscreen'], // current directory file menu files : ['getfile', '|' ,'open', 'opennew', 'download', 'opendir', 'quicklook', '|', 'upload', 'mkdir', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', 'empty', 'hide', '|', 'rename', 'edit', 'resize', '|', 'archive', 'extract', '|', 'selectall', 'selectinvert', '|', 'places', 'info', 'chmod', 'netunmount'] }, /** * elFinder node enable always * This value will set to `true` if <body> has elFinder node only * * @type Bool * @default false */ enableAlways : false, /** * elFinder node enable by mouse over * * @type Bool * @default true */ enableByMouseOver : true, /** * Show window close confirm dialog * Value is which state to show * 'hasNotifyDialog', 'editingFile', 'hasSelectedItem' and 'hasClipboardData' * * @type Array * @default ['hasNotifyDialog', 'editingFile'] */ windowCloseConfirm : ['hasNotifyDialog', 'editingFile'], /** * Function decoding 'raw' string converted to unicode * It is used instead of fm.decodeRawString(str) * * @type Null|Function */ rawStringDecoder : typeof Encoding === 'object' && jQuery.isFunction(Encoding.convert)? function(str) { return Encoding.convert(str, { to: 'UNICODE', type: 'string' }); } : null, /** * Debug config * * @type Array|String('auto')|Boolean(true|false) */ // debug : true debug : ['error', 'warning', 'event-destroy'] }; /* * File: /js/elFinder.options.netmount.js */ /** * Default elFinder config of commandsOptions.netmount * * @type Object */ elFinder.prototype._options.commandsOptions.netmount = { ftp: { name : 'FTP', inputs: { host : jQuery('<input type="text"/>'), port : jQuery('<input type="number" placeholder="21" class="elfinder-input-optional"/>'), path : jQuery('<input type="text" value="/"/>'), user : jQuery('<input type="text"/>'), pass : jQuery('<input type="password" autocomplete="new-password"/>'), FTPS : jQuery('<input type="checkbox" value="1" title="File Transfer Protocol over SSL/TLS"/>'), encoding : jQuery('<input type="text" placeholder="Optional" class="elfinder-input-optional"/>'), locale : jQuery('<input type="text" placeholder="Optional" class="elfinder-input-optional"/>') } }, dropbox2: elFinder.prototype.makeNetmountOptionOauth('dropbox2', 'Dropbox', 'Dropbox', {noOffline : true, root : '/', pathI18n : 'path', integrate : { title: 'Dropbox.com', link: 'https://www.dropbox.com' } }), googledrive: elFinder.prototype.makeNetmountOptionOauth('googledrive', 'Google Drive', 'Google', { integrate : { title: 'Google Drive', link: 'https://www.google.com/drive/' } }), onedrive: elFinder.prototype.makeNetmountOptionOauth('onedrive', 'One Drive', 'OneDrive', { integrate : { title: 'Microsoft OneDrive', link: 'https://onedrive.live.com' } }), box: elFinder.prototype.makeNetmountOptionOauth('box', 'Box', 'Box', { noOffline : true, integrate : { title: 'Box.com', link: 'https://www.box.com' } }) }; /* * File: /js/elFinder.history.js */ /** * @class elFinder.history * Store visited folders * and provide "back" and "forward" methods * * @author Dmitry (dio) Levashov */ elFinder.prototype.history = function(fm) { var self = this, /** * Update history on "open" event? * * @type Boolean */ update = true, /** * Directories hashes storage * * @type Array */ history = [], /** * Current directory index in history * * @type Number */ current, /** * Clear history * * @return void */ reset = function() { history = [fm.cwd().hash]; current = 0; update = true; }, /** * Browser native history object */ nativeHistory = (fm.options.useBrowserHistory && window.history && window.history.pushState)? window.history : null, /** * Open prev/next folder * * @Boolen open next folder? * @return jQuery.Deferred */ go = function(fwd) { if ((fwd && self.canForward()) || (!fwd && self.canBack())) { update = false; return fm.exec('open', history[fwd ? ++current : --current]).fail(reset); } return jQuery.Deferred().reject(); }, /** * Sets the native history. * * @param String thash target hash */ setNativeHistory = function(thash) { if (nativeHistory && (! nativeHistory.state || nativeHistory.state.thash !== thash)) { nativeHistory.pushState({thash: thash}, null, location.pathname + location.search + (thash? '#elf_' + thash : '')); } }; /** * Return true if there is previous visited directories * * @return Boolen */ this.canBack = function() { return current > 0; }; /** * Return true if can go forward * * @return Boolen */ this.canForward = function() { return current < history.length - 1; }; /** * Go back * * @return void */ this.back = go; /** * Go forward * * @return void */ this.forward = function() { return go(true); }; // bind to elfinder events fm.bind('init', function() { if (nativeHistory && !nativeHistory.state) { setNativeHistory(fm.startDir()); } }) .open(function() { var l = history.length, cwd = fm.cwd().hash; if (update) { current >= 0 && l > current + 1 && history.splice(current+1); history[history.length-1] != cwd && history.push(cwd); current = history.length - 1; } update = true; setNativeHistory(cwd); }) .reload(fm.options.reloadClearHistory && reset); }; /* * File: /js/elFinder.command.js */ /** * elFinder command prototype * * @type elFinder.command * @author Dmitry (dio) Levashov */ elFinder.prototype.command = function(fm) { /** * elFinder instance * * @type elFinder */ this.fm = fm; /** * Command name, same as class name * * @type String */ this.name = ''; /** * Dialog class name * * @type String */ this.dialogClass = ''; /** * Command icon class name with out 'elfinder-button-icon-' * Use this.name if it is empty * * @type String */ this.className = ''; /** * Short command description * * @type String */ this.title = ''; /** * Linked(Child) commands name * They are loaded together when tthis command is loaded. * * @type Array */ this.linkedCmds = []; /** * Current command state * * @example * this.state = -1; // command disabled * this.state = 0; // command enabled * this.state = 1; // command active (for example "fullscreen" command while elfinder in fullscreen mode) * @default -1 * @type Number */ this.state = -1; /** * If true, command can not be disabled by connector. * @see this.update() * * @type Boolen */ this.alwaysEnabled = false; /** * Do not change dirctory on removed current work directory * * @type Boolen */ this.noChangeDirOnRemovedCwd = false; /** * If true, this means command was disabled by connector. * @see this.update() * * @type Boolen */ this._disabled = false; /** * If true, this command is disabled on serach results * * @type Boolean */ this.disableOnSearch = false; /** * Call update() when event select fired * * @type Boolean */ this.updateOnSelect = true; /** * Sync toolbar button title on change * * @type Boolean */ this.syncTitleOnChange = false; /** * Keep display of the context menu when command execution * * @type Boolean */ this.keepContextmenu = false; /** * elFinder events defaults handlers. * Inside handlers "this" is current command object * * @type Object */ this._handlers = { enable : function() { this.update(void(0), this.value); }, disable : function() { this.update(-1, this.value); }, 'open reload load sync' : function() { this._disabled = !(this.alwaysEnabled || this.fm.isCommandEnabled(this.name)); this.update(void(0), this.value); this.change(); } }; /** * elFinder events handlers. * Inside handlers "this" is current command object * * @type Object */ this.handlers = {}; /** * Shortcuts * * @type Array */ this.shortcuts = []; /** * Command options * * @type Object */ this.options = {ui : 'button'}; /** * Callback functions on `change` event * * @type Array */ this.listeners = []; /** * Prepare object - * bind events and shortcuts * * @return void */ this.setup = function(name, opts) { var self = this, fm = this.fm, setCallback = function(s) { var cb = s.callback || function(e) { fm.exec(self.name, void(0), { _userAction: true, _currentType: 'shortcut' }); }; s.callback = function(e) { var enabled, checks = {}; if (self.enabled()) { if (fm.searchStatus.state < 2) { enabled = fm.isCommandEnabled(self.name); } else { jQuery.each(fm.selected(), function(i, h) { if (fm.optionsByHashes[h]) { checks[h] = true; } else { jQuery.each(fm.volOptions, function(id) { if (!checks[id] && h.indexOf(id) === 0) { checks[id] = true; return false; } }); } }); jQuery.each(checks, function(h) { enabled = fm.isCommandEnabled(self.name, h); if (! enabled) { return false; } }); } if (enabled) { self.event = e; cb.call(self); delete self.event; } } }; }, i, s, sc; this.name = name; this.title = fm.messages['cmd'+name] ? fm.i18n('cmd'+name) : ((this.extendsCmd && fm.messages['cmd'+this.extendsCmd]) ? fm.i18n('cmd'+this.extendsCmd) : name); this.options = Object.assign({}, this.options, opts); this.listeners = []; this.dialogClass = 'elfinder-dialog-' + name; if (opts.shortcuts) { if (typeof opts.shortcuts === 'function') { sc = opts.shortcuts(this.fm, this.shortcuts); } else if (Array.isArray(opts.shortcuts)) { sc = opts.shortcuts; } this.shortcuts = sc || []; } if (this.updateOnSelect) { this._handlers.select = function() { this.update(void(0), this.value); }; } jQuery.each(Object.assign({}, self._handlers, self.handlers), function(cmd, handler) { fm.bind(cmd, jQuery.proxy(handler, self)); }); for (i = 0; i < this.shortcuts.length; i++) { s = this.shortcuts[i]; setCallback(s); !s.description && (s.description = this.title); fm.shortcut(s); } if (this.disableOnSearch) { fm.bind('search searchend', function() { self._disabled = this.type === 'search'? true : ! (this.alwaysEnabled || fm.isCommandEnabled(name)); self.update(void(0), self.value); }); } this.init(); }; /** * Command specific init stuffs * * @return void */ this.init = function() {}; /** * Exec command * * @param Array target files hashes * @param Array|Object command value * @return jQuery.Deferred */ this.exec = function(files, opts) { return jQuery.Deferred().reject(); }; this.getUndo = function(opts, resData) { return false; }; /** * Return true if command disabled. * * @return Boolen */ this.disabled = function() { return this.state < 0; }; /** * Return true if command enabled. * * @return Boolen */ this.enabled = function() { return this.state > -1; }; /** * Return true if command active. * * @return Boolen */ this.active = function() { return this.state > 0; }; /** * Return current command state. * Must be overloaded in most commands * * @return Number */ this.getstate = function() { return -1; }; /** * Update command state/value * and rize 'change' event if smth changed * * @param Number new state or undefined to auto update state * @param mixed new value * @return void */ this.update = function(s, v) { var state = this.state, value = this.value; if (this._disabled && this.fm.searchStatus === 0) { this.state = -1; } else { this.state = s !== void(0) ? s : this.getstate(); } this.value = v; if (state != this.state || value != this.value) { this.change(); } }; /** * Bind handler / fire 'change' event. * * @param Function|undefined event callback * @return void */ this.change = function(c) { var cmd, i; if (typeof(c) === 'function') { this.listeners.push(c); } else { for (i = 0; i < this.listeners.length; i++) { cmd = this.listeners[i]; try { cmd(this.state, this.value); } catch (e) { this.fm.debug('error', e); } } } return this; }; /** * With argument check given files hashes and return list of existed files hashes. * Without argument return selected files hashes. * * @param Array|String|void hashes * @return Array */ this.hashes = function(hashes) { return hashes ? jQuery.grep(Array.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) ? true : false; }) : fm.selected(); }; /** * Return only existed files from given fils hashes | selected files * * @param Array|String|void hashes * @return Array */ this.files = function(hashes) { var fm = this.fm; return hashes ? jQuery.map(Array.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) || null; }) : fm.selectedFiles(); }; /** * Wrapper to fm.dialog() * * @param String|DOMElement content * @param Object options * @return Object jQuery element object */ this.fmDialog = function(content, options) { if (options.cssClass) { options.cssClass += ' ' + this.dialogClass; } else { options.cssClass = this.dialogClass; } return this.fm.dialog(content, options); }; }; /* * File: /js/elFinder.resources.js */ /** * elFinder resources registry. * Store shared data * * @type Object * @author Dmitry (dio) Levashov **/ elFinder.prototype.resources = { 'class' : { hover : 'ui-state-hover', active : 'ui-state-active', disabled : 'ui-state-disabled', draggable : 'ui-draggable', droppable : 'ui-droppable', adroppable : 'elfinder-droppable-active', cwdfile : 'elfinder-cwd-file', cwd : 'elfinder-cwd', tree : 'elfinder-tree', treeroot : 'elfinder-navbar-root', navdir : 'elfinder-navbar-dir', navdirwrap : 'elfinder-navbar-dir-wrapper', navarrow : 'elfinder-navbar-arrow', navsubtree : 'elfinder-navbar-subtree', navcollapse : 'elfinder-navbar-collapsed', navexpand : 'elfinder-navbar-expanded', treedir : 'elfinder-tree-dir', placedir : 'elfinder-place-dir', searchbtn : 'elfinder-button-search', editing : 'elfinder-to-editing', preventback : 'elfinder-prevent-back', tabstab : 'ui-state-default ui-tabs-tab ui-corner-top ui-tab', tabsactive : 'ui-tabs-active ui-state-active' }, tpl : { perms : '<span class="elfinder-perms"/>', lock : '<span class="elfinder-lock"/>', symlink : '<span class="elfinder-symlink"/>', navicon : '<span class="elfinder-nav-icon"/>', navspinner : '<span class="elfinder-spinner elfinder-navbar-spinner"/>', navdir : '<div class="elfinder-navbar-wrapper{root}"><span id="{id}" class="ui-corner-all elfinder-navbar-dir {cssclass}"><span class="elfinder-navbar-arrow"/><span class="elfinder-navbar-icon" {style}/>{symlink}{permissions}{name}</span><div class="elfinder-navbar-subtree" style="display:none"/></div>', placedir : '<div class="elfinder-navbar-wrapper"><span id="{id}" class="ui-corner-all elfinder-navbar-dir {cssclass}" title="{title}"><span class="elfinder-navbar-arrow"/><span class="elfinder-navbar-icon" {style}/>{symlink}{permissions}{name}</span><div class="elfinder-navbar-subtree" style="display:none"/></div>' }, // mimes.text will be overwritten with connector config if `textMimes` is included in initial response // @see php/elFInder.class.php `public static $textMimes` mimes : { text : [ 'application/dash+xml', 'application/docbook+xml', 'application/javascript', 'application/json', 'application/plt', 'application/sat', 'application/sql', 'application/step', 'application/vnd.hp-hpgl', 'application/x-awk', 'application/x-config', 'application/x-csh', 'application/x-empty', 'application/x-mpegurl', 'application/x-perl', 'application/x-php', 'application/x-web-config', 'application/xhtml+xml', 'application/xml', 'audio/x-mp3-playlist', 'image/cgm', 'image/svg+xml', 'image/vnd.dxf', 'model/iges' ] }, mixin : { make : function() { var self = this, fm = this.fm, cmd = this.name, req = this.requestCmd || cmd, wz = fm.getUI('workzone'), org = (this.origin && this.origin === 'navbar')? 'tree' : 'cwd', tree = (org === 'tree'), find = tree? 'navHash2Elm' : 'cwdHash2Elm', tarea= (! tree && fm.storage('view') != 'list'), sel = fm.selected(), move = this.move || false, empty= wz.hasClass('elfinder-cwd-wrapper-empty'), unselect = function() { requestAnimationFrame(function() { input && input.trigger('blur'); }); }, rest = function(){ if (!overlay.is(':hidden')) { overlay.elfinderoverlay('hide').off('click close', cancel); } pnode.removeClass('ui-front') .css('position', '') .off('unselect.'+fm.namespace, unselect); if (tarea) { nnode && nnode.css('max-height', ''); } else if (!tree) { pnode.css('width', '') .parent('td').css('overflow', ''); } }, colwidth, dfrd = jQuery.Deferred() .fail(function(error) { dstCls && dst.attr('class', dstCls); empty && wz.addClass('elfinder-cwd-wrapper-empty'); if (sel) { move && fm.trigger('unlockfiles', {files: sel}); fm.clipboard([]); fm.trigger('selectfiles', { files: sel }); } error && fm.error(error); }) .always(function() { rest(); cleanup(); fm.enable().unbind('open', openCallback).trigger('resMixinMake'); }), id = 'tmp_'+parseInt(Math.random()*100000), phash = this.data && this.data.target? this.data.target : (tree? fm.file(sel[0]).hash : fm.cwd().hash), date = new Date(), file = { hash : id, phash : phash, name : fm.uniqueName(this.prefix, phash), mime : this.mime, read : true, write : true, date : 'Today '+date.getHours()+':'+date.getMinutes(), move : move }, dum = fm.getUI(org).trigger('create.'+fm.namespace, file), data = this.data || {}, node = fm[find](id), nnode, pnode, overlay = fm.getUI('overlay'), cleanup = function() { if (node && node.length) { input.off(); node.hide(); fm.unselectfiles({files : [id]}).unbind('resize', resize); requestAnimationFrame(function() { if (tree) { node.closest('.elfinder-navbar-wrapper').remove(); } else { node.remove(); } }); } }, cancel = function(e) { if (!overlay.is(':hidden')) { pnode.css('z-index', ''); } if (! inError) { cleanup(); dfrd.reject(); if (e) { e.stopPropagation(); e.preventDefault(); } } }, input = jQuery(tarea? '<textarea/>' : '<input type="text"/>') .on('keyup text', function(){ if (tarea) { this.style.height = '1px'; this.style.height = this.scrollHeight + 'px'; } else if (colwidth) { this.style.width = colwidth + 'px'; if (this.scrollWidth > colwidth) { this.style.width = this.scrollWidth + 10 + 'px'; } } }) .on('keydown', function(e) { e.stopImmediatePropagation(); if (e.keyCode == jQuery.ui.keyCode.ESCAPE) { dfrd.reject(); } else if (e.keyCode == jQuery.ui.keyCode.ENTER) { e.preventDefault(); input.trigger('blur'); } }) .on('mousedown click dblclick', function(e) { e.stopPropagation(); if (e.type === 'dblclick') { e.preventDefault(); } }) .on('blur', function() { var name = jQuery.trim(input.val()), parent = input.parent(), valid = true, cut; if (!overlay.is(':hidden')) { pnode.css('z-index', ''); } if (name === '') { return cancel(); } if (!inError && parent.length) { if (fm.options.validName && fm.options.validName.test) { try { valid = fm.options.validName.test(name); } catch(e) { valid = false; } } if (!name || name === '.' || name === '..' || !valid) { inError = true; fm.error(file.mime === 'directory'? 'errInvDirname' : 'errInvName', {modal: true, close: function(){setTimeout(select, 120);}}); return false; } if (fm.fileByName(name, phash)) { inError = true; fm.error(['errExists', name], {modal: true, close: function(){setTimeout(select, 120);}}); return false; } cut = (sel && move)? fm.exec('cut', sel) : null; jQuery.when(cut) .done(function() { var toast = {}, nextAct = {}; rest(); input.hide().before(jQuery('<span>').text(name)); fm.lockfiles({files : [id]}); fm.request({ data : Object.assign({cmd : req, name : name, target : phash}, data || {}), notify : {type : req, cnt : 1}, preventFail : true, syncOnFail : true, navigate : {toast : toast}, }) .fail(function(error) { fm.unlockfiles({files : [id]}); inError = true; input.show().prev().remove(); fm.error(error, { modal: true, close: function() { if (Array.isArray(error) && jQuery.inArray('errUploadMime', error) !== -1) { dfrd.notify('errUploadMime').reject(); } else { setTimeout(select, 120); } } }); }) .done(function(data) { if (data && data.added && data.added[0]) { var item = data.added[0], dirhash = item.hash, newItem = fm[find](dirhash), acts = { 'directory' : { cmd: 'open', msg: 'cmdopendir' }, 'text' : { cmd: 'edit', msg: 'cmdedit' }, 'default' : { cmd: 'open', msg: 'cmdopen' } }, tmpMimes; if (sel && move) { fm.one(req+'done', function() { fm.exec('paste', dirhash); }); } if (!move) { if (fm.mimeIsText(item.mime) && !fm.mimesCanMakeEmpty[item.mime] && fm.mimeTypes[item.mime]) { fm.trigger('canMakeEmptyFile', {mimes: [item.mime], unshift: true}); tmpMimes = {}; tmpMimes[item.mime] = fm.mimeTypes[item.mime]; fm.storage('mkfileTextMimes', Object.assign(tmpMimes, fm.storage('mkfileTextMimes') || {})); } Object.assign(nextAct, nextAction || acts[item.mime] || acts[item.mime.split('/')[0]] || acts[(fm.mimesCanMakeEmpty[item.mime] || jQuery.inArray(item.mime, fm.resources.mimes.text) !== -1) ? 'text' : 'none'] || acts['default']); Object.assign(toast, nextAct.cmd ? { incwd : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)]), action: nextAct}, inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)]), action: nextAct} } : { inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)])} }); } } dfrd.resolve(data); }); }) .fail(function() { dfrd.reject(); }); } }) .on('dragenter dragleave dragover drop', function(e) { // stop bubbling to prevent upload with native drop event e.stopPropagation(); }), select = function() { var name = fm.splitFileExtention(input.val())[0]; if (!inError && fm.UA.Mobile && !fm.UA.iOS) { // since iOS has a bug? (z-index not effect) so disable it overlay.on('click close', cancel).elfinderoverlay('show'); pnode.css('z-index', overlay.css('z-index') + 1); } inError = false; ! fm.enabled() && fm.enable(); input.trigger('focus').trigger('select'); input[0].setSelectionRange && input[0].setSelectionRange(0, name.length); }, resize = function() { node.trigger('scrolltoview', {blink : false}); }, openCallback = function() { dfrd && (dfrd.state() === 'pending') && dfrd.reject(); }, inError = false, nextAction, // for tree dst, dstCls, collapsed, expanded, arrow, subtree; if (!fm.isCommandEnabled(req, phash) || !node.length) { return dfrd.reject(); } if (jQuery.isPlainObject(self.nextAction)){ nextAction = Object.assign({}, self.nextAction); } if (tree) { dst = fm[find](phash); collapsed = fm.res('class', 'navcollapse'); expanded = fm.res('class', 'navexpand'); arrow = fm.res('class', 'navarrow'); subtree = fm.res('class', 'navsubtree'); node.closest('.'+subtree).show(); if (! dst.hasClass(collapsed)) { dstCls = dst.attr('class'); dst.addClass(collapsed+' '+expanded+' elfinder-subtree-loaded'); } if (dst.is('.'+collapsed+':not(.'+expanded+')')) { dst.children('.'+arrow).trigger('click').data('dfrd').done(function() { if (input.val() === file.name) { input.val(fm.uniqueName(self.prefix, phash)).trigger('select').trigger('focus'); } }); } nnode = node.contents().filter(function(){ return this.nodeType==3 && jQuery(this).parent().attr('id') === fm.navHash2Id(file.hash); }); pnode = nnode.parent(); nnode.replaceWith(input.val(file.name)); } else { empty && wz.removeClass('elfinder-cwd-wrapper-empty'); nnode = node.find('.elfinder-cwd-filename'); pnode = nnode.parent(); if (tarea) { nnode.css('max-height', 'none'); } else { colwidth = pnode.width(); pnode.width(colwidth - 15) .parent('td').css('overflow', 'visible'); } nnode.empty().append(input.val(file.name)); } pnode.addClass('ui-front') .css('position', 'relative') .on('unselect.'+fm.namespace, unselect); fm.bind('resize', resize).one('open', openCallback); input.trigger('keyup'); select(); return dfrd; } }, blink: function(elm, mode) { var acts = { slowonce : function(){elm.hide().delay(250).fadeIn(750).delay(500).fadeOut(3500);}, lookme : function(){elm.show().fadeOut(500).fadeIn(750);} }, func; mode = mode || 'slowonce'; func = acts[mode] || acts['lookme']; elm.stop(true, true); func(); } }; /* * File: /js/jquery.dialogelfinder.js */ /** * @class dialogelfinder - open elFinder in dialog window * * @param Object elFinder options with dialog options * @example * jQuery(selector).dialogelfinder({ * // some elfinder options * title : 'My files', // dialog title, default = "Files" * width : 850, // dialog width, default 840 * autoOpen : false, // if false - dialog will not be opened after init, default = true * destroyOnClose : true // destroy elFinder on close dialog, default = false * }) * @author Dmitry (dio) Levashov **/ jQuery.fn.dialogelfinder = function(opts) { var position = 'elfinderPosition', destroy = 'elfinderDestroyOnClose', node; this.not('.elfinder').each(function() { var doc = jQuery(document), toolbar = jQuery('<div class="ui-widget-header dialogelfinder-drag ui-corner-top">'+(opts.title || 'Files')+'</div>'), button = jQuery('<a href="#" class="dialogelfinder-drag-close ui-corner-all"><span class="ui-icon ui-icon-closethick"> </span></a>') .appendTo(toolbar) .on('click', function(e) { e.preventDefault(); node.dialogelfinder('close'); }), node = jQuery(this).addClass('dialogelfinder') .css('position', 'absolute') .hide() .appendTo('body') .draggable({ handle : '.dialogelfinder-drag', containment : 'window', stop : function() { node.trigger('resize'); elfinder.trigger('resize'); } }) .elfinder(opts) .prepend(toolbar), elfinder = node.elfinder('instance'); node.width(parseInt(node.width()) || 840) // fix width if set to "auto" .data(destroy, !!opts.destroyOnClose) .find('.elfinder-toolbar').removeClass('ui-corner-top'); opts.position && node.data(position, opts.position); opts.autoOpen !== false && jQuery(this).dialogelfinder('open'); }); if (opts == 'open') { var node = jQuery(this), pos = node.data(position) || { top : parseInt(jQuery(document).scrollTop() + (jQuery(window).height() < node.height() ? 2 : (jQuery(window).height() - node.height())/2)), left : parseInt(jQuery(document).scrollLeft() + (jQuery(window).width() < node.width() ? 2 : (jQuery(window).width() - node.width())/2)) }; if (node.is(':hidden')) { node.addClass('ui-front').css(pos).show().trigger('resize'); setTimeout(function() { // fix resize icon position and make elfinder active node.trigger('resize').trigger('mousedown'); }, 200); } } else if (opts == 'close') { node = jQuery(this).removeClass('ui-front'); if (node.is(':visible')) { !!node.data(destroy) ? node.elfinder('destroy').remove() : node.elfinder('close'); } } else if (opts == 'instance') { return jQuery(this).getElFinder(); } return this; }; /* * File: /js/i18n/elfinder.en.js */ /** * English translation * @author Troex Nevelin <troex@fury.scancode.ru> * @author Naoki Sawada <hypweb+elfinder@gmail.com> * @version 2018-12-09 */ // elfinder.en.js is integrated into elfinder.(full|min).js by jake build if (typeof elFinder === 'function' && elFinder.prototype.i18) { elFinder.prototype.i18.en = { translator : 'Troex Nevelin <troex@fury.scancode.ru>, Naoki Sawada <hypweb+elfinder@gmail.com>', language : 'English', direction : 'ltr', dateFormat : 'M d, Y h:i A', // will show like: Aug 24, 2018 04:39 PM fancyDateFormat : '$1 h:i A', // will show like: Today 04:39 PM nonameDateFormat : 'ymd-His', // noname upload will show like: 180824-163916 messages : { /********************************** errors **********************************/ 'error' : 'Error', 'errUnknown' : 'Unknown error.', 'errUnknownCmd' : 'Unknown command.', 'errJqui' : 'Invalid jQuery UI configuration. Selectable, draggable and droppable components must be included.', 'errNode' : 'elFinder requires DOM Element to be created.', 'errURL' : 'Invalid elFinder configuration! URL option is not set.', 'errAccess' : 'Access denied.', 'errConnect' : 'Unable to connect to backend.', 'errAbort' : 'Connection aborted.', 'errTimeout' : 'Connection timeout.', 'errNotFound' : 'Backend not found.', 'errResponse' : 'Invalid backend response.', 'errConf' : 'Invalid backend configuration.', 'errJSON' : 'PHP JSON module not installed.', 'errNoVolumes' : 'Readable volumes not available.', 'errCmdParams' : 'Invalid parameters for command "$1".', 'errDataNotJSON' : 'Data is not JSON.', 'errDataEmpty' : 'Data is empty.', 'errCmdReq' : 'Backend request requires command name.', 'errOpen' : 'Unable to open "$1".', 'errNotFolder' : 'Object is not a folder.', 'errNotFile' : 'Object is not a file.', 'errRead' : 'Unable to read "$1".', 'errWrite' : 'Unable to write into "$1".', 'errPerm' : 'Permission denied.', 'errLocked' : '"$1" is locked and can not be renamed, moved or removed.', 'errExists' : 'Item named "$1" already exists.', 'errInvName' : 'Invalid file name.', 'errInvDirname' : 'Invalid folder name.', // from v2.1.24 added 12.4.2017 'errFolderNotFound' : 'Folder not found.', 'errFileNotFound' : 'File not found.', 'errTrgFolderNotFound' : 'Target folder "$1" not found.', 'errPopup' : 'Browser prevented opening popup window. To open file enable it in browser options.', 'errMkdir' : 'Unable to create folder "$1".', 'errMkfile' : 'Unable to create file "$1".', 'errRename' : 'Unable to rename "$1".', 'errCopyFrom' : 'Copying files from volume "$1" not allowed.', 'errCopyTo' : 'Copying files to volume "$1" not allowed.', 'errMkOutLink' : 'Unable to create a link to outside the volume root.', // from v2.1 added 03.10.2015 'errUpload' : 'Upload error.', // old name - errUploadCommon 'errUploadFile' : 'Unable to upload "$1".', // old name - errUpload 'errUploadNoFiles' : 'No files found for upload.', 'errUploadTotalSize' : 'Data exceeds the maximum allowed size.', // old name - errMaxSize 'errUploadFileSize' : 'File exceeds maximum allowed size.', // old name - errFileMaxSize 'errUploadMime' : 'File type not allowed.', 'errUploadTransfer' : '"$1" transfer error.', 'errUploadTemp' : 'Unable to make temporary file for upload.', // from v2.1 added 26.09.2015 'errNotReplace' : 'Object "$1" already exists at this location and can not be replaced by object with another type.', // new 'errReplace' : 'Unable to replace "$1".', 'errSave' : 'Unable to save "$1".', 'errCopy' : 'Unable to copy "$1".', 'errMove' : 'Unable to move "$1".', 'errCopyInItself' : 'Unable to copy "$1" into itself.', 'errRm' : 'Unable to remove "$1".', 'errTrash' : 'Unable into trash.', // from v2.1.24 added 30.4.2017 'errRmSrc' : 'Unable remove source file(s).', 'errExtract' : 'Unable to extract files from "$1".', 'errArchive' : 'Unable to create archive.', 'errArcType' : 'Unsupported archive type.', 'errNoArchive' : 'File is not archive or has unsupported archive type.', 'errCmdNoSupport' : 'Backend does not support this command.', 'errReplByChild' : 'The folder "$1" can\'t be replaced by an item it contains.', 'errArcSymlinks' : 'For security reason denied to unpack archives contains symlinks or files with not allowed names.', // edited 24.06.2012 'errArcMaxSize' : 'Archive files exceeds maximum allowed size.', 'errResize' : 'Unable to resize "$1".', 'errResizeDegree' : 'Invalid rotate degree.', // added 7.3.2013 'errResizeRotate' : 'Unable to rotate image.', // added 7.3.2013 'errResizeSize' : 'Invalid image size.', // added 7.3.2013 'errResizeNoChange' : 'Image size not changed.', // added 7.3.2013 'errUsupportType' : 'Unsupported file type.', 'errNotUTF8Content' : 'File "$1" is not in UTF-8 and cannot be edited.', // added 9.11.2011 'errNetMount' : 'Unable to mount "$1".', // added 17.04.2012 'errNetMountNoDriver' : 'Unsupported protocol.', // added 17.04.2012 'errNetMountFailed' : 'Mount failed.', // added 17.04.2012 'errNetMountHostReq' : 'Host required.', // added 18.04.2012 'errSessionExpires' : 'Your session has expired due to inactivity.', 'errCreatingTempDir' : 'Unable to create temporary directory: "$1"', 'errFtpDownloadFile' : 'Unable to download file from FTP: "$1"', 'errFtpUploadFile' : 'Unable to upload file to FTP: "$1"', 'errFtpMkdir' : 'Unable to create remote directory on FTP: "$1"', 'errArchiveExec' : 'Error while archiving files: "$1"', 'errExtractExec' : 'Error while extracting files: "$1"', 'errNetUnMount' : 'Unable to unmount.', // from v2.1 added 30.04.2012 'errConvUTF8' : 'Not convertible to UTF-8', // from v2.1 added 08.04.2014 'errFolderUpload' : 'Try the modern browser, If you\'d like to upload the folder.', // from v2.1 added 26.6.2015 'errSearchTimeout' : 'Timed out while searching "$1". Search result is partial.', // from v2.1 added 12.1.2016 'errReauthRequire' : 'Re-authorization is required.', // from v2.1.10 added 24.3.2016 'errMaxTargets' : 'Max number of selectable items is $1.', // from v2.1.17 added 17.10.2016 'errRestore' : 'Unable to restore from the trash. Can\'t identify the restore destination.', // from v2.1.24 added 3.5.2017 'errEditorNotFound' : 'Editor not found to this file type.', // from v2.1.25 added 23.5.2017 'errServerError' : 'Error occurred on the server side.', // from v2.1.25 added 16.6.2017 'errEmpty' : 'Unable to empty folder "$1".', // from v2.1.25 added 22.6.2017 'moreErrors' : 'There are $1 more errors.', // from v2.1.44 added 9.12.2018 /******************************* commands names ********************************/ 'cmdarchive' : 'Create archive', 'cmdback' : 'Back', 'cmdcopy' : 'Copy', 'cmdcut' : 'Cut', 'cmddownload' : 'Download', 'cmdduplicate' : 'Duplicate', 'cmdedit' : 'Edit file', 'cmdextract' : 'Extract files from archive', 'cmdforward' : 'Forward', 'cmdgetfile' : 'Select files', 'cmdhelp' : 'About this software', 'cmdhome' : 'Root', 'cmdinfo' : 'Get info & Share', 'cmdmkdir' : 'New folder', 'cmdmkdirin' : 'Into New Folder', // from v2.1.7 added 19.2.2016 'cmdmkfile' : 'New file', 'cmdopen' : 'Open', 'cmdpaste' : 'Paste', 'cmdquicklook' : 'Preview', 'cmdreload' : 'Reload', 'cmdrename' : 'Rename', 'cmdrm' : 'Delete', 'cmdtrash' : 'Into trash', //from v2.1.24 added 29.4.2017 'cmdrestore' : 'Restore', //from v2.1.24 added 3.5.2017 'cmdsearch' : 'Find files', 'cmdup' : 'Go to parent folder', 'cmdupload' : 'Upload files', 'cmdview' : 'View', 'cmdresize' : 'Resize & Rotate', 'cmdsort' : 'Sort', 'cmdnetmount' : 'Mount network volume', // added 18.04.2012 'cmdnetunmount': 'Unmount', // from v2.1 added 30.04.2012 'cmdplaces' : 'To Places', // added 28.12.2014 'cmdchmod' : 'Change mode', // from v2.1 added 20.6.2015 'cmdopendir' : 'Open a folder', // from v2.1 added 13.1.2016 'cmdcolwidth' : 'Reset column width', // from v2.1.13 added 12.06.2016 'cmdfullscreen': 'Full Screen', // from v2.1.15 added 03.08.2016 'cmdmove' : 'Move', // from v2.1.15 added 21.08.2016 'cmdempty' : 'Empty the folder', // from v2.1.25 added 22.06.2017 'cmdundo' : 'Undo', // from v2.1.27 added 31.07.2017 'cmdredo' : 'Redo', // from v2.1.27 added 31.07.2017 'cmdpreference': 'Preferences', // from v2.1.27 added 03.08.2017 'cmdselectall' : 'Select all', // from v2.1.28 added 15.08.2017 'cmdselectnone': 'Select none', // from v2.1.28 added 15.08.2017 'cmdselectinvert': 'Invert selection', // from v2.1.28 added 15.08.2017 'cmdopennew' : 'Open in new window', // from v2.1.38 added 3.4.2018 'cmdhide' : 'Hide (Preference)', // from v2.1.41 added 24.7.2018 /*********************************** buttons ***********************************/ 'btnClose' : 'Close', 'btnSave' : 'Save', 'btnRm' : 'Remove', 'btnApply' : 'Apply', 'btnCancel' : 'Cancel', 'btnNo' : 'No', 'btnYes' : 'Yes', 'btnMount' : 'Mount', // added 18.04.2012 'btnApprove': 'Goto $1 & approve', // from v2.1 added 26.04.2012 'btnUnmount': 'Unmount', // from v2.1 added 30.04.2012 'btnConv' : 'Convert', // from v2.1 added 08.04.2014 'btnCwd' : 'Here', // from v2.1 added 22.5.2015 'btnVolume' : 'Volume', // from v2.1 added 22.5.2015 'btnAll' : 'All', // from v2.1 added 22.5.2015 'btnMime' : 'MIME Type', // from v2.1 added 22.5.2015 'btnFileName':'Filename', // from v2.1 added 22.5.2015 'btnSaveClose': 'Save & Close', // from v2.1 added 12.6.2015 'btnBackup' : 'Backup', // fromv2.1 added 28.11.2015 'btnRename' : 'Rename', // from v2.1.24 added 6.4.2017 'btnRenameAll' : 'Rename(All)', // from v2.1.24 added 6.4.2017 'btnPrevious' : 'Prev ($1/$2)', // from v2.1.24 added 11.5.2017 'btnNext' : 'Next ($1/$2)', // from v2.1.24 added 11.5.2017 'btnSaveAs' : 'Save As', // from v2.1.25 added 24.5.2017 /******************************** notifications ********************************/ 'ntfopen' : 'Open folder', 'ntffile' : 'Open file', 'ntfreload' : 'Reload folder content', 'ntfmkdir' : 'Creating folder', 'ntfmkfile' : 'Creating files', 'ntfrm' : 'Delete items', 'ntfcopy' : 'Copy items', 'ntfmove' : 'Move items', 'ntfprepare' : 'Checking existing items', 'ntfrename' : 'Rename files', 'ntfupload' : 'Uploading files', 'ntfdownload' : 'Downloading files', 'ntfsave' : 'Save files', 'ntfarchive' : 'Creating archive', 'ntfextract' : 'Extracting files from archive', 'ntfsearch' : 'Searching files', 'ntfresize' : 'Resizing images', 'ntfsmth' : 'Doing something', 'ntfloadimg' : 'Loading image', 'ntfnetmount' : 'Mounting network volume', // added 18.04.2012 'ntfnetunmount': 'Unmounting network volume', // from v2.1 added 30.04.2012 'ntfdim' : 'Acquiring image dimension', // added 20.05.2013 'ntfreaddir' : 'Reading folder infomation', // from v2.1 added 01.07.2013 'ntfurl' : 'Getting URL of link', // from v2.1 added 11.03.2014 'ntfchmod' : 'Changing file mode', // from v2.1 added 20.6.2015 'ntfpreupload': 'Verifying upload file name', // from v2.1 added 31.11.2015 'ntfzipdl' : 'Creating a file for download', // from v2.1.7 added 23.1.2016 'ntfparents' : 'Getting path infomation', // from v2.1.17 added 2.11.2016 'ntfchunkmerge': 'Processing the uploaded file', // from v2.1.17 added 2.11.2016 'ntftrash' : 'Doing throw in the trash', // from v2.1.24 added 2.5.2017 'ntfrestore' : 'Doing restore from the trash', // from v2.1.24 added 3.5.2017 'ntfchkdir' : 'Checking destination folder', // from v2.1.24 added 3.5.2017 'ntfundo' : 'Undoing previous operation', // from v2.1.27 added 31.07.2017 'ntfredo' : 'Redoing previous undone', // from v2.1.27 added 31.07.2017 'ntfchkcontent' : 'Checking contents', // from v2.1.41 added 3.8.2018 /*********************************** volumes *********************************/ 'volume_Trash' : 'Trash', //from v2.1.24 added 29.4.2017 /************************************ dates **********************************/ 'dateUnknown' : 'unknown', 'Today' : 'Today', 'Yesterday' : 'Yesterday', 'msJan' : 'Jan', 'msFeb' : 'Feb', 'msMar' : 'Mar', 'msApr' : 'Apr', 'msMay' : 'May', 'msJun' : 'Jun', 'msJul' : 'Jul', 'msAug' : 'Aug', 'msSep' : 'Sep', 'msOct' : 'Oct', 'msNov' : 'Nov', 'msDec' : 'Dec', 'January' : 'January', 'February' : 'February', 'March' : 'March', 'April' : 'April', 'May' : 'May', 'June' : 'June', 'July' : 'July', 'August' : 'August', 'September' : 'September', 'October' : 'October', 'November' : 'November', 'December' : 'December', 'Sunday' : 'Sunday', 'Monday' : 'Monday', 'Tuesday' : 'Tuesday', 'Wednesday' : 'Wednesday', 'Thursday' : 'Thursday', 'Friday' : 'Friday', 'Saturday' : 'Saturday', 'Sun' : 'Sun', 'Mon' : 'Mon', 'Tue' : 'Tue', 'Wed' : 'Wed', 'Thu' : 'Thu', 'Fri' : 'Fri', 'Sat' : 'Sat', /******************************** sort variants ********************************/ 'sortname' : 'by name', 'sortkind' : 'by kind', 'sortsize' : 'by size', 'sortdate' : 'by date', 'sortFoldersFirst' : 'Folders first', 'sortperm' : 'by permission', // from v2.1.13 added 13.06.2016 'sortmode' : 'by mode', // from v2.1.13 added 13.06.2016 'sortowner' : 'by owner', // from v2.1.13 added 13.06.2016 'sortgroup' : 'by group', // from v2.1.13 added 13.06.2016 'sortAlsoTreeview' : 'Also Treeview', // from v2.1.15 added 01.08.2016 /********************************** new items **********************************/ 'untitled file.txt' : 'NewFile.txt', // added 10.11.2015 'untitled folder' : 'NewFolder', // added 10.11.2015 'Archive' : 'NewArchive', // from v2.1 added 10.11.2015 'untitled file' : 'NewFile.$1', // from v2.1.41 added 6.8.2018 'extentionfile' : '$1: File', // from v2.1.41 added 6.8.2018 'extentiontype' : '$1: $2', // from v2.1.43 added 17.10.2018 /********************************** messages **********************************/ 'confirmReq' : 'Confirmation required', 'confirmRm' : 'Are you sure you want to permanently remove items?<br/>This cannot be undone!', 'confirmRepl' : 'Replace old file with new one? (If it contains folders, it will be merged. To backup and replace, select Backup.)', 'confirmRest' : 'Replace existing item with the item in trash?', // fromv2.1.24 added 5.5.2017 'confirmConvUTF8' : 'Not in UTF-8<br/>Convert to UTF-8?<br/>Contents become UTF-8 by saving after conversion.', // from v2.1 added 08.04.2014 'confirmNonUTF8' : 'Character encoding of this file couldn\'t be detected. It need to temporarily convert to UTF-8 for editting.<br/>Please select character encoding of this file.', // from v2.1.19 added 28.11.2016 'confirmNotSave' : 'It has been modified.<br/>Losing work if you do not save changes.', // from v2.1 added 15.7.2015 'confirmTrash' : 'Are you sure you want to move items to trash bin?', //from v2.1.24 added 29.4.2017 'apllyAll' : 'Apply to all', 'name' : 'Name', 'size' : 'Size', 'perms' : 'Permissions', 'modify' : 'Modified', 'kind' : 'Kind', 'read' : 'read', 'write' : 'write', 'noaccess' : 'no access', 'and' : 'and', 'unknown' : 'unknown', 'selectall' : 'Select all items', 'selectfiles' : 'Select item(s)', 'selectffile' : 'Select first item', 'selectlfile' : 'Select last item', 'viewlist' : 'List view', 'viewicons' : 'Icons view', 'viewSmall' : 'Small icons', // from v2.1.39 added 22.5.2018 'viewMedium' : 'Medium icons', // from v2.1.39 added 22.5.2018 'viewLarge' : 'Large icons', // from v2.1.39 added 22.5.2018 'viewExtraLarge' : 'Extra large icons', // from v2.1.39 added 22.5.2018 'places' : 'Places', 'calc' : 'Calculate', 'path' : 'Path', 'aliasfor' : 'Alias for', 'locked' : 'Locked', 'dim' : 'Dimensions', 'files' : 'Files', 'folders' : 'Folders', 'items' : 'Items', 'yes' : 'yes', 'no' : 'no', 'link' : 'Link', 'searcresult' : 'Search results', 'selected' : 'selected items', 'about' : 'About', 'shortcuts' : 'Shortcuts', 'help' : 'Help', 'webfm' : 'Web file manager', 'ver' : 'Version', 'protocolver' : 'protocol version', 'homepage' : 'Project home', 'docs' : 'Documentation', 'github' : 'Fork us on GitHub', 'twitter' : 'Follow us on Twitter', 'facebook' : 'Join us on Facebook', 'team' : 'Team', 'chiefdev' : 'chief developer', 'developer' : 'developer', 'contributor' : 'contributor', 'maintainer' : 'maintainer', 'translator' : 'translator', 'icons' : 'Icons', 'dontforget' : 'and don\'t forget to take your towel', 'shortcutsof' : 'Shortcuts disabled', 'dropFiles' : 'Drop files here', 'or' : 'or', 'selectForUpload' : 'Select files', 'moveFiles' : 'Move items', 'copyFiles' : 'Copy items', 'restoreFiles' : 'Restore items', // from v2.1.24 added 5.5.2017 'rmFromPlaces' : 'Remove from places', 'aspectRatio' : 'Aspect ratio', 'scale' : 'Scale', 'width' : 'Width', 'height' : 'Height', 'resize' : 'Resize', 'crop' : 'Crop', 'rotate' : 'Rotate', 'rotate-cw' : 'Rotate 90 degrees CW', 'rotate-ccw' : 'Rotate 90 degrees CCW', 'degree' : '°', 'netMountDialogTitle' : 'Mount network volume', // added 18.04.2012 'protocol' : 'Protocol', // added 18.04.2012 'host' : 'Host', // added 18.04.2012 'port' : 'Port', // added 18.04.2012 'user' : 'User', // added 18.04.2012 'pass' : 'Password', // added 18.04.2012 'confirmUnmount' : 'Are you sure to unmount $1?', // from v2.1 added 30.04.2012 'dropFilesBrowser': 'Drop or Paste files from browser', // from v2.1 added 30.05.2012 'dropPasteFiles' : 'Drop files, Paste URLs or images(clipboard) here', // from v2.1 added 07.04.2014 'encoding' : 'Encoding', // from v2.1 added 19.12.2014 'locale' : 'Locale', // from v2.1 added 19.12.2014 'searchTarget' : 'Target: $1', // from v2.1 added 22.5.2015 'searchMime' : 'Search by input MIME Type', // from v2.1 added 22.5.2015 'owner' : 'Owner', // from v2.1 added 20.6.2015 'group' : 'Group', // from v2.1 added 20.6.2015 'other' : 'Other', // from v2.1 added 20.6.2015 'execute' : 'Execute', // from v2.1 added 20.6.2015 'perm' : 'Permission', // from v2.1 added 20.6.2015 'mode' : 'Mode', // from v2.1 added 20.6.2015 'emptyFolder' : 'Folder is empty', // from v2.1.6 added 30.12.2015 'emptyFolderDrop' : 'Folder is empty\\A Drop to add items', // from v2.1.6 added 30.12.2015 'emptyFolderLTap' : 'Folder is empty\\A Long tap to add items', // from v2.1.6 added 30.12.2015 'quality' : 'Quality', // from v2.1.6 added 5.1.2016 'autoSync' : 'Auto sync', // from v2.1.6 added 10.1.2016 'moveUp' : 'Move up', // from v2.1.6 added 18.1.2016 'getLink' : 'Get URL link', // from v2.1.7 added 9.2.2016 'selectedItems' : 'Selected items ($1)', // from v2.1.7 added 2.19.2016 'folderId' : 'Folder ID', // from v2.1.10 added 3.25.2016 'offlineAccess' : 'Allow offline access', // from v2.1.10 added 3.25.2016 'reAuth' : 'To re-authenticate', // from v2.1.10 added 3.25.2016 'nowLoading' : 'Now loading...', // from v2.1.12 added 4.26.2016 'openMulti' : 'Open multiple files', // from v2.1.12 added 5.14.2016 'openMultiConfirm': 'You are trying to open the $1 files. Are you sure you want to open in browser?', // from v2.1.12 added 5.14.2016 'emptySearch' : 'Search results is empty in search target.', // from v2.1.12 added 5.16.2016 'editingFile' : 'It is editing a file.', // from v2.1.13 added 6.3.2016 'hasSelected' : 'You have selected $1 items.', // from v2.1.13 added 6.3.2016 'hasClipboard' : 'You have $1 items in the clipboard.', // from v2.1.13 added 6.3.2016 'incSearchOnly' : 'Incremental search is only from the current view.', // from v2.1.13 added 6.30.2016 'reinstate' : 'Reinstate', // from v2.1.15 added 3.8.2016 'complete' : '$1 complete', // from v2.1.15 added 21.8.2016 'contextmenu' : 'Context menu', // from v2.1.15 added 9.9.2016 'pageTurning' : 'Page turning', // from v2.1.15 added 10.9.2016 'volumeRoots' : 'Volume roots', // from v2.1.16 added 16.9.2016 'reset' : 'Reset', // from v2.1.16 added 1.10.2016 'bgcolor' : 'Background color', // from v2.1.16 added 1.10.2016 'colorPicker' : 'Color picker', // from v2.1.16 added 1.10.2016 '8pxgrid' : '8px Grid', // from v2.1.16 added 4.10.2016 'enabled' : 'Enabled', // from v2.1.16 added 4.10.2016 'disabled' : 'Disabled', // from v2.1.16 added 4.10.2016 'emptyIncSearch' : 'Search results is empty in current view.\\A Press [Enter] to expand search target.', // from v2.1.16 added 5.10.2016 'emptyLetSearch' : 'First letter search results is empty in current view.', // from v2.1.23 added 24.3.2017 'textLabel' : 'Text label', // from v2.1.17 added 13.10.2016 'minsLeft' : '$1 mins left', // from v2.1.17 added 13.11.2016 'openAsEncoding' : 'Reopen with selected encoding', // from v2.1.19 added 2.12.2016 'saveAsEncoding' : 'Save with the selected encoding', // from v2.1.19 added 2.12.2016 'selectFolder' : 'Select folder', // from v2.1.20 added 13.12.2016 'firstLetterSearch': 'First letter search', // from v2.1.23 added 24.3.2017 'presets' : 'Presets', // from v2.1.25 added 26.5.2017 'tooManyToTrash' : 'It\'s too many items so it can\'t into trash.', // from v2.1.25 added 9.6.2017 'TextArea' : 'TextArea', // from v2.1.25 added 14.6.2017 'folderToEmpty' : 'Empty the folder "$1".', // from v2.1.25 added 22.6.2017 'filderIsEmpty' : 'There are no items in a folder "$1".', // from v2.1.25 added 22.6.2017 'preference' : 'Preference', // from v2.1.26 added 28.6.2017 'language' : 'Language', // from v2.1.26 added 28.6.2017 'clearBrowserData': 'Initialize the settings saved in this browser', // from v2.1.26 added 28.6.2017 'toolbarPref' : 'Toolbar settings', // from v2.1.27 added 2.8.2017 'charsLeft' : '... $1 chars left.', // from v2.1.29 added 30.8.2017 'sum' : 'Sum', // from v2.1.29 added 28.9.2017 'roughFileSize' : 'Rough file size', // from v2.1.30 added 2.11.2017 'autoFocusDialog' : 'Focus on the element of dialog with mouseover', // from v2.1.30 added 2.11.2017 'select' : 'Select', // from v2.1.30 added 23.11.2017 'selectAction' : 'Action when select file', // from v2.1.30 added 23.11.2017 'useStoredEditor' : 'Open with the editor used last time', // from v2.1.30 added 23.11.2017 'selectinvert' : 'Invert selection', // from v2.1.30 added 25.11.2017 'renameMultiple' : 'Are you sure you want to rename $1 selected items like $2?<br/>This cannot be undone!', // from v2.1.31 added 4.12.2017 'batchRename' : 'Batch rename', // from v2.1.31 added 8.12.2017 'plusNumber' : '+ Number', // from v2.1.31 added 8.12.2017 'asPrefix' : 'Add prefix', // from v2.1.31 added 8.12.2017 'asSuffix' : 'Add suffix', // from v2.1.31 added 8.12.2017 'changeExtention' : 'Change extention', // from v2.1.31 added 8.12.2017 'columnPref' : 'Columns settings (List view)', // from v2.1.32 added 6.2.2018 'reflectOnImmediate' : 'All changes will reflect immediately to the archive.', // from v2.1.33 added 2.3.2018 'reflectOnUnmount' : 'Any changes will not reflect until un-mount this volume.', // from v2.1.33 added 2.3.2018 'unmountChildren' : 'The following volume(s) mounted on this volume also unmounted. Are you sure to unmount it?', // from v2.1.33 added 5.3.2018 'selectionInfo' : 'Selection Info', // from v2.1.33 added 7.3.2018 'hashChecker' : 'Algorithms to show the file hash', // from v2.1.33 added 10.3.2018 'infoItems' : 'Info Items (Selection Info Panel)', // from v2.1.38 added 28.3.2018 'pressAgainToExit': 'Press again to exit.', // from v2.1.38 added 1.4.2018 'toolbar' : 'Toolbar', // from v2.1.38 added 4.4.2018 'workspace' : 'Work Space', // from v2.1.38 added 4.4.2018 'dialog' : 'Dialog', // from v2.1.38 added 4.4.2018 'all' : 'All', // from v2.1.38 added 4.4.2018 'iconSize' : 'Icon Size (Icons view)', // from v2.1.39 added 7.5.2018 'editorMaximized' : 'Open the maximized editor window', // from v2.1.40 added 30.6.2018 'editorConvNoApi' : 'Because conversion by API is not currently available, please convert on the website.', //from v2.1.40 added 8.7.2018 'editorConvNeedUpload' : 'After conversion, you must be upload with the item URL or a downloaded file to save the converted file.', //from v2.1.40 added 8.7.2018 'convertOn' : 'Convert on the site of $1', // from v2.1.40 added 10.7.2018 'integrations' : 'Integrations', // from v2.1.40 added 11.7.2018 'integrationWith' : 'This elFinder has the following external services integrated. Please check the terms of use, privacy policy, etc. before using it.', // from v2.1.40 added 11.7.2018 'showHidden' : 'Show hidden items', // from v2.1.41 added 24.7.2018 'hideHidden' : 'Hide hidden items', // from v2.1.41 added 24.7.2018 'toggleHidden' : 'Show/Hide hidden items', // from v2.1.41 added 24.7.2018 'makefileTypes' : 'File types to enable with "New file"', // from v2.1.41 added 7.8.2018 'typeOfTextfile' : 'Type of the Text file', // from v2.1.41 added 7.8.2018 'add' : 'Add', // from v2.1.41 added 7.8.2018 'theme' : 'Theme', // from v2.1.43 added 19.10.2018 'default' : 'Default', // from v2.1.43 added 19.10.2018 'description' : 'Description', // from v2.1.43 added 19.10.2018 'website' : 'Website', // from v2.1.43 added 19.10.2018 'author' : 'Author', // from v2.1.43 added 19.10.2018 'email' : 'Email', // from v2.1.43 added 19.10.2018 'license' : 'License', // from v2.1.43 added 19.10.2018 'exportToSave' : 'This item can\'t be saved. To avoid losing the edits you need to export to your PC.', // from v2.1.44 added 1.12.2018 /********************************** mimetypes **********************************/ 'kindUnknown' : 'Unknown', 'kindRoot' : 'Volume Root', // from v2.1.16 added 16.10.2016 'kindFolder' : 'Folder', 'kindSelects' : 'Selections', // from v2.1.29 added 29.8.2017 'kindAlias' : 'Alias', 'kindAliasBroken' : 'Broken alias', // applications 'kindApp' : 'Application', 'kindPostscript' : 'Postscript document', 'kindMsOffice' : 'Microsoft Office document', 'kindMsWord' : 'Microsoft Word document', 'kindMsExcel' : 'Microsoft Excel document', 'kindMsPP' : 'Microsoft Powerpoint presentation', 'kindOO' : 'Open Office document', 'kindAppFlash' : 'Flash application', 'kindPDF' : 'Portable Document Format (PDF)', 'kindTorrent' : 'Bittorrent file', 'kind7z' : '7z archive', 'kindTAR' : 'TAR archive', 'kindGZIP' : 'GZIP archive', 'kindBZIP' : 'BZIP archive', 'kindXZ' : 'XZ archive', 'kindZIP' : 'ZIP archive', 'kindRAR' : 'RAR archive', 'kindJAR' : 'Java JAR file', 'kindTTF' : 'True Type font', 'kindOTF' : 'Open Type font', 'kindRPM' : 'RPM package', // texts 'kindText' : 'Text document', 'kindTextPlain' : 'Plain text', 'kindPHP' : 'PHP source', 'kindCSS' : 'Cascading style sheet', 'kindHTML' : 'HTML document', 'kindJS' : 'Javascript source', 'kindRTF' : 'Rich Text Format', 'kindC' : 'C source', 'kindCHeader' : 'C header source', 'kindCPP' : 'C++ source', 'kindCPPHeader' : 'C++ header source', 'kindShell' : 'Unix shell script', 'kindPython' : 'Python source', 'kindJava' : 'Java source', 'kindRuby' : 'Ruby source', 'kindPerl' : 'Perl script', 'kindSQL' : 'SQL source', 'kindXML' : 'XML document', 'kindAWK' : 'AWK source', 'kindCSV' : 'Comma separated values', 'kindDOCBOOK' : 'Docbook XML document', 'kindMarkdown' : 'Markdown text', // added 20.7.2015 // images 'kindImage' : 'Image', 'kindBMP' : 'BMP image', 'kindJPEG' : 'JPEG image', 'kindGIF' : 'GIF Image', 'kindPNG' : 'PNG Image', 'kindTIFF' : 'TIFF image', 'kindTGA' : 'TGA image', 'kindPSD' : 'Adobe Photoshop image', 'kindXBITMAP' : 'X bitmap image', 'kindPXM' : 'Pixelmator image', // media 'kindAudio' : 'Audio media', 'kindAudioMPEG' : 'MPEG audio', 'kindAudioMPEG4' : 'MPEG-4 audio', 'kindAudioMIDI' : 'MIDI audio', 'kindAudioOGG' : 'Ogg Vorbis audio', 'kindAudioWAV' : 'WAV audio', 'AudioPlaylist' : 'MP3 playlist', 'kindVideo' : 'Video media', 'kindVideoDV' : 'DV movie', 'kindVideoMPEG' : 'MPEG movie', 'kindVideoMPEG4' : 'MPEG-4 movie', 'kindVideoAVI' : 'AVI movie', 'kindVideoMOV' : 'Quick Time movie', 'kindVideoWM' : 'Windows Media movie', 'kindVideoFlash' : 'Flash movie', 'kindVideoMKV' : 'Matroska movie', 'kindVideoOGG' : 'Ogg movie' } }; } /* * File: /js/ui/button.js */ /** * @class elFinder toolbar button widget. * If command has variants - create menu * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfinderbutton = function(cmd) { return this.each(function() { var c = 'class', fm = cmd.fm, disabled = fm.res(c, 'disabled'), active = fm.res(c, 'active'), hover = fm.res(c, 'hover'), item = 'elfinder-button-menu-item', selected = 'elfinder-button-menu-item-selected', menu, text = jQuery('<span class="elfinder-button-text">'+cmd.title+'</span>'), prvCname = 'elfinder-button-icon-' + (cmd.className? cmd.className : cmd.name), button = jQuery(this).addClass('ui-state-default elfinder-button') .attr('title', cmd.title) .append('<span class="elfinder-button-icon ' + prvCname + '"/>', text) .on('mouseenter mouseleave', function(e) { !button.hasClass(disabled) && button[e.type == 'mouseleave' ? 'removeClass' : 'addClass'](hover);}) .on('click', function(e) { if (!button.hasClass(disabled)) { if (menu && cmd.variants.length >= 1) { // close other menus menu.is(':hidden') && fm.getUI().click(); e.stopPropagation(); menu.css(getMenuOffset()).slideToggle({ duration: 100, done: function(e) { fm[menu.is(':visible')? 'toFront' : 'toHide'](menu); } }); } else { fm.exec(cmd.name, getSelected(), {_userAction: true, _currentType: 'toolbar', _currentNode: button }); } } }), hideMenu = function() { fm.toHide(menu); }, getMenuOffset = function() { var fmNode = fm.getUI(), baseOffset = fmNode.offset(), buttonOffset = button.offset(); return { top : buttonOffset.top - baseOffset.top, left : buttonOffset.left - baseOffset.left, maxHeight : fmNode.height() - 40 }; }, getSelected = function() { var sel = fm.selected(), cwd; if (!sel.length) { if (cwd = fm.cwd()) { sel = [ fm.cwd().hash ]; } else { sel = void(0); } } return sel; }, tm; text.hide(); // set self button object to cmd object cmd.button = button; // if command has variants create menu if (Array.isArray(cmd.variants)) { button.addClass('elfinder-menubutton'); menu = jQuery('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>') .hide() .appendTo(fm.getUI()) .on('mouseenter mouseleave', '.'+item, function() { jQuery(this).toggleClass(hover); }) .on('click', '.'+item, function(e) { var opts = jQuery(this).data('value'); e.preventDefault(); e.stopPropagation(); button.removeClass(hover); fm.toHide(menu); if (typeof opts === 'undefined') { opts = {}; } if (typeof opts === 'object') { opts._userAction = true; } fm.exec(cmd.name, getSelected(), opts); }) .on('close', hideMenu); fm.bind('disable select', hideMenu).getUI().on('click', hideMenu); cmd.change(function() { menu.html(''); jQuery.each(cmd.variants, function(i, variant) { menu.append(jQuery('<div class="'+item+'">'+variant[1]+'</div>').data('value', variant[0]).addClass(variant[0] == cmd.value ? selected : '')); }); }); } cmd.change(function() { var cName; tm && cancelAnimationFrame(tm); tm = requestAnimationFrame(function() { if (cmd.disabled()) { button.removeClass(active+' '+hover).addClass(disabled); } else { button.removeClass(disabled); button[cmd.active() ? 'addClass' : 'removeClass'](active); } if (cmd.syncTitleOnChange) { cName = 'elfinder-button-icon-' + (cmd.className? cmd.className : cmd.name); if (prvCname !== cName) { button.children('.elfinder-button-icon').removeClass(prvCname).addClass(cName); prvCname = cName; } text.html(cmd.title); button.attr('title', cmd.title); } }); }) .change(); }); }; /* * File: /js/ui/contextmenu.js */ /** * @class elFinder contextmenu * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfindercontextmenu = function(fm) { return this.each(function() { var self = jQuery(this), cmItem = 'elfinder-contextmenu-item', smItem = 'elfinder-contextsubmenu-item', exIcon = 'elfinder-contextmenu-extra-icon', cHover = fm.res('class', 'hover'), dragOpt = { distance: 8, start: function() { menu.data('drag', true).data('touching') && menu.find('.'+cHover).removeClass(cHover); }, stop: function() { menu.data('draged', true).removeData('drag'); } }, menu = jQuery(this).addClass('touch-punch ui-helper-reset ui-front ui-widget ui-state-default ui-corner-all elfinder-contextmenu elfinder-contextmenu-'+fm.direction) .hide() .on('touchstart', function(e) { menu.data('touching', true).children().removeClass(cHover); }) .on('touchend', function(e) { menu.removeData('touching'); }) .on('mouseenter mouseleave', '.'+cmItem, function(e) { jQuery(this).toggleClass(cHover, (e.type === 'mouseenter' || (! menu.data('draged') && menu.data('submenuKeep'))? true : false)); if (menu.data('draged') && menu.data('submenuKeep')) { menu.find('.elfinder-contextmenu-sub:visible').parent().addClass(cHover); } }) .on('mouseenter mouseleave', '.'+exIcon, function(e) { jQuery(this).parent().toggleClass(cHover, e.type === 'mouseleave'); }) .on('mouseenter mouseleave', '.'+cmItem+',.'+smItem, function(e) { var setIndex = function(target, sub) { jQuery.each(sub? subnodes : nodes, function(i, n) { if (target[0] === n) { (sub? subnodes : nodes)._cur = i; if (sub) { subselected = target; } else { selected = target; } return false; } }); }; if (e.originalEvent) { var target = jQuery(this), unHover = function() { if (selected && !selected.children('div.elfinder-contextmenu-sub:visible').length) { selected.removeClass(cHover); } }; if (e.type === 'mouseenter') { // mouseenter if (target.hasClass(smItem)) { // submenu if (subselected) { subselected.removeClass(cHover); } if (selected) { subnodes = selected.find('div.'+smItem); } setIndex(target, true); } else { // menu unHover(); setIndex(target); } } else { // mouseleave if (target.hasClass(smItem)) { //submenu subselected = null; subnodes = null; } else { // menu unHover(); (function(sel) { setTimeout(function() { if (sel === selected) { selected = null; } }, 250); })(selected); } } } }) .on('contextmenu', function(){return false;}) .on('mouseup', function() { setTimeout(function() { menu.removeData('draged'); }, 100); }) .draggable(dragOpt), ltr = fm.direction === 'ltr', subpos = ltr? 'left' : 'right', types = Object.assign({}, fm.options.contextmenu), tpl = '<div class="'+cmItem+'{className}"><span class="elfinder-button-icon {icon} elfinder-contextmenu-icon"{style}/><span>{label}</span></div>', item = function(label, icon, callback, opts) { var className = '', style = '', iconClass = '', v, pos; if (opts) { if (opts.className) { className = ' ' + opts.className; } if (opts.iconClass) { iconClass = opts.iconClass; icon = ''; } if (opts.iconImg) { v = opts.iconImg.split(/ +/); pos = v[1] && v[2]? fm.escape(v[1] + 'px ' + v[2] + 'px') : ''; style = ' style="background:url(\''+fm.escape(v[0])+'\') '+(pos? pos : '0 0')+' no-repeat;'+(pos? '' : 'posbackground-size:contain;')+'"'; } } return jQuery(tpl.replace('{icon}', icon ? 'elfinder-button-icon-'+icon : (iconClass? iconClass : '')) .replace('{label}', label) .replace('{style}', style) .replace('{className}', className)) .on('click', function(e) { e.stopPropagation(); e.preventDefault(); callback(); }); }, urlIcon = function(iconUrl) { var v = iconUrl.split(/ +/), pos = v[1] && v[2]? (v[1] + 'px ' + v[2] + 'px') : ''; return { backgroundImage: 'url("'+v[0]+'")', backgroundRepeat: 'no-repeat', backgroundPosition: pos? pos : '', backgroundSize: pos? '' : 'contain' }; }, base, cwd, nodes, selected, subnodes, subselected, autoSyncStop, subHoverTm, autoToggle = function() { var evTouchStart = 'touchstart.contextmenuAutoToggle'; menu.data('hideTm') && clearTimeout(menu.data('hideTm')); if (menu.is(':visible')) { menu.on('touchstart', function(e) { if (e.originalEvent.touches.length > 1) { return; } menu.stop(); fm.toFront(menu); menu.data('hideTm') && clearTimeout(menu.data('hideTm')); }) .data('hideTm', setTimeout(function() { if (menu.is(':visible')) { cwd.find('.elfinder-cwd-file').off(evTouchStart); cwd.find('.elfinder-cwd-file.ui-selected') .one(evTouchStart, function(e) { if (e.originalEvent.touches.length > 1) { return; } var tgt = jQuery(e.target); if (menu.first().length && !tgt.is('input:checkbox') && !tgt.hasClass('elfinder-cwd-select')) { e.stopPropagation(); //e.preventDefault(); open(e.originalEvent.touches[0].pageX, e.originalEvent.touches[0].pageY); cwd.data('longtap', true) tgt.one('touchend', function() { setTimeout(function() { cwd.removeData('longtap'); }, 80); }); return; } cwd.find('.elfinder-cwd-file').off(evTouchStart); }) .one('unselect.'+fm.namespace, function() { cwd.find('.elfinder-cwd-file').off(evTouchStart); }); menu.fadeOut({ duration: 300, fail: function() { menu.css('opacity', '1').show(); }, done: function() { fm.toHide(menu); } }); } }, 4500)); } }, keyEvts = function(e) { var code = e.keyCode, ESC = jQuery.ui.keyCode.ESCAPE, ENT = jQuery.ui.keyCode.ENTER, LEFT = jQuery.ui.keyCode.LEFT, RIGHT = jQuery.ui.keyCode.RIGHT, UP = jQuery.ui.keyCode.UP, DOWN = jQuery.ui.keyCode.DOWN, subent = fm.direction === 'ltr'? RIGHT : LEFT, sublev = subent === RIGHT? LEFT : RIGHT; if (jQuery.inArray(code, [ESC, ENT, LEFT, RIGHT, UP, DOWN]) !== -1) { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); if (code == ESC || code === sublev) { if (selected && subnodes && subselected) { subselected.trigger('mouseleave').trigger('submenuclose'); selected.addClass(cHover); subnodes = null; subselected = null; } else { code == ESC && close(); } } else if (code == UP || code == DOWN) { if (subnodes) { if (subselected) { subselected.trigger('mouseleave'); } if (code == DOWN && (! subselected || subnodes.length <= ++subnodes._cur)) { subnodes._cur = 0; } else if (code == UP && (! subselected || --subnodes._cur < 0)) { subnodes._cur = subnodes.length - 1; } subselected = subnodes.eq(subnodes._cur).trigger('mouseenter'); } else { subnodes = null; if (selected) { selected.trigger('mouseleave'); } if (code == DOWN && (! selected || nodes.length <= ++nodes._cur)) { nodes._cur = 0; } else if (code == UP && (! selected || --nodes._cur < 0)) { nodes._cur = nodes.length - 1; } selected = nodes.eq(nodes._cur).addClass(cHover); } } else if (selected && (code == ENT || code === subent)) { if (selected.hasClass('elfinder-contextmenu-group')) { if (subselected) { code == ENT && subselected.click(); } else { selected.trigger('mouseenter'); subnodes = selected.find('div.'+smItem); subnodes._cur = 0; subselected = subnodes.first().addClass(cHover); } } else { code == ENT && selected.click(); } } } }, open = function(x, y, css) { var width = menu.outerWidth(), height = menu.outerHeight(), bstyle = base.attr('style'), bpos = base.offset(), bwidth = base.width(), bheight = base.height(), mw = fm.UA.Mobile? 40 : 2, mh = fm.UA.Mobile? 20 : 2, x = x - (bpos? bpos.left : 0), y = y - (bpos? bpos.top : 0), css = Object.assign(css || {}, { top : Math.max(0, y + mh + height < bheight ? y + mh : y - (y + height - bheight)), left : Math.max(0, (x < width + mw || x + mw + width < bwidth)? x + mw : x - mw - width), opacity : '1' }), evts; autoSyncStop = true; fm.autoSync('stop'); base.width(bwidth); menu.stop().removeAttr('style').css(css); fm.toFront(menu); menu.show(); base.attr('style', bstyle); css[subpos] = parseInt(menu.width()); menu.find('.elfinder-contextmenu-sub').css(css); if (fm.UA.iOS) { jQuery('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'auto'); } selected = null; subnodes = null; subselected = null; jQuery(document).on('keydown.' + fm.namespace, keyEvts); evts = jQuery._data(document).events; if (evts && evts.keydown) { evts.keydown.unshift(evts.keydown.pop()); } fm.UA.Mobile && autoToggle(); requestAnimationFrame(function() { fm.getUI().one('click.' + fm.namespace, close); }); }, close = function() { fm.getUI().off('click.' + fm.namespace, close); jQuery(document).off('keydown.' + fm.namespace, keyEvts); currentType = currentTargets = null; if (menu.is(':visible') || menu.children().length) { fm.toHide(menu.removeAttr('style').empty().removeData('submenuKeep')); try { if (! menu.draggable('instance')) { menu.draggable(dragOpt); } } catch(e) { if (! menu.hasClass('ui-draggable')) { menu.draggable(dragOpt); } } if (menu.data('prevNode')) { menu.data('prevNode').after(menu); menu.removeData('prevNode'); } fm.trigger('closecontextmenu'); if (fm.UA.iOS) { jQuery('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'touch'); } } autoSyncStop && fm.searchStatus.state < 1 && ! fm.searchStatus.ininc && fm.autoSync(); autoSyncStop = false; }, create = function(type, targets) { var sep = false, insSep = false, disabled = [], isCwd = type === 'cwd', selcnt = 0, cmdMap; currentType = type; currentTargets = targets; // get current uiCmdMap option if (!(cmdMap = fm.option('uiCmdMap', isCwd? void(0) : targets[0]))) { cmdMap = {}; } if (!isCwd) { disabled = fm.getDisabledCmds(targets); } selcnt = fm.selected().length; if (selcnt > 1) { menu.append('<div class="ui-corner-top ui-widget-header elfinder-contextmenu-header"><span>' + fm.i18n('selectedItems', ''+selcnt) + '</span></div>'); } nodes = jQuery(); jQuery.each(types[type]||[], function(i, name) { var cmd, cmdName, useMap, node, submenu, hover; if (name === '|') { if (sep) { insSep = true; } return; } if (cmdMap[name]) { cmdName = cmdMap[name]; useMap = true; } else { cmdName = name; } cmd = fm.getCommand(cmdName); if (cmd && !isCwd && (!fm.searchStatus.state || !cmd.disableOnSearch)) { cmd.__disabled = cmd._disabled; cmd._disabled = !(cmd.alwaysEnabled || (fm._commands[cmdName] ? jQuery.inArray(name, disabled) === -1 && (!useMap || !disabled[cmdName]) : false)); jQuery.each(cmd.linkedCmds, function(i, n) { var c; if (c = fm.getCommand(n)) { c.__disabled = c._disabled; c._disabled = !(c.alwaysEnabled || (fm._commands[n] ? !disabled[n] : false)); } }); } if (cmd && !cmd._disabled && cmd.getstate(targets) != -1) { if (cmd.variants) { if (!cmd.variants.length) { return; } node = item(cmd.title, cmd.className? cmd.className : cmd.name, function(){}, cmd.contextmenuOpts); submenu = jQuery('<div class="ui-front ui-corner-all elfinder-contextmenu-sub"/>') .hide() .css('max-height', fm.getUI().height() - 30) .appendTo(node.append('<span class="elfinder-contextmenu-arrow"/>')); hover = function(show){ if (! show) { submenu.hide(); } else { var bstyle = base.attr('style'); base.width(base.width()); // top: '-1000px' to prevent visible scrollbar of window with the elFinder option `height: '100%'` submenu.css({ top: '-1000px', left: 'auto', right: 'auto' }); var nodeOffset = node.offset(), nodeleft = nodeOffset.left, nodetop = nodeOffset.top, nodewidth = node.outerWidth(), width = submenu.outerWidth(true), height = submenu.outerHeight(true), baseOffset = base.offset(), wwidth = baseOffset.left + base.width(), wheight = baseOffset.top + base.height(), cltr = ltr, x = nodewidth, y, over; if (ltr) { over = (nodeleft + nodewidth + width) - wwidth; if (over > 10) { if (nodeleft > width - 5) { x = x - 5; cltr = false; } else { if (!fm.UA.Mobile) { x = nodewidth - over; } } } } else { over = width - nodeleft; if (over > 0) { if ((nodeleft + nodewidth + width - 15) < wwidth) { x = x - 5; cltr = true; } else { if (!fm.UA.Mobile) { x = nodewidth - over; } } } } over = (nodetop + 5 + height) - wheight; y = (over > 0 && nodetop < wheight)? 5 - over : (over > 0? 30 - height : 5); menu.find('.elfinder-contextmenu-sub:visible').hide(); submenu.css({ top : y, left : cltr? x : 'auto', right: cltr? 'auto' : x, overflowY: 'auto' }).show(); base.attr('style', bstyle); } }; node.addClass('elfinder-contextmenu-group') .on('mouseleave', '.elfinder-contextmenu-sub', function(e) { if (! menu.data('draged')) { menu.removeData('submenuKeep'); } }) .on('submenuclose', '.elfinder-contextmenu-sub', function(e) { hover(false); }) .on('click', '.'+smItem, function(e){ var opts, $this; e.stopPropagation(); if (! menu.data('draged')) { $this = jQuery(this); if (!cmd.keepContextmenu) { menu.hide(); } else { $this.removeClass(cHover); node.addClass(cHover); } opts = $this.data('exec'); if (typeof opts === 'undefined') { opts = {}; } if (typeof opts === 'object') { opts._userAction = true; opts._currentType = type; opts._currentNode = $this; } !cmd.keepContextmenu && close(); fm.exec(cmd.name, targets, opts); } }) .on('touchend', function(e) { if (! menu.data('drag')) { hover(true); menu.data('submenuKeep', true); } }) .on('mouseenter mouseleave', function(e){ if (! menu.data('touching')) { if (node.data('timer')) { clearTimeout(node.data('timer')); node.removeData('timer'); } if (!jQuery(e.target).closest('.elfinder-contextmenu-sub', menu).length) { if (e.type === 'mouseleave') { if (! menu.data('submenuKeep')) { node.data('timer', setTimeout(function() { node.removeData('timer'); hover(false); }, 250)); } } else { node.data('timer', setTimeout(function() { node.removeData('timer'); hover(true); }, nodes.find('div.elfinder-contextmenu-sub:visible').length? 250 : 0)); } } } }); jQuery.each(cmd.variants, function(i, variant) { var item = variant === '|' ? '<div class="elfinder-contextmenu-separator"/>' : jQuery('<div class="'+cmItem+' '+smItem+'"><span>'+variant[1]+'</span></div>').data('exec', variant[0]), iconClass, icon; if (typeof variant[2] !== 'undefined') { icon = jQuery('<span/>').addClass('elfinder-button-icon elfinder-contextmenu-icon'); if (! /\//.test(variant[2])) { icon.addClass('elfinder-button-icon-'+variant[2]); } else { icon.css(urlIcon(variant[2])); } item.prepend(icon).addClass(smItem+'-icon'); } submenu.append(item); }); } else { node = item(cmd.title, cmd.className? cmd.className : cmd.name, function() { if (! menu.data('draged')) { !cmd.keepContextmenu && close(); fm.exec(cmd.name, targets, {_userAction: true, _currentType: type, _currentNode: node}); } }, cmd.contextmenuOpts); if (cmd.extra && cmd.extra.node) { jQuery('<span class="elfinder-button-icon elfinder-button-icon-'+(cmd.extra.icon || '')+' '+exIcon+'"/>') .append(cmd.extra.node).appendTo(node); jQuery(cmd.extra.node).trigger('ready', {targets: targets}); } else { node.remove('.'+exIcon); } } if (cmd.extendsCmd) { node.children('span.elfinder-button-icon').addClass('elfinder-button-icon-' + cmd.extendsCmd); } if (insSep) { menu.append('<div class="elfinder-contextmenu-separator"/>'); } menu.append(node); sep = true; insSep = false; } if (cmd && typeof cmd.__disabled !== 'undefined') { cmd._disabled = cmd.__disabled; delete cmd.__disabled; jQuery.each(cmd.linkedCmds, function(i, n) { var c; if (c = fm.getCommand(n)) { c._disabled = c.__disabled; delete c.__disabled; } }); } }); nodes = menu.children('div.'+cmItem); }, createFromRaw = function(raw) { currentType = 'raw'; jQuery.each(raw, function(i, data) { var node; if (data === '|') { menu.append('<div class="elfinder-contextmenu-separator"/>'); } else if (data.label && typeof data.callback == 'function') { node = item(data.label, data.icon, function() { if (! menu.data('draged')) { !data.remain && close(); data.callback(); } }, data.options || null); menu.append(node); } }); nodes = menu.children('div.'+cmItem); }, currentType = null, currentTargets = null; fm.one('load', function() { base = fm.getUI(); cwd = fm.getUI('cwd'); fm.bind('contextmenu', function(e) { var data = e.data, css = {}, prevNode; if (data.type && data.type !== 'files') { cwd.trigger('unselectall'); } close(); if (data.type && data.targets) { fm.trigger('contextmenucreate', data); create(data.type, data.targets); fm.trigger('contextmenucreatedone', data); } else if (data.raw) { createFromRaw(data.raw); } if (menu.children().length) { prevNode = data.prevNode || null; if (prevNode) { menu.data('prevNode', menu.prev()); prevNode.after(menu); } if (data.fitHeight) { css = {maxHeight: Math.min(fm.getUI().height(), jQuery(window).height()), overflowY: 'auto'}; menu.draggable('destroy').removeClass('ui-draggable'); } open(data.x, data.y, css); // call opened callback function if (data.opened && typeof data.opened === 'function') { data.opened.call(menu); } } }) .one('destroy', function() { menu.remove(); }) .bind('disable', close) .bind('select', function(e){ (currentType === 'files' && (!e.data || e.data.selected.toString() !== currentTargets.toString())) && close(); }); }) .shortcut({ pattern : fm.OS === 'mac' ? 'ctrl+m' : 'contextmenu shift+f10', description : 'contextmenu', callback : function(e) { e.stopPropagation(); e.preventDefault(); jQuery(document).one('contextmenu.' + fm.namespace, function(e) { e.preventDefault(); e.stopPropagation(); }); var sel = fm.selected(), type, targets, pos, elm; if (sel.length) { type = 'files'; targets = sel; elm = fm.cwdHash2Elm(sel[0]); } else { type = 'cwd'; targets = [ fm.cwd().hash ]; pos = fm.getUI('workzone').offset(); } if (! elm || ! elm.length) { elm = fm.getUI('workzone'); } pos = elm.offset(); pos.top += (elm.height() / 2); pos.left += (elm.width() / 2); fm.trigger('contextmenu', { 'type' : type, 'targets' : targets, 'x' : pos.left, 'y' : pos.top }); } }); }); }; /* * File: /js/ui/cwd.js */ /** * elFinder current working directory ui. * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfindercwd = function(fm, options) { this.not('.elfinder-cwd').each(function() { // fm.time('cwdLoad'); var mobile = fm.UA.Mobile, list = fm.viewType == 'list', undef = 'undefined', /** * Select event full name * * @type String **/ evtSelect = 'select.'+fm.namespace, /** * Unselect event full name * * @type String **/ evtUnselect = 'unselect.'+fm.namespace, /** * Disable event full name * * @type String **/ evtDisable = 'disable.'+fm.namespace, /** * Disable event full name * * @type String **/ evtEnable = 'enable.'+fm.namespace, c = 'class', /** * File css class * * @type String **/ clFile = fm.res(c, 'cwdfile'), /** * Selected css class * * @type String **/ fileSelector = '.'+clFile, /** * Selected css class * * @type String **/ clSelected = 'ui-selected', /** * Disabled css class * * @type String **/ clDisabled = fm.res(c, 'disabled'), /** * Draggable css class * * @type String **/ clDraggable = fm.res(c, 'draggable'), /** * Droppable css class * * @type String **/ clDroppable = fm.res(c, 'droppable'), /** * Hover css class * * @type String **/ clHover = fm.res(c, 'hover'), /** * Active css class * * @type String **/ clActive = fm.res(c, 'active'), /** * Hover css class * * @type String **/ clDropActive = fm.res(c, 'adroppable'), /** * Css class for temporary nodes (for mkdir/mkfile) commands * * @type String **/ clTmp = clFile+'-tmp', /** * Select checkbox css class * * @type String */ clSelChk = 'elfinder-cwd-selectchk', /** * Number of thumbnails to load in one request (new api only) * * @type Number **/ tmbNum = fm.options.loadTmbs > 0 ? fm.options.loadTmbs : 5, /** * Current search query. * * @type String */ query = '', /** * Currect clipboard(cut) hashes as object key * * @type Object */ clipCuts = {}, /** * Parents hashes of cwd * * @type Array */ cwdParents = [], /** * cwd current hashes * * @type Array */ cwdHashes = [], /** * incsearch current hashes * * @type Array */ incHashes = void 0, /** * Custom columns name and order * * @type Array */ customCols = [], /** * Current clicked element id of first time for dblclick * * @type String */ curClickId = '', /** * Custom columns builder * * @type Function */ customColsBuild = function() { var cols = ''; for (var i = 0; i < customCols.length; i++) { cols += '<td class="elfinder-col-'+customCols[i]+'">{' + customCols[i] + '}</td>'; } return cols; }, /** * Make template.row from customCols * * @type Function */ makeTemplateRow = function() { return '<tr id="{id}" class="'+clFile+' {permsclass} {dirclass}" title="{tooltip}"{css}><td class="elfinder-col-name"><div class="elfinder-cwd-file-wrapper"><span class="elfinder-cwd-icon {mime}"{style}/>{marker}<span class="elfinder-cwd-filename">{name}</span></div>'+selectCheckbox+'</td>'+customColsBuild()+'</tr>'; }, selectCheckbox = (jQuery.map(options.showSelectCheckboxUA, function(t) {return (fm.UA[t] || t.match(/^all$/i))? true : null;}).length)? '<div class="elfinder-cwd-select"><input type="checkbox" class="'+clSelChk+'"></div>' : '', colResizing = false, colWidth = null, /** * Table header height */ thHeight, /** * File templates * * @type Object **/ templates = { icon : '<div id="{id}" class="'+clFile+' {permsclass} {dirclass} ui-corner-all" title="{tooltip}"><div class="elfinder-cwd-file-wrapper ui-corner-all"><div class="elfinder-cwd-icon {mime} ui-corner-all" unselectable="on"{style}/>{marker}</div><div class="elfinder-cwd-filename" title="{nametitle}">{name}</div>'+selectCheckbox+'</div>', row : '' }, permsTpl = fm.res('tpl', 'perms'), lockTpl = fm.res('tpl', 'lock'), symlinkTpl = fm.res('tpl', 'symlink'), /** * Template placeholders replacement rules * * @type Object **/ replacement = { id : function(f) { return fm.cwdHash2Id(f.hash); }, name : function(f) { var name = fm.escape(f.i18 || f.name); !list && (name = name.replace(/([_.])/g, '​$1')); return name; }, nametitle : function(f) { return fm.escape(f.i18 || f.name); }, permsclass : function(f) { return fm.perms2class(f); }, perm : function(f) { return fm.formatPermissions(f); }, dirclass : function(f) { var cName = f.mime == 'directory' ? 'directory' : ''; f.isroot && (cName += ' isroot'); f.csscls && (cName += ' ' + fm.escape(f.csscls)); options.getClass && (cName += ' ' + options.getClass(f)); return cName; }, style : function(f) { return f.icon? fm.getIconStyle(f) : ''; }, mime : function(f) { var cName = fm.mime2class(f.mime); f.icon && (cName += ' elfinder-cwd-bgurl'); return cName; }, size : function(f) { return (f.mime === 'directory' && !f.size)? '-' : fm.formatSize(f.size); }, date : function(f) { return fm.formatDate(f); }, kind : function(f) { return fm.mime2kind(f); }, mode : function(f) { return f.perm? fm.formatFileMode(f.perm) : ''; }, modestr : function(f) { return f.perm? fm.formatFileMode(f.perm, 'string') : ''; }, modeoct : function(f) { return f.perm? fm.formatFileMode(f.perm, 'octal') : ''; }, modeboth : function(f) { return f.perm? fm.formatFileMode(f.perm, 'both') : ''; }, marker : function(f) { return (f.alias || f.mime == 'symlink-broken' ? symlinkTpl : '')+(!f.read || !f.write ? permsTpl : '')+(f.locked ? lockTpl : ''); }, tooltip : function(f) { var title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''), info = ''; if (query && f.path) { info = fm.escape(f.path.replace(/\/[^\/]*$/, '')); } else { info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, ' ') : ''; } if (list) { info += (info? ' ' : '') + fm.escape(f.i18 || f.name); } return info? info + ' ' + title : title; } }, /** * Type badge CSS added flag * * @type Object */ addedBadges = {}, /** * Type badge style sheet element * * @type Object */ addBadgeStyleSheet, /** * Add type badge CSS into 'head' * * @type Fundtion */ addBadgeStyle = function(mime, name) { var sel, ext, type; if (mime && ! addedBadges[mime]) { if (typeof addBadgeStyleSheet === 'undefined') { if (jQuery('#elfinderAddBadgeStyle'+fm.namespace).length) { jQuery('#elfinderAddBadgeStyle'+fm.namespace).remove(); } addBadgeStyleSheet = jQuery('<style id="addBadgeStyle'+fm.namespace+'"/>').insertBefore(jQuery('head').children(':first')).get(0).sheet || null; } if (addBadgeStyleSheet) { mime = mime.toLowerCase(); type = mime.split('/'); ext = fm.escape(fm.mimeTypes[mime] || (name.replace(/.bac?k$/i, '').match(/\.([^.]+)$/) || ['',''])[1]); if (ext) { sel = '.elfinder-cwd-icon-' + type[0].replace(/(\.|\+)/g, '-'); if (typeof type[1] !== 'undefined') { sel += '.elfinder-cwd-icon-' + type[1].replace(/(\.|\+)/g, '-'); } try { addBadgeStyleSheet.insertRule(sel + ':before{content:"' + ext.toLowerCase() + '"}', 0); } catch(e) {} } addedBadges[mime] = true; } } }, /** * Return file html * * @param Object file info * @return String **/ itemhtml = function(f) { f.mime && f.mime !== 'directory' && !addedBadges[f.mime] && addBadgeStyle(f.mime, f.name); return templates[list ? 'row' : 'icon'] .replace(/\{([a-z0-9_]+)\}/g, function(s, e) { return replacement[e] ? replacement[e](f, fm) : (f[e] ? f[e] : ''); }); }, /** * jQueery node that will be selected next * * @type Object jQuery node */ selectedNext = jQuery(), /** * Flag. Required for msie to avoid unselect files on dragstart * * @type Boolean **/ selectLock = false, /** * Move selection to prev/next file * * @param String move direction * @param Boolean append to current selection * @return void * @rise select */ select = function(keyCode, append) { var code = jQuery.ui.keyCode, prev = keyCode == code.LEFT || keyCode == code.UP, sel = cwd.find('[id].'+clSelected), selector = prev ? 'first:' : 'last', s, n, sib, top, left; function sibling(n, direction) { return n[direction+'All']('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):first'); } if (sel.length) { s = sel.filter(prev ? ':first' : ':last'); sib = sibling(s, prev ? 'prev' : 'next'); if (!sib.length) { // there is no sibling on required side - do not move selection n = s; } else if (list || keyCode == code.LEFT || keyCode == code.RIGHT) { // find real prevoius file n = sib; } else { // find up/down side file in icons view top = s.position().top; left = s.position().left; n = s; if (prev) { do { n = n.prev('[id]'); } while (n.length && !(n.position().top < top && n.position().left <= left)); if (n.hasClass(clDisabled)) { n = sibling(n, 'next'); } } else { do { n = n.next('[id]'); } while (n.length && !(n.position().top > top && n.position().left >= left)); if (n.hasClass(clDisabled)) { n = sibling(n, 'prev'); } // there is row before last one - select last file if (!n.length) { sib = cwd.find('[id]:not(.'+clDisabled+'):last'); if (sib.position().top > top) { n = sib; } } } } // !append && unselectAll(); } else { if (selectedNext.length) { n = prev? selectedNext.prev() : selectedNext; } else { // there are no selected file - select first/last one n = cwd.find('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):'+(prev ? 'last' : 'first')); } } if (n && n.length && !n.hasClass('elfinder-cwd-parent')) { if (s && append) { // append new files to selected n = s.add(s[prev ? 'prevUntil' : 'nextUntil']('#'+n.attr('id'))).add(n); } else { // unselect selected files sel.trigger(evtUnselect); } // select file(s) n.trigger(evtSelect); // set its visible scrollToView(n.filter(prev ? ':first' : ':last')); // update cache/view trigger(); } }, selectedFiles = {}, selectFile = function(hash) { fm.cwdHash2Elm(hash).trigger(evtSelect); }, allSelected = false, selectAll = function() { var phash = fm.cwd().hash; selectCheckbox && selectAllCheckbox.find('input').prop('checked', true); fm.lazy(function() { var files; if (fm.maxTargets && (incHashes || cwdHashes).length > fm.maxTargets) { unselectAll({ notrigger: true }); files = jQuery.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null; }); files = files.slice(0, fm.maxTargets); selectedFiles = {}; jQuery.each(files, function(i, v) { selectedFiles[v.hash] = true; fm.cwdHash2Elm(v.hash).trigger(evtSelect); }); fm.toast({mode: 'warning', msg: fm.i18n(['errMaxTargets', fm.maxTargets])}); } else { cwd.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').trigger(evtSelect); selectedFiles = fm.arrayFlip(incHashes || cwdHashes, true); } trigger(); selectCheckbox && selectAllCheckbox.data('pending', false); }, 0, {repaint: true}); }, /** * Unselect all files * * @param Object options * @return void */ unselectAll = function(opts) { var o = opts || {}; selectCheckbox && selectAllCheckbox.find('input').prop('checked', false); if (Object.keys(selectedFiles).length) { selectLock = false; selectedFiles = {}; cwd.find('[id].'+clSelected).trigger(evtUnselect); selectCheckbox && cwd.find('input:checkbox.'+clSelChk).prop('checked', false); } !o.notrigger && trigger(); selectCheckbox && selectAllCheckbox.data('pending', false); cwd.removeClass('elfinder-cwd-allselected'); }, selectInvert = function() { var invHashes = {}; if (allSelected) { unselectAll(); } else if (! Object.keys(selectedFiles).length) { selectAll(); } else { jQuery.each((incHashes || cwdHashes), function(i, h) { var itemNode = fm.cwdHash2Elm(h); if (! selectedFiles[h]) { invHashes[h] = true; itemNode.length && itemNode.trigger(evtSelect); } else { itemNode.length && itemNode.trigger(evtUnselect); } }); selectedFiles = invHashes; trigger(); } }, /** * Return selected files hashes list * * @return Array */ selected = function() { return Object.keys(selectedFiles); }, /** * Last selected node id * * @type String|Void */ lastSelect = void 0, /** * Fire elfinder "select" event and pass selected files to it * * @return void */ trigger = function() { var selected = Object.keys(selectedFiles), opts = { selected : selected, origin : 'cwd' }; if (oldSchoolItem && (selected.length > 1 || selected[0] !== fm.cwdId2Hash( oldSchoolItem.attr('id'))) && oldSchoolItem.hasClass(clSelected)) { oldSchoolItem.trigger(evtUnselect); } allSelected = selected.length && (selected.length === (incHashes || cwdHashes).length) && (!fm.maxTargets || selected.length <= fm.maxTargets); if (selectCheckbox) { selectAllCheckbox.find('input').prop('checked', allSelected); cwd[allSelected? 'addClass' : 'removeClass']('elfinder-cwd-allselected'); } if (allSelected) { opts.selectall = true; } else if (! selected.length) { opts.unselectall = true; } fm.trigger('select', opts); }, /** * Scroll file to set it visible * * @param DOMElement file/dir node * @return void */ scrollToView = function(o, blink) { if (! o.length) { return; } var ftop = o.position().top, fheight = o.outerHeight(true), wtop = wrapper.scrollTop(), wheight = wrapper.get(0).clientHeight, thheight = tableHeader? tableHeader.outerHeight(true) : 0; if (ftop + thheight + fheight > wtop + wheight) { wrapper.scrollTop(parseInt(ftop + thheight + fheight - wheight)); } else if (ftop < wtop) { wrapper.scrollTop(ftop); } list && wrapper.scrollLeft(0); !!blink && fm.resources.blink(o, 'lookme'); }, /** * Files we get from server but not show yet * * @type Array **/ buffer = [], /** * Extra data of buffer * * @type Object **/ bufferExt = {}, /** * Return index of elements with required hash in buffer * * @param String file hash * @return Number */ index = function(hash) { var l = buffer.length; while (l--) { if (buffer[l].hash == hash) { return l; } } return -1; }, /** * Scroll start event name * * @type String **/ scrollStartEvent = 'elfscrstart', /** * Scroll stop event name * * @type String **/ scrollEvent = 'elfscrstop', scrolling = false, /** * jQuery UI selectable option * * @type Object */ selectableOption = { disabled : true, filter : '[id]:first', stop : trigger, delay : 250, appendTo : 'body', autoRefresh: false, selected : function(e, ui) { jQuery(ui.selected).trigger(evtSelect); }, unselected : function(e, ui) { jQuery(ui.unselected).trigger(evtUnselect); } }, /** * hashes of items displayed in current view * * @type Object ItemHash => DomId */ inViewHashes = {}, /** * Processing when the current view is changed (On open, search, scroll, resize etc.) * * @return void */ wrapperRepaint = function(init, recnt) { if (!bufferExt.renderd) { return; } var firstNode = (list? cwd.find('tbody:first') : cwd).children('[id]'+(options.oldSchool? ':not(.elfinder-cwd-parent)' : '')+':first'); if (!firstNode.length) { return; } var selectable = cwd.data('selectable'), rec = (function() { var wos = wrapper.offset(), ww = wrapper.width(), w = jQuery(window), x = firstNode.width() / 2, l = Math.min(wos.left - w.scrollLeft() + (fm.direction === 'ltr'? x : ww - x), wos.left + ww - 10), t = wos.top - w.scrollTop() + 10 + (list? thHeight : 0); return {left: Math.max(0, Math.round(l)), top: Math.max(0, Math.round(t))}; })(), tgt = init? firstNode : jQuery(document.elementFromPoint(rec.left , rec.top)), ids = {}, tmbs = {}, multi = 5, cnt = Math.ceil((bufferExt.hpi? Math.ceil((wz.data('rectangle').height / bufferExt.hpi) * 1.5) : showFiles) / multi), chk = function() { var id, hash, file, i; for (i = 0; i < multi; i++) { id = tgt.attr('id'); if (id) { bufferExt.getTmbs = []; hash = fm.cwdId2Hash(id); inViewHashes[hash] = id; // for tmbs if (bufferExt.attachTmbs[hash]) { tmbs[hash] = bufferExt.attachTmbs[hash]; } // for selectable selectable && (ids[id] = true); } // next node tgt = tgt.next(); if (!tgt.length) { break; } } }, done = function() { var idsArr; if (cwd.data('selectable')) { Object.assign(ids, selectedFiles); idsArr = Object.keys(ids); if (idsArr.length) { selectableOption.filter = '#'+idsArr.join(', #'); cwd.selectable('enable').selectable('option', {filter : selectableOption.filter}).selectable('refresh'); } } if (Object.keys(tmbs).length) { bufferExt.getTmbs = []; attachThumbnails(tmbs); } }, setTarget = function() { if (!tgt.hasClass(clFile)) { tgt = tgt.closest(fileSelector); } }, arr, widget; inViewHashes = {}; selectable && cwd.selectable('option', 'disabled'); if (tgt.length) { if (!tgt.hasClass(clFile) && !tgt.closest(fileSelector).length) { // dialog, serach button etc. widget = fm.getUI().find('.ui-dialog:visible,.ui-widget:visible'); if (widget.length) { widget.hide(); tgt = jQuery(document.elementFromPoint(rec.left , rec.top)); widget.show(); } else { widget = null; } } setTarget(); if (!tgt.length) { // try search 5px down widget && widget.hide(); tgt = jQuery(document.elementFromPoint(rec.left , rec.top + 5)); widget && widget.show(); setTarget(); } } if (tgt.length) { if (tgt.attr('id')) { if (init) { for (var i = 0; i < cnt; i++) { chk(); if (! tgt.length) { break; } } done(); } else { bufferExt.repaintJob && bufferExt.repaintJob.state() === 'pending' && bufferExt.repaintJob.reject(); arr = new Array(cnt); bufferExt.repaintJob = fm.asyncJob(function() { chk(); if (! tgt.length) { done(); bufferExt.repaintJob && bufferExt.repaintJob.state() === 'pending' && bufferExt.repaintJob.reject(); } }, arr).done(done); } } } else if (init && bufferExt.renderd) { // In initial request, cwd DOM not renderd so doing lazy check recnt = recnt || 0; if (recnt < 10) { // Prevent infinite loop requestAnimationFrame(function() { wrapperRepaint(init, ++recnt); }); } } }, /** * Item node of oldScholl ".." */ oldSchoolItem = null, /** * display parent folder with ".." name * * @param String phash * @return void */ oldSchool = function(p) { var phash = fm.cwd().phash, pdir = fm.file(phash) || null, set = function(pdir) { if (pdir) { oldSchoolItem = jQuery(itemhtml(jQuery.extend(true, {}, pdir, {name : '..', i18 : '..', mime : 'directory'}))) .addClass('elfinder-cwd-parent') .on('dblclick', function() { var hash = fm.cwdId2Hash(this.id); fm.trigger('select', {selected : [hash]}).exec('open', hash); }); (list ? oldSchoolItem.children('td:first') : oldSchoolItem).children('.elfinder-cwd-select').remove(); (list ? cwd.find('tbody') : cwd).prepend(oldSchoolItem); fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1); } }; if (pdir) { set(pdir); } else { if (fm.getUI('tree').length) { fm.one('parents', function() { set(fm.file(phash) || null); wrapper.trigger(scrollEvent); }); } else { fm.request({ data : {cmd : 'parents', target : fm.cwd().hash}, preventFail : true }) .done(function(data) { set(fm.file(phash) || null); wrapper.trigger(scrollEvent); }); } } }, showFiles = fm.options.showFiles, /** * Cwd scroll event handler. * Lazy load - append to cwd not shown files * * @return void */ render = function() { if (bufferExt.rendering || (bufferExt.renderd && ! buffer.length)) { return; } var place = (list ? cwd.children('table').children('tbody') : cwd), phash, chk, // created document fragment for jQuery >= 1.12, 2.2, 3.0 // see Studio-42/elFinder#1544 @ github docFlag = jQuery.htmlPrefilter? true : false, tempDom = docFlag? jQuery(document.createDocumentFragment()) : jQuery('<div/>'), go = function(o){ var over = o || null, html = [], dirs = false, atmb = {}, stmb = (fm.option('tmbUrl') === 'self'), init = bufferExt.renderd? false : true, files, locks, selected; files = buffer.splice(0, showFiles + (over || 0) / (bufferExt.hpi || 1)); bufferExt.renderd += files.length; if (! buffer.length) { bottomMarker.hide(); wrapper.off(scrollEvent, render); } locks = []; html = jQuery.map(files, function(f) { if (f.hash && f.name) { if (f.mime == 'directory') { dirs = true; } if ((f.tmb && (f.tmb != 1 || f.size > 0)) || (stmb && f.mime.indexOf('image/') === 0)) { atmb[f.hash] = f.tmb || 'self'; } clipCuts[f.hash] && locks.push(f.hash); return itemhtml(f); } return null; }); // html into temp node tempDom.empty().append(html.join('')); // make directory droppable dirs && !mobile && makeDroppable(tempDom); // check selected items selected = []; if (Object.keys(selectedFiles).length) { tempDom.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').each(function() { selectedFiles[fm.cwdId2Hash(this.id)] && selected.push(jQuery(this)); }); } // append to cwd place.append(docFlag? tempDom : tempDom.children()); // trigger select if (selected.length) { jQuery.each(selected, function(i, n) { n.trigger(evtSelect); }); trigger(); } locks.length && fm.trigger('lockfiles', {files: locks}); !bufferExt.hpi && bottomMarkerShow(place, files.length); if (list) { // show thead cwd.find('thead').show(); // fixed table header fixTableHeader({fitWidth: ! colWidth}); } if (Object.keys(atmb).length) { Object.assign(bufferExt.attachTmbs, atmb); } if (init) { if (! mobile && ! cwd.data('selectable')) { // make files selectable cwd.selectable(selectableOption).data('selectable', true); } } ! scrolling && wrapper.trigger(scrollEvent); }; if (! bufferExt.renderd) { // first time to go() bufferExt.rendering = true; // scroll top on dir load to avoid scroll after page reload wrapper.scrollTop(0); phash = fm.cwd().phash; go(); if (options.oldSchool) { if (phash && !query) { oldSchool(phash); } else { oldSchoolItem = jQuery(); } } if (list) { colWidth && setColwidth(); fixTableHeader({fitWidth: true}); } bufferExt.itemH = (list? place.find('tr:first') : place.find('[id]:first')).outerHeight(true); fm.trigger('cwdrender'); bufferExt.rendering = false; wrapperRepaint(true); } if (! bufferExt.rendering && buffer.length) { // next go() if ((chk = (wrapper.height() + wrapper.scrollTop() + fm.options.showThreshold + bufferExt.row) - (bufferExt.renderd * bufferExt.hpi)) > 0) { bufferExt.rendering = true; fm.lazy(function() { go(chk); bufferExt.rendering = false; }); } else { !fm.enabled() && resize(); } } else { resize(); } }, // fixed table header jQuery object tableHeader = null, // Is UA support CSS sticky cssSticky = fm.UA.CSS.positionSticky && fm.UA.CSS.widthMaxContent, // To fixed table header colmun fixTableHeader = function(optsArg) { thHeight = 0; if (! options.listView.fixedHeader) { return; } var setPos = function() { var val, pos; pos = (fm.direction === 'ltr')? 'left' : 'right'; val = ((fm.direction === 'ltr')? wrapper.scrollLeft() : table.outerWidth(true) - wrapper.width() - wrapper.scrollLeft()) * -1; if (base.css(pos) !== val) { base.css(pos, val); } }, opts = optsArg || {}, cnt, base, table, htable, thead, tbody, hheight, htr, btr, htd, btd, htw, btw, init; tbody = cwd.find('tbody'); btr = tbody.children('tr:first'); if (btr.length && btr.is(':visible')) { table = tbody.parent(); if (! tableHeader) { init = true; tbody.addClass('elfinder-cwd-fixheader'); thead = cwd.find('thead').attr('id', fm.namespace+'-cwd-thead'); htr = thead.children('tr:first'); hheight = htr.outerHeight(true); cwd.css('margin-top', hheight - parseInt(table.css('padding-top'))); if (cssSticky) { tableHeader = jQuery('<div class="elfinder-table-header-sticky"/>').addClass(cwd.attr('class')).append(jQuery('<table/>').append(thead)); cwd.after(tableHeader); wrapper.on('resize.fixheader', function(e) { e.stopPropagation(); fixTableHeader({fitWidth: true}); }); } else { base = jQuery('<div/>').addClass(cwd.attr('class')).append(jQuery('<table/>').append(thead)); tableHeader = jQuery('<div/>').addClass(wrapper.attr('class') + ' elfinder-cwd-fixheader') .removeClass('ui-droppable native-droppable') .css(wrapper.position()) .css({ height: hheight, width: cwd.outerWidth() }) .append(base); if (fm.direction === 'rtl') { tableHeader.css('left', (wrapper.data('width') - wrapper.width()) + 'px'); } setPos(); wrapper.after(tableHeader) .on('scroll.fixheader resize.fixheader', function(e) { setPos(); if (e.type === 'resize') { e.stopPropagation(); tableHeader.css(wrapper.position()); wrapper.data('width', wrapper.css('overflow', 'hidden').width()); wrapper.css('overflow', 'auto'); fixTableHeader(); } }); } } else { thead = jQuery('#'+fm.namespace+'-cwd-thead'); htr = thead.children('tr:first'); } if (init || opts.fitWidth || Math.abs(btr.outerWidth() - htr.outerWidth()) > 2) { cnt = customCols.length + 1; for (var i = 0; i < cnt; i++) { htd = htr.children('td:eq('+i+')'); btd = btr.children('td:eq('+i+')'); htw = htd.width(); btw = btd.width(); if (typeof htd.data('delta') === 'undefined') { htd.data('delta', (htd.outerWidth() - htw) - (btd.outerWidth() - btw)); } btw -= htd.data('delta'); if (! init && ! opts.fitWidth && htw === btw) { break; } htd.css('width', btw + 'px'); } } if (!cssSticky) { tableHeader.data('widthTimer') && cancelAnimationFrame(tableHeader.data('widthTimer')); tableHeader.data('widthTimer', requestAnimationFrame(function() { if (tableHeader) { tableHeader.css('width', mBoard.width() + 'px'); if (fm.direction === 'rtl') { tableHeader.css('left', (wrapper.data('width') - wrapper.width()) + 'px'); } } })); } thHeight = thead.height(); } }, // Set colmun width setColwidth = function() { if (list && colWidth) { var cl = 'elfinder-cwd-colwidth', first = cwd.find('tr[id]:first'), former; if (! first.hasClass(cl)) { former = cwd.find('tr.'+cl); former.removeClass(cl).find('td').css('width', ''); first.addClass(cl); cwd.find('table:first').css('table-layout', 'fixed'); jQuery.each(jQuery.merge(['name'], customCols), function(i, k) { var w = colWidth[k] || first.find('td.elfinder-col-'+k).width(); first.find('td.elfinder-col-'+k).width(w); }); } } }, /** * Droppable options for cwd. * Drop target is `wrapper` * Do not add class on childs file over * * @type Object */ droppable = Object.assign({}, fm.droppable, { over : function(e, ui) { var dst = jQuery(this), helper = ui.helper, ctr = (e.shiftKey || e.ctrlKey || e.metaKey), hash, status, inParent; e.stopPropagation(); helper.data('dropover', helper.data('dropover') + 1); dst.data('dropover', true); helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus'); if (helper.data('namespace') !== fm.namespace || ! fm.insideWorkzone(e.pageX, e.pageY)) { dst.removeClass(clDropActive); //helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus'); return; } if (dst.hasClass(fm.res(c, 'cwdfile'))) { hash = fm.cwdId2Hash(dst.attr('id')); dst.data('dropover', hash); } else { hash = fm.cwd().hash; fm.cwd().write && dst.data('dropover', hash); } inParent = (fm.file(helper.data('files')[0]).phash === hash); if (dst.data('dropover') === hash) { jQuery.each(helper.data('files'), function(i, h) { if (h === hash || (inParent && !ctr && !helper.hasClass('elfinder-drag-helper-plus'))) { dst.removeClass(clDropActive); return false; // break jQuery.each } }); } else { dst.removeClass(clDropActive); } if (helper.data('locked') || inParent) { status = 'elfinder-drag-helper-plus'; } else { status = 'elfinder-drag-helper-move'; if (ctr) { status += ' elfinder-drag-helper-plus'; } } dst.hasClass(clDropActive) && helper.addClass(status); requestAnimationFrame(function(){ dst.hasClass(clDropActive) && helper.addClass(status); }); }, out : function(e, ui) { var helper = ui.helper; e.stopPropagation(); helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0)); jQuery(this).removeData('dropover') .removeClass(clDropActive); }, deactivate : function() { jQuery(this).removeData('dropover') .removeClass(clDropActive); }, drop : function(e, ui) { unselectAll({ notrigger: true }); fm.droppable.drop.call(this, e, ui); } }), /** * Make directory droppable * * @return void */ makeDroppable = function(place) { place = place? place : (list ? cwd.find('tbody') : cwd); var targets = place.children('.directory:not(.'+clDroppable+',.elfinder-na,.elfinder-ro)'); if (fm.isCommandEnabled('paste')) { targets.droppable(droppable); } if (fm.isCommandEnabled('upload')) { targets.addClass('native-droppable'); } place.children('.isroot').each(function(i, n) { var $n = jQuery(n), hash = fm.cwdId2Hash(n.id); if (fm.isCommandEnabled('paste', hash)) { if (! $n.hasClass(clDroppable+',elfinder-na,elfinder-ro')) { $n.droppable(droppable); } } else { if ($n.hasClass(clDroppable)) { $n.droppable('destroy'); } } if (fm.isCommandEnabled('upload', hash)) { if (! $n.hasClass('native-droppable,elfinder-na,elfinder-ro')) { $n.addClass('native-droppable'); } } else { if ($n.hasClass('native-droppable')) { $n.removeClass('native-droppable'); } } }); }, /** * Preload required thumbnails and on load add css to files. * Return false if required file is not visible yet (in buffer) - * required for old api to stop loading thumbnails. * * @param Object file hash -> thumbnail map * @param Bool reload * @return void */ attachThumbnails = function(tmbs, reload) { var attach = function(node, tmb) { jQuery('<img/>') .on('load', function() { node.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); }) .attr('src', tmb.url); }, chk = function(hash, tmb) { var node = fm.cwdHash2Elm(hash), file, tmbObj, reloads = []; if (node.length) { if (tmb != '1') { file = fm.file(hash); if (file.tmb !== tmb) { file.tmb = tmb; } tmbObj = fm.tmb(file); if (reload) { node.find('.elfinder-cwd-icon').addClass(tmbObj.className).css('background-image', "url('"+tmbObj.url+"')"); } else { attach(node, tmbObj); } delete bufferExt.attachTmbs[hash]; } else { if (reload) { loadThumbnails([hash]); } else if (! bufferExt.tmbLoading[hash]) { bufferExt.getTmbs.push(hash); } } } }; if (jQuery.isPlainObject(tmbs) && Object.keys(tmbs).length) { Object.assign(bufferExt.attachTmbs, tmbs); jQuery.each(tmbs, chk); if (! reload && bufferExt.getTmbs.length && ! Object.keys(bufferExt.tmbLoading).length) { loadThumbnails(); } } }, /** * Load thumbnails from backend. * * @param Array|void reloads hashes list for reload thumbnail items * @return void */ loadThumbnails = function(reloads) { var tmbs = [], reload = false; if (fm.oldAPI) { fm.request({ data : {cmd : 'tmb', current : fm.cwd().hash}, preventFail : true }) .done(function(data) { if (data.images && Object.keys(data.images).length) { attachThumbnails(data.images); } if (data.tmb) { loadThumbnails(); } }); return; } if (reloads) { reload = true; tmbs = reloads.splice(0, tmbNum); } else { tmbs = bufferExt.getTmbs.splice(0, tmbNum); } if (tmbs.length) { if (reload || inViewHashes[tmbs[0]] || inViewHashes[tmbs[tmbs.length-1]]) { jQuery.each(tmbs, function(i, h) { bufferExt.tmbLoading[h] = true; }); fm.request({ data : {cmd : 'tmb', targets : tmbs}, preventFail : true }) .done(function(data) { var errs = [], resLen; if (data.images) { if (resLen = Object.keys(data.images).length) { if (resLen < tmbs.length) { jQuery.each(tmbs, function(i, h) { if (! data.images[h]) { errs.push(h); } }); } attachThumbnails(data.images, reload); } else { errs = tmbs; } // unset error items from bufferExt.attachTmbs if (errs.length) { jQuery.each(errs, function(i, h) { delete bufferExt.attachTmbs[h]; }); } } if (reload) { if (reloads.length) { loadThumbnails(reloads); } } }) .always(function() { bufferExt.tmbLoading = {}; if (! reload && bufferExt.getTmbs.length) { loadThumbnails(); } }); } } }, /** * Add new files to cwd/buffer * * @param Array new files * @return void */ add = function(files, mode) { var place = list ? cwd.find('tbody') : cwd, l = files.length, atmb = {}, findNode = function(file) { var pointer = cwd.find('[id]:first'), file2; while (pointer.length) { file2 = fm.file(fm.cwdId2Hash(pointer.attr('id'))); if (!pointer.hasClass('elfinder-cwd-parent') && file2 && fm.compare(file, file2) < 0) { return pointer; } pointer = pointer.next('[id]'); } }, findIndex = function(file) { var l = buffer.length, i; for (i =0; i < l; i++) { if (fm.compare(file, buffer[i]) < 0) { return i; } } return l || -1; }, // created document fragment for jQuery >= 1.12, 2.2, 3.0 // see Studio-42/elFinder#1544 @ github docFlag = jQuery.htmlPrefilter? true : false, tempDom = docFlag? jQuery(document.createDocumentFragment()) : jQuery('<div/>'), file, hash, node, nodes, ndx, stmb; if (l > showFiles) { // re-render for performance tune content(); selectedFiles = fm.arrayFlip(jQuery.map(files, function(f) { return f.hash; }), true); trigger(); } else { // add the item immediately l && wz.removeClass('elfinder-cwd-wrapper-empty'); // Self thumbnail stmb = (fm.option('tmbUrl') === 'self'); while (l--) { file = files[l]; hash = file.hash; if (fm.cwdHash2Elm(hash).length) { continue; } if ((node = findNode(file)) && ! node.length) { node = null; } if (! node && (ndx = findIndex(file)) >= 0) { buffer.splice(ndx, 0, file); } else { tempDom.empty().append(itemhtml(file)); (file.mime === 'directory') && !mobile && makeDroppable(tempDom); nodes = docFlag? tempDom : tempDom.children(); if (node) { node.before(nodes); } else { place.append(nodes); } } if (fm.cwdHash2Elm(hash).length) { if ((file.tmb && (file.tmb != 1 || file.size > 0)) || (stmb && file.mime.indexOf('image/') === 0)) { atmb[hash] = file.tmb || 'self'; } } } if (list) { setColwidth(); fixTableHeader({fitWidth: ! colWidth}); } bottomMarkerShow(place); if (Object.keys(atmb).length) { Object.assign(bufferExt.attachTmbs, atmb); } } }, /** * Remove files from cwd/buffer * * @param Array files hashes * @return void */ remove = function(files) { var l = files.length, inSearch = fm.searchStatus.state > 1, curCmd = fm.getCommand(fm.currentReqCmd) || {}, hash, n, ndx, found; // removed cwd if (!fm.cwd().hash && !curCmd.noChangeDirOnRemovedCwd) { jQuery.each(cwdParents.reverse(), function(i, h) { if (fm.file(h)) { found = true; fm.one(fm.currentReqCmd + 'done', function() { !fm.cwd().hash && fm.exec('open', h); }); return false; } }); // fallback to fm.roots[0] !found && !fm.cwd().hash && fm.exec('open', fm.roots[Object.keys(fm.roots)[0]]); return; } while (l--) { hash = files[l]; if ((n = fm.cwdHash2Elm(hash)).length) { try { n.remove(); --bufferExt.renderd; } catch(e) { fm.debug('error', e); } } else if ((ndx = index(hash)) !== -1) { buffer.splice(ndx, 1); } selectedFiles[hash] && delete selectedFiles[hash]; if (inSearch) { if ((ndx = jQuery.inArray(hash, cwdHashes)) !== -1) { cwdHashes.splice(ndx, 1); } } } inSearch && fm.trigger('cwdhasheschange', cwdHashes); if (list) { setColwidth(); fixTableHeader({fitWidth: ! colWidth}); } }, customColsNameBuild = function() { var name = '', customColsName = ''; for (var i = 0; i < customCols.length; i++) { name = fm.getColumnName(customCols[i]); customColsName +='<td class="elfinder-cwd-view-th-'+customCols[i]+' sortable-item">'+name+'</td>'; } return customColsName; }, setItemBoxSize = function(boxSize) { var place, elm; if (!boxSize.height) { place = (list ? cwd.find('tbody') : cwd); elm = place.find(list? 'tr:first' : '[id]:first'); boxSize.height = elm.outerHeight(true); if (!list) { boxSize.width = elm.outerWidth(true); } } }, bottomMarkerShow = function(cur, cnt) { var place = cur || (list ? cwd.find('tbody') : cwd), boxSize = itemBoxSize[fm.viewType], col = 1, row; if (buffer.length > 0) { if (!bufferExt.hpi) { setItemBoxSize(boxSize); if (! list) { col = Math.floor(place.width() / boxSize.width); bufferExt.row = boxSize.height; bufferExt.hpi = bufferExt.row / col; } else { bufferExt.row = bufferExt.hpi = boxSize.height; } } else if (!list) { col = Math.floor(place.width() / boxSize.width); } row = Math.ceil((buffer.length + (cnt || 0)) / col); if (list && tableHeader) { ++row; } bottomMarker.css({top: (bufferExt.row * row) + 'px'}).show(); } }, wrapperContextMenu = { contextmenu : function(e) { e.preventDefault(); if (cwd.data('longtap') !== void(0)) { e.stopPropagation(); return; } fm.trigger('contextmenu', { 'type' : 'cwd', 'targets' : [fm.cwd().hash], 'x' : e.pageX, 'y' : e.pageY }); }, touchstart : function(e) { if (e.originalEvent.touches.length > 1) { return; } if (cwd.data('longtap') !== false) { wrapper.data('touching', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY}); cwd.data('tmlongtap', setTimeout(function(){ // long tap cwd.data('longtap', true); fm.trigger('contextmenu', { 'type' : 'cwd', 'targets' : [fm.cwd().hash], 'x' : wrapper.data('touching').x, 'y' : wrapper.data('touching').y }); }, 500)); } cwd.data('longtap', null); }, touchend : function(e) { if (e.type === 'touchmove') { if (! wrapper.data('touching') || ( Math.abs(wrapper.data('touching').x - e.originalEvent.touches[0].pageX) + Math.abs(wrapper.data('touching').y - e.originalEvent.touches[0].pageY)) > 4) { wrapper.data('touching', null); } } else { setTimeout(function() { cwd.removeData('longtap'); }, 80); } clearTimeout(cwd.data('tmlongtap')); }, click : function(e) { if (cwd.data('longtap')) { e.preventDefault(); e.stopPropagation(); } } }, /** * Update directory content * * @return void */ content = function() { fm.lazy(function() { var phash, emptyMethod, thtr; wz.append(selectAllCheckbox).removeClass('elfinder-cwd-wrapper-empty elfinder-search-result elfinder-incsearch-result elfinder-letsearch-result'); if (fm.searchStatus.state > 1 || fm.searchStatus.ininc) { wz.addClass('elfinder-search-result' + (fm.searchStatus.ininc? ' elfinder-'+(query.substr(0,1) === '/' ? 'let':'inc')+'search-result' : '')); } // abort attachThumbJob bufferExt.attachThumbJob && bufferExt.attachThumbJob._abort(); // destroy selectable for GC cwd.data('selectable') && cwd.selectable('disable').selectable('destroy').removeData('selectable'); // notify cwd init fm.trigger('cwdinit'); selectedNext = jQuery(); try { // to avoid problem with draggable cwd.empty(); } catch (e) { cwd.html(''); } if (tableHeader) { wrapper.off('scroll.fixheader resize.fixheader'); tableHeader.remove(); tableHeader = null; } cwd.removeClass('elfinder-cwd-view-icons elfinder-cwd-view-list') .addClass('elfinder-cwd-view-'+(list ? 'list' :'icons')) .attr('style', '') .css('height', 'auto'); bottomMarker.hide(); wrapper[list ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-list')._padding = parseInt(wrapper.css('padding-top')) + parseInt(wrapper.css('padding-bottom')); if (fm.UA.iOS) { wrapper.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch'); } if (list) { cwd.html('<table><thead/><tbody/></table>'); thtr = jQuery('<tr class="ui-state-default"><td class="elfinder-cwd-view-th-name">'+fm.getColumnName('name')+'</td>'+customColsNameBuild()+'</tr>'); cwd.find('thead').hide().append(thtr).find('td:first').append(selectAllCheckbox); if (jQuery.fn.sortable) { thtr.addClass('touch-punch touch-punch-keep-default') .sortable({ axis: 'x', distance: 8, items: '> .sortable-item', start: function(e, ui) { jQuery(ui.item[0]).data('dragging', true); ui.placeholder .width(ui.helper.removeClass('ui-state-hover').width()) .removeClass('ui-state-active') .addClass('ui-state-hover') .css('visibility', 'visible'); }, update: function(e, ui){ var target = jQuery(ui.item[0]).attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', ''), prev, done; customCols = jQuery.map(jQuery(this).children(), function(n) { var name = jQuery(n).attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', ''); if (! done) { if (target === name) { done = true; } else { prev = name; } } return (name === 'name')? null : name; }); templates.row = makeTemplateRow(); fm.storage('cwdCols', customCols); prev = '.elfinder-col-'+prev+':first'; target = '.elfinder-col-'+target+':first'; fm.lazy(function() { cwd.find('tbody tr').each(function() { var $this = jQuery(this); $this.children(prev).after($this.children(target)); }); }); }, stop: function(e, ui) { setTimeout(function() { jQuery(ui.item[0]).removeData('dragging'); }, 100); } }); } thtr.find('td').addClass('touch-punch').resizable({ handles: fm.direction === 'ltr'? 'e' : 'w', start: function(e, ui) { var target = cwd.find('td.elfinder-col-' + ui.element.attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', '') + ':first'); ui.element .data('dragging', true) .data('resizeTarget', target) .data('targetWidth', target.width()); colResizing = true; if (cwd.find('table').css('table-layout') !== 'fixed') { cwd.find('tbody tr:first td').each(function() { jQuery(this).width(jQuery(this).width()); }); cwd.find('table').css('table-layout', 'fixed'); } }, resize: function(e, ui) { ui.element.data('resizeTarget').width(ui.element.data('targetWidth') - (ui.originalSize.width - ui.size.width)); }, stop : function(e, ui) { colResizing = false; fixTableHeader({fitWidth: true}); colWidth = {}; cwd.find('tbody tr:first td').each(function() { var name = jQuery(this).attr('class').split(' ')[0].replace('elfinder-col-', ''); colWidth[name] = jQuery(this).width(); }); fm.storage('cwdColWidth', colWidth); setTimeout(function() { ui.element.removeData('dragging'); }, 100); } }) .find('.ui-resizable-handle').addClass('ui-icon ui-icon-grip-dotted-vertical'); } buffer = jQuery.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null; }); buffer = fm.sortFiles(buffer); if (incHashes) { incHashes = jQuery.map(buffer, function(f) { return f.hash; }); } else { cwdHashes = jQuery.map(buffer, function(f) { return f.hash; }); } bufferExt = { renderd: 0, attachTmbs: {}, getTmbs: [], tmbLoading: {}, lazyOpts: { tm : 0 } }; wz[(buffer.length < 1) ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-empty'); wrapper.off(scrollEvent, render).on(scrollEvent, render).trigger(scrollEvent); // set droppable if (!fm.cwd().write) { wrapper.removeClass('native-droppable') .droppable('disable') .removeClass('ui-state-disabled'); // for old jQueryUI see https://bugs.jqueryui.com/ticket/5974 } else { wrapper[fm.isCommandEnabled('upload')? 'addClass' : 'removeClass']('native-droppable'); wrapper.droppable(fm.isCommandEnabled('paste')? 'enable' : 'disable'); } }); }, /** * CWD node itself * * @type JQuery **/ cwd = jQuery(this) .addClass('ui-helper-clearfix elfinder-cwd') .attr('unselectable', 'on') // fix ui.selectable bugs and add shift+click support .on('click.'+fm.namespace, fileSelector, function(e) { var p = this.id ? jQuery(this) : jQuery(this).parents('[id]:first'), tgt = jQuery(e.target), prev, next, pl, nl, sib; if (selectCheckbox && (tgt.is('input:checkbox.'+clSelChk) || tgt.hasClass('elfinder-cwd-select'))) { e.stopPropagation(); e.preventDefault(); p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect); trigger(); requestAnimationFrame(function() { tgt.prop('checked', p.hasClass(clSelected)); }); return; } if (cwd.data('longtap') || tgt.hasClass('elfinder-cwd-nonselect')) { e.stopPropagation(); return; } if (!curClickId) { curClickId = p.attr('id'); setTimeout(function() { curClickId = ''; }, 500); } if (e.shiftKey) { prev = p.prevAll(lastSelect || '.'+clSelected+':first'); next = p.nextAll(lastSelect || '.'+clSelected+':first'); pl = prev.length; nl = next.length; } if (e.shiftKey && (pl || nl)) { sib = pl ? p.prevUntil('#'+prev.attr('id')) : p.nextUntil('#'+next.attr('id')); sib.add(p).trigger(evtSelect); } else if (e.ctrlKey || e.metaKey) { p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect); } else { if (wrapper.data('touching') && p.hasClass(clSelected)) { wrapper.data('touching', null); fm.dblclick({file : fm.cwdId2Hash(this.id)}); return; } else { unselectAll({ notrigger: true }); p.trigger(evtSelect); } } trigger(); }) // call fm.open() .on('dblclick.'+fm.namespace, fileSelector, function(e) { if (curClickId) { var hash = fm.cwdId2Hash(curClickId); e.stopPropagation(); if (this.id !== curClickId) { jQuery(this).trigger(evtUnselect); jQuery('#'+curClickId).trigger(evtSelect); trigger(); } fm.dblclick({file : hash}); } }) // for touch device .on('touchstart.'+fm.namespace, fileSelector, function(e) { if (e.originalEvent.touches.length > 1) { return; } var p = this.id ? jQuery(this) : jQuery(this).parents('[id]:first'), tgt = jQuery(e.target), nodeName = e.target.nodeName, sel; if ((nodeName === 'INPUT' && e.target.type === 'text') || nodeName === 'TEXTAREA' || tgt.hasClass('elfinder-cwd-nonselect')) { e.stopPropagation(); return; } // now name editing if (p.find('input:text,textarea').length) { e.stopPropagation(); e.preventDefault(); return; } wrapper.data('touching', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY}); if (selectCheckbox && (tgt.is('input:checkbox.'+clSelChk) || tgt.hasClass('elfinder-cwd-select'))) { return; } sel = p.prevAll('.'+clSelected+':first').length + p.nextAll('.'+clSelected+':first').length; cwd.data('longtap', null); if (Object.keys(selectedFiles).length || (list && e.target.nodeName !== 'TD') || (!list && this !== e.target) ) { cwd.data('longtap', false); p.addClass(clHover); p.data('tmlongtap', setTimeout(function(){ // long tap cwd.data('longtap', true); p.trigger(evtSelect); trigger(); fm.trigger('contextmenu', { 'type' : 'files', 'targets' : fm.selected(), 'x' : e.originalEvent.touches[0].pageX, 'y' : e.originalEvent.touches[0].pageY }); }, 500)); } }) .on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, fileSelector, function(e) { var tgt = jQuery(e.target), p; if (selectCheckbox && (tgt.is('input:checkbox.'+clSelChk) || tgt.hasClass('elfinder-cwd-select'))) { return; } if (e.target.nodeName == 'INPUT' || e.target.nodeName == 'TEXTAREA') { e.stopPropagation(); return; } p = this.id ? jQuery(this) : jQuery(this).parents('[id]:first'); clearTimeout(p.data('tmlongtap')); if (e.type === 'touchmove') { wrapper.data('touching', null); p.removeClass(clHover); } else { if (wrapper.data('touching') && !cwd.data('longtap') && p.hasClass(clSelected)) { e.preventDefault(); wrapper.data('touching', null); fm.dblclick({file : fm.cwdId2Hash(this.id)}); } setTimeout(function() { cwd.removeData('longtap'); }, 80); } }) // attach draggable .on('mouseenter.'+fm.namespace, fileSelector, function(e) { if (scrolling) { return; } var $this = jQuery(this), helper = null; if (!mobile && !$this.data('dragRegisted') && !$this.hasClass(clTmp) && !$this.hasClass(clDraggable) && !$this.hasClass(clDisabled)) { $this.data('dragRegisted', true); if (!fm.isCommandEnabled('copy', fm.searchStatus.state > 1 || $this.hasClass('isroot')? fm.cwdId2Hash($this.attr('id')) : void 0)) { return; } $this.on('mousedown', function(e) { // shiftKey or altKey + drag start for HTML5 native drag function // Note: can no use shiftKey with the Google Chrome var metaKey = e.shiftKey || e.altKey, disable = false; if (metaKey && !fm.UA.IE && cwd.data('selectable')) { // destroy jQuery-ui selectable while trigger native drag cwd.selectable('disable').selectable('destroy').removeData('selectable'); requestAnimationFrame(function(){ cwd.selectable(selectableOption).selectable('option', {disabled: false}).selectable('refresh').data('selectable', true); }); } $this.removeClass('ui-state-disabled'); if (metaKey) { $this.draggable('option', 'disabled', true).attr('draggable', 'true'); } else { if (!$this.hasClass(clSelected)) { if (list) { disable = jQuery(e.target).closest('span,tr').is('tr'); } else { disable = jQuery(e.target).hasClass('elfinder-cwd-file'); } } if (disable) { $this.draggable('option', 'disabled', true); } else { $this.draggable('option', 'disabled', false) .removeAttr('draggable') .draggable('option', 'cursorAt', {left: 50 - parseInt(jQuery(e.currentTarget).css('margin-left')), top: 47}); } } }) .on('dragstart', function(e) { var dt = e.dataTransfer || e.originalEvent.dataTransfer || null; helper = null; if (dt && !fm.UA.IE) { var p = this.id ? jQuery(this) : jQuery(this).parents('[id]:first'), elm = jQuery('<span>'), url = '', durl = null, murl = null, files = [], icon = function(f) { var mime = f.mime, i, tmb = fm.tmb(f); i = '<div class="elfinder-cwd-icon elfinder-cwd-icon-drag '+fm.mime2class(mime)+' ui-corner-all"/>'; if (tmb) { i = jQuery(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML; } return i; }, l, geturl = []; p.trigger(evtSelect); trigger(); jQuery.each(selectedFiles, function(v){ var file = fm.file(v), furl = file.url; if (file && file.mime !== 'directory') { if (!furl) { furl = fm.url(file.hash); } else if (furl == '1') { geturl.push(v); return true; } if (furl) { furl = fm.convAbsUrl(furl); files.push(v); jQuery('<a>').attr('href', furl).text(furl).appendTo(elm); url += furl + "\n"; if (!durl) { durl = file.mime + ':' + file.name + ':' + furl; } if (!murl) { murl = furl + "\n" + file.name; } } } }); if (geturl.length) { jQuery.each(geturl, function(i, v){ var rfile = fm.file(v); rfile.url = ''; fm.request({ data : {cmd : 'url', target : v}, notify : {type : 'url', cnt : 1}, preventDefault : true }) .always(function(data) { rfile.url = data.url? data.url : '1'; }); }); return false; } else if (url) { if (dt.setDragImage) { helper = jQuery('<div class="elfinder-drag-helper html5-native"></div>').append(icon(fm.file(files[0]))).appendTo(jQuery(document.body)); if ((l = files.length) > 1) { helper.append(icon(fm.file(files[l-1])) + '<span class="elfinder-drag-num">'+l+'</span>'); } dt.setDragImage(helper.get(0), 50, 47); } dt.effectAllowed = 'copyLink'; dt.setData('DownloadURL', durl); dt.setData('text/x-moz-url', murl); dt.setData('text/uri-list', url); dt.setData('text/plain', url); dt.setData('text/html', elm.html()); dt.setData('elfinderfrom', window.location.href + fm.cwd().hash); dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), ''); } else { return false; } } }) .on('dragend', function(e){ unselectAll({ notrigger: true }); helper && helper.remove(); }) .draggable(fm.draggable); } }) // add hover class to selected file .on(evtSelect, fileSelector, function(e) { var $this = jQuery(this), id = fm.cwdId2Hash($this.attr('id')); if (!selectLock && !$this.hasClass(clDisabled)) { lastSelect = '#'+ this.id; $this.addClass(clSelected).children().addClass(clHover).find('input:checkbox.'+clSelChk).prop('checked', true); if (! selectedFiles[id]) { selectedFiles[id] = true; } // will be selected next selectedNext = cwd.find('[id].'+clSelected+':last').next(); } }) // remove hover class from unselected file .on(evtUnselect, fileSelector, function(e) { var $this = jQuery(this), id = fm.cwdId2Hash($this.attr('id')); if (!selectLock) { $this.removeClass(clSelected).children().removeClass(clHover).find('input:checkbox.'+clSelChk).prop('checked', false); if (cwd.hasClass('elfinder-cwd-allselected')) { selectCheckbox && selectAllCheckbox.children('input').prop('checked', false); cwd.removeClass('elfinder-cwd-allselected'); } selectedFiles[id] && delete selectedFiles[id]; } }) // disable files wich removing or moving .on(evtDisable, fileSelector, function() { var $this = jQuery(this).removeClass(clHover+' '+clSelected).addClass(clDisabled), child = $this.children(), target = (list ? $this : child.find('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename')); child.removeClass(clHover+' '+clSelected); $this.hasClass(clDroppable) && $this.droppable('disable'); target.hasClass(clDraggable) && target.draggable('disable'); }) // if any files was not removed/moved - unlock its .on(evtEnable, fileSelector, function() { var $this = jQuery(this).removeClass(clDisabled), target = list ? $this : $this.children('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename'); $this.hasClass(clDroppable) && $this.droppable('enable'); target.hasClass(clDraggable) && target.draggable('enable'); }) .on('scrolltoview', fileSelector, function(e, data) { scrollToView(jQuery(this), (data && typeof data.blink !== 'undefined')? data.blink : true); }) .on('mouseenter.'+fm.namespace+' mouseleave.'+fm.namespace, fileSelector, function(e) { var enter = (e.type === 'mouseenter'); if (enter && (scrolling || fm.UA.Mobile)) { return; } fm.trigger('hover', {hash : fm.cwdId2Hash(jQuery(this).attr('id')), type : e.type}); jQuery(this).toggleClass(clHover, (e.type == 'mouseenter')); }) // for file contextmenu .on('mouseenter.'+fm.namespace+' mouseleave.'+fm.namespace, '.elfinder-cwd-file-wrapper,.elfinder-cwd-filename', function(e) { var enter = (e.type === 'mouseenter'); if (enter && scrolling) { return; } jQuery(this).closest(fileSelector).children('.elfinder-cwd-file-wrapper,.elfinder-cwd-filename').toggleClass(clActive, (e.type == 'mouseenter')); }) .on('contextmenu.'+fm.namespace, function(e) { var file = jQuery(e.target).closest(fileSelector); if (file.get(0) === e.target && !selectedFiles[fm.cwdId2Hash(file.get(0).id)]) { return; } // now filename editing if (file.find('input:text,textarea').length) { e.stopPropagation(); return; } if (file.length && (e.target.nodeName != 'TD' || selectedFiles[fm.cwdId2Hash(file.get(0).id)])) { e.stopPropagation(); e.preventDefault(); if (!file.hasClass(clDisabled) && !wrapper.data('touching')) { if (!file.hasClass(clSelected)) { unselectAll({ notrigger: true }); file.trigger(evtSelect); trigger(); } fm.trigger('contextmenu', { 'type' : 'files', 'targets' : fm.selected(), 'x' : e.pageX, 'y' : e.pageY }); } } }) // unselect all on cwd click .on('click.'+fm.namespace, function(e) { if (e.target === this && ! cwd.data('longtap')) { !e.shiftKey && !e.ctrlKey && !e.metaKey && unselectAll(); } }) // prepend fake file/dir .on('create.'+fm.namespace, function(e, f) { var parent = list ? cwd.find('tbody') : cwd, p = parent.find('.elfinder-cwd-parent'), lock = f.move || false, file = jQuery(itemhtml(f)).addClass(clTmp), selected = fm.selected(); if (selected.length) { lock && fm.trigger('lockfiles', {files: selected}); } else { unselectAll(); } if (p.length) { p.after(file); } else { parent.prepend(file); } setColwidth(); wrapper.scrollTop(0).scrollLeft(0); }) // unselect all selected files .on('unselectall', unselectAll) .on('selectfile', function(e, id) { fm.cwdHash2Elm(id).trigger(evtSelect); trigger(); }) .on('colwidth', function() { if (list) { cwd.find('table').css('table-layout', '') .find('td').css('width', ''); fixTableHeader({fitWidth: true}); fm.storage('cwdColWidth', colWidth = null); } }) .on('iconpref', function(e, data) { cwd.removeClass(function(i, cName) { return (cName.match(/\belfinder-cwd-size\S+/g) || []).join(' '); }); iconSize = data? (parseInt(data.size) || 0) : 0; if (!list) { if (iconSize > 0) { cwd.addClass('elfinder-cwd-size' + iconSize); } if (bufferExt.renderd) { requestAnimationFrame(function() { itemBoxSize.icons = {}; bufferExt.hpi = null; bottomMarkerShow(cwd, bufferExt.renderd); wrapperRepaint(); }); } } }) // Change icon size with mouse wheel event .on('onwheel' in document ? 'wheel' : 'mousewheel', function(e) { var tm, size, delta; if (!list && ((e.ctrlKey && !e.metaKey) || (!e.ctrlKey && e.metaKey))) { e.stopPropagation(); e.preventDefault(); tm = cwd.data('wheelTm'); if (typeof tm !== 'undefined') { clearTimeout(tm); cwd.data('wheelTm', setTimeout(function() { cwd.removeData('wheelTm'); }, 200)); } else { cwd.data('wheelTm', false); size = iconSize || 0; delta = e.originalEvent.deltaY ? e.originalEvent.deltaY : -(e.originalEvent.wheelDelta); if (delta > 0) { if (iconSize > 0) { size = iconSize - 1; } } else { if (iconSize < options.iconsView.sizeMax) { size = iconSize + 1; } } if (size !== iconSize) { fm.storage('iconsize', size); cwd.trigger('iconpref', {size: size}); } } } }), wrapper = jQuery('<div class="elfinder-cwd-wrapper"/>') // make cwd itself droppable for folders from nav panel .droppable(Object.assign({}, droppable, {autoDisable: false})) .on('contextmenu.'+fm.namespace, wrapperContextMenu.contextmenu) .on('touchstart.'+fm.namespace, wrapperContextMenu.touchstart) .on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, wrapperContextMenu.touchend) .on('click.'+fm.namespace, wrapperContextMenu.click) .on('scroll.'+fm.namespace, function() { if (! scrolling) { cwd.data('selectable') && cwd.selectable('disable'); wrapper.trigger(scrollStartEvent); } scrolling = true; bufferExt.scrtm && cancelAnimationFrame(bufferExt.scrtm); if (bufferExt.scrtm && Math.abs((bufferExt.scrolltop || 0) - (bufferExt.scrolltop = (this.scrollTop || jQuery(this).scrollTop()))) < 5) { bufferExt.scrtm = 0; wrapper.trigger(scrollEvent); } bufferExt.scrtm = requestAnimationFrame(function() { bufferExt.scrtm = 0; wrapper.trigger(scrollEvent); }); }) .on(scrollEvent, function() { scrolling = false; wrapperRepaint(); }), bottomMarker = jQuery('<div> </div>') .css({position: 'absolute', width: '1px', height: '1px'}) .hide(), selectAllCheckbox = selectCheckbox? jQuery('<div class="elfinder-cwd-selectall"><input type="checkbox"/></div>') .attr('title', fm.i18n('selectall')) .on('touchstart mousedown click', function(e) { e.stopPropagation(); e.preventDefault(); if (jQuery(this).data('pending') || e.type === 'click') { return false; } selectAllCheckbox.data('pending', true); if (cwd.hasClass('elfinder-cwd-allselected')) { selectAllCheckbox.find('input').prop('checked', false); requestAnimationFrame(function() { unselectAll(); }); } else { selectAll(); } }) : jQuery(), restm = null, resize = function(init) { var initHeight = function() { if (typeof bufferExt.renderd !== 'undefined') { var h = 0; wrapper.siblings('div.elfinder-panel:visible').each(function() { h += jQuery(this).outerHeight(true); }); wrapper.height(wz.height() - h - wrapper._padding); } }; init && initHeight(); restm && cancelAnimationFrame(restm); restm = requestAnimationFrame(function(){ !init && initHeight(); var wph, cwdoh; // fix cwd height if it less then wrapper cwd.css('height', 'auto'); wph = wrapper[0].clientHeight - parseInt(wrapper.css('padding-top')) - parseInt(wrapper.css('padding-bottom')) - parseInt(cwd.css('margin-top')), cwdoh = cwd.outerHeight(true); if (cwdoh < wph) { cwd.height(wph); } }); list && ! colResizing && (init? wrapper.trigger('resize.fixheader') : fixTableHeader()); wrapperRepaint(); }, // elfinder node parent = jQuery(this).parent().on('resize', resize), // workzone node wz = parent.children('.elfinder-workzone').append(wrapper.append(this).append(bottomMarker)), // message board mBoard = jQuery('<div class="elfinder-cwd-message-board"/>').insertAfter(cwd), // Volume expires vExpires = jQuery('<div class="elfinder-cwd-expires" />'), vExpiresTm, showVolumeExpires = function() { var remain, sec, int; vExpiresTm && clearTimeout(vExpiresTm); if (curVolId && fm.volumeExpires[curVolId]) { sec = fm.volumeExpires[curVolId] - ((+new Date()) / 1000); int = (sec % 60) + 0.1; remain = Math.floor(sec / 60); vExpires.html(fm.i18n(['minsLeft', remain])).show(); if (remain) { vExpiresTm = setTimeout(showVolumeExpires, int * 1000); } } }, // each item box size itemBoxSize = { icons : {}, list : {} }, // has UI tree hasUiTree, // Icon size of icons view iconSize, // Current volume id curVolId, winScrTm; // IE < 11 not support CSS `pointer-events: none` if (!fm.UA.ltIE10) { mBoard.append(jQuery('<div class="elfinder-cwd-trash" />').html(fm.i18n('volume_Trash'))) .append(vExpires); } // setup by options replacement = Object.assign(replacement, options.replacement || {}); try { colWidth = fm.storage('cwdColWidth')? fm.storage('cwdColWidth') : null; } catch(e) { colWidth = null; } // setup costomCols fm.bind('columnpref', function(e) { var opts = e.data || {}; if (customCols = fm.storage('cwdCols')) { customCols = jQuery.grep(customCols, function(n) { return (options.listView.columns.indexOf(n) !== -1)? true : false; }); if (options.listView.columns.length > customCols.length) { jQuery.each(options.listView.columns, function(i, n) { if (customCols.indexOf(n) === -1) { customCols.push(n); } }); } } else { customCols = options.listView.columns; } // column names array that hidden var columnhides = fm.storage('columnhides') || null; if (columnhides && Object.keys(columnhides).length) customCols = jQuery.grep(customCols, function(n) { return columnhides[n]? false : true; }); // make template with customCols templates.row = makeTemplateRow(); // repaint if need it list && opts.repaint && content(); }).trigger('columnpref'); if (mobile) { // for iOS5 bug jQuery('body').on('touchstart touchmove touchend', function(e){}); } selectCheckbox && cwd.addClass('elfinder-has-checkbox'); jQuery(window).on('scroll.'+fm.namespace, function() { winScrTm && cancelAnimationFrame(winScrTm); winScrTm = requestAnimationFrame(function() { wrapper.trigger(scrollEvent); }); }); jQuery(document).on('keydown.'+fm.namespace, function(e) { if (e.keyCode == jQuery.ui.keyCode.ESCAPE) { if (! fm.getUI().find('.ui-widget:visible').length) { unselectAll(); } } }); fm .one('init', function(){ var style = document.createElement('style'), sheet, node, base, resizeTm, iconSize, i = 0; if (document.head) { document.head.appendChild(style); sheet = style.sheet; sheet.insertRule('.elfinder-cwd-wrapper-empty .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyFolder')+'" }', i++); sheet.insertRule('.elfinder-cwd-wrapper-empty .native-droppable .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyFolder'+(mobile? 'LTap' : 'Drop'))+'" }', i++); sheet.insertRule('.elfinder-cwd-wrapper-empty .ui-droppable-disabled .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyFolder')+'" }', i++); sheet.insertRule('.elfinder-cwd-wrapper-empty.elfinder-search-result .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptySearch')+'" }', i++); sheet.insertRule('.elfinder-cwd-wrapper-empty.elfinder-search-result.elfinder-incsearch-result .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyIncSearch')+'" }', i++); sheet.insertRule('.elfinder-cwd-wrapper-empty.elfinder-search-result.elfinder-letsearch-result .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyLetSearch')+'" }', i++); } if (iconSize = fm.storage('iconsize') || 0) { cwd.trigger('iconpref', {size: iconSize}); } if (! mobile) { fm.one('open', function() { sheet && fm.zIndex && sheet.insertRule('.ui-selectable-helper{z-index:'+fm.zIndex+';}', i++); }); base = jQuery('<div style="position:absolute"/>'); node = fm.getUI(); node.on('resize', function(e, data) { var offset; e.preventDefault(); e.stopPropagation(); if (data && data.fullscreen) { offset = node.offset(); if (data.fullscreen === 'on') { base.css({top:offset.top * -1 , left:offset.left * -1 }).appendTo(node); selectableOption.appendTo = base; } else { base.detach(); selectableOption.appendTo = 'body'; } cwd.data('selectable') && cwd.selectable('option', {appendTo : selectableOption.appendTo}); } }); } hasUiTree = fm.getUI('tree').length; }) .bind('enable', function() { resize(); }) .bind('request.open', function() { bufferExt.getTmbs = []; }) .one('open', function() { if (fm.maxTargets) { tmbNum = Math.min(fm.maxTargets, tmbNum); } }) .bind('open add remove searchend', function() { var phash = fm.cwd().hash, type = this.type; if (type === 'open' || type === 'searchend' || fm.searchStatus.state < 2) { cwdHashes = jQuery.map(fm.files(phash), function(f) { return f.hash; }); fm.trigger('cwdhasheschange', cwdHashes); } if (type === 'open') { var inTrash = function() { var isIn = false; jQuery.each(cwdParents, function(i, h) { if (fm.trashes[h]) { isIn = true; return false; } }); return isIn; }, req = phash? (! fm.file(phash) || hasUiTree? (! hasUiTree? fm.request({ data: { cmd : 'parents', target : fm.cwd().hash }, preventFail : true }) : (function() { var dfd = jQuery.Deferred(); fm.one('treesync', function(e) { e.data.always(function() { dfd.resolve(); }); }); return dfd; })() ) : null ) : null, cwdObj = fm.cwd(); // add/remove volume id class if (cwdObj.volumeid !== curVolId) { vExpires.empty().hide(); if (curVolId) { wrapper.removeClass('elfinder-cwd-wrapper-' + curVolId); } curVolId = cwdObj.volumeid; showVolumeExpires(); wrapper.addClass('elfinder-cwd-wrapper-' + curVolId); } // add/remove trash class jQuery.when(req).done(function() { cwdParents = fm.parents(cwdObj.hash); wrapper[inTrash()? 'addClass':'removeClass']('elfinder-cwd-wrapper-trash'); }); incHashes = void 0; unselectAll({ notrigger: true }); content(); } }) .bind('search', function(e) { cwdHashes = jQuery.map(e.data.files, function(f) { return f.hash; }); fm.trigger('cwdhasheschange', cwdHashes); incHashes = void 0; fm.searchStatus.ininc = false; content(); fm.autoSync('stop'); }) .bind('searchend', function(e) { if (query || incHashes) { query = ''; if (incHashes) { fm.trigger('incsearchend', e.data); } else { if (!e.data || !e.data.noupdate) { content(); } } } fm.autoSync(); }) .bind('searchstart', function(e) { unselectAll(); query = e.data.query; }) .bind('incsearchstart', function(e) { selectedFiles = {}; fm.lazy(function() { // incremental search var regex, q, fst = ''; q = query = e.data.query || ''; if (q) { if (q.substr(0,1) === '/') { q = q.substr(1); fst = '^'; } regex = new RegExp(fst + q.replace(/([\\*\;\.\?\[\]\{\}\(\)\^\$\-\|])/g, '\\$1'), 'i'); incHashes = jQuery.grep(cwdHashes, function(hash) { var file = fm.file(hash); return (file && (file.name.match(regex) || (file.i18 && file.i18.match(regex))))? true : false; }); fm.trigger('incsearch', { hashes: incHashes, query: q }) .searchStatus.ininc = true; content(); fm.autoSync('stop'); } else { fm.trigger('incsearchend'); } }); }) .bind('incsearchend', function(e) { query = ''; fm.searchStatus.ininc = false; incHashes = void 0; if (!e.data || !e.data.noupdate) { content(); } fm.autoSync(); }) .bind('sortchange', function() { var lastScrollLeft = wrapper.scrollLeft(), allsel = cwd.hasClass('elfinder-cwd-allselected'); content(); fm.one('cwdrender', function() { wrapper.scrollLeft(lastScrollLeft); if (allsel) { selectedFiles = fm.arrayFlip(incHashes || cwdHashes, true); } (allsel || Object.keys(selectedFiles).length) && trigger(); }); }) .bind('viewchange', function() { var l = fm.storage('view') == 'list', allsel = cwd.hasClass('elfinder-cwd-allselected'); if (l != list) { list = l; fm.viewType = list? 'list' : 'icons'; if (iconSize) { fm.one('cwdinit', function() { cwd.trigger('iconpref', {size: iconSize}); }); } content(); resize(); if (allsel) { cwd.addClass('elfinder-cwd-allselected'); selectAllCheckbox.find('input').prop('checked', true); } Object.keys(selectedFiles).length && trigger(); } }) .bind('wzresize', function() { var place = list ? cwd.find('tbody') : cwd, cwdOffset; resize(true); if (bufferExt.hpi) { bottomMarkerShow(place, place.find('[id]').length); } cwdOffset = cwd.offset(); wz.data('rectangle', Object.assign( { width: wz.width(), height: wz.height(), cwdEdge: (fm.direction === 'ltr')? cwdOffset.left : cwdOffset.left + cwd.width() }, wz.offset()) ); bufferExt.itemH = (list? place.find('tr:first') : place.find('[id]:first')).outerHeight(true); }) .bind('changeclipboard', function(e) { clipCuts = {}; if (e.data && e.data.clipboard && e.data.clipboard.length) { jQuery.each(e.data.clipboard, function(i, f) { if (f.cut) { clipCuts[f.hash] = true; } }); } }) .bind('resMixinMake', function() { setColwidth(); }) .bind('tmbreload', function(e) { var imgs = {}, files = (e.data && e.data.files)? e.data.files : null; jQuery.each(files, function(i, f) { if (f.tmb && f.tmb != '1') { imgs[f.hash] = f.tmb; } }); if (Object.keys(imgs).length) { attachThumbnails(imgs, true); } }) .add(function(e) { var regex = query? new RegExp(query.replace(/([\\*\;\.\?\[\]\{\}\(\)\^\$\-\|])/g, '\\$1'), 'i') : null, mime = fm.searchStatus.mime, inSearch = fm.searchStatus.state > 1, phash = inSearch && fm.searchStatus.target? fm.searchStatus.target : fm.cwd().hash, curPath = fm.path(phash), inTarget = function(f) { var res, parents, path; res = (f.phash === phash); if (!res && inSearch) { path = f.path || fm.path(f.hash); res = (curPath && path.indexOf(curPath) === 0); if (! res && fm.searchStatus.mixed) { res = jQuery.grep(fm.searchStatus.mixed, function(vid) { return f.hash.indexOf(vid) === 0? true : false; }).length? true : false; } } if (res && inSearch) { if (mime) { res = (f.mime.indexOf(mime) === 0); } else { res = (f.name.match(regex) || (f.i18 && f.i18.match(regex)))? true : false; } } return res; }, files = jQuery.grep(e.data.added || [], function(f) { return inTarget(f)? true : false ;}); add(files); if (fm.searchStatus.state === 2) { jQuery.each(files, function(i, f) { if (jQuery.inArray(f.hash, cwdHashes) === -1) { cwdHashes.push(f.hash); } }); fm.trigger('cwdhasheschange', cwdHashes); } list && resize(); wrapper.trigger(scrollEvent); }) .change(function(e) { var phash = fm.cwd().hash, sel = fm.selected(), files, added; if (query) { jQuery.each(e.data.changed || [], function(i, file) { if (fm.cwdHash2Elm(file.hash).length) { remove([file.hash]); add([file], 'change'); jQuery.inArray(file.hash, sel) !== -1 && selectFile(file.hash); added = true; } }); } else { jQuery.each(jQuery.grep(e.data.changed || [], function(f) { return f.phash == phash ? true : false; }), function(i, file) { if (fm.cwdHash2Elm(file.hash).length) { remove([file.hash]); add([file], 'change'); jQuery.inArray(file.hash, sel) !== -1 && selectFile(file.hash); added = true; } }); } if (added) { fm.trigger('cwdhasheschange', cwdHashes); list && resize(); wrapper.trigger(scrollEvent); } trigger(); }) .remove(function(e) { var place = list ? cwd.find('tbody') : cwd; remove(e.data.removed || []); trigger(); if (buffer.length < 1 && place.children(fileSelector).length < 1) { wz.addClass('elfinder-cwd-wrapper-empty'); selectCheckbox && selectAllCheckbox.find('input').prop('checked', false); bottomMarker.hide(); wrapper.off(scrollEvent, render); resize(); } else { bottomMarkerShow(place); wrapper.trigger(scrollEvent); } }) // select dragged file if no selected, disable selectable .dragstart(function(e) { var target = jQuery(e.data.target), oe = e.data.originalEvent; if (target.hasClass(clFile)) { if (!target.hasClass(clSelected)) { !(oe.ctrlKey || oe.metaKey || oe.shiftKey) && unselectAll({ notrigger: true }); target.trigger(evtSelect); trigger(); } } cwd.removeClass(clDisabled).data('selectable') && cwd.selectable('disable'); selectLock = true; }) // enable selectable .dragstop(function() { cwd.data('selectable') && cwd.selectable('enable'); selectLock = false; }) .bind('lockfiles unlockfiles selectfiles unselectfiles', function(e) { var events = { lockfiles : evtDisable , unlockfiles : evtEnable , selectfiles : evtSelect, unselectfiles : evtUnselect }, event = events[e.type], files = e.data.files || [], l = files.length, helper = e.data.helper || jQuery(), parents, ctr, add; if (l > 0) { parents = fm.parents(files[0]); } if (event === evtSelect || event === evtUnselect) { add = (event === evtSelect), jQuery.each(files, function(i, hash) { var all = cwd.hasClass('elfinder-cwd-allselected'); if (! selectedFiles[hash]) { add && (selectedFiles[hash] = true); } else { if (all) { selectCheckbox && selectAllCheckbox.children('input').prop('checked', false); cwd.removeClass('elfinder-cwd-allselected'); all = false; } ! add && delete selectedFiles[hash]; } }); } if (!helper.data('locked')) { while (l--) { try { fm.cwdHash2Elm(files[l]).trigger(event); } catch(e) {} } ! e.data.inselect && trigger(); } if (wrapper.data('dropover') && parents.indexOf(wrapper.data('dropover')) !== -1) { ctr = e.type !== 'lockfiles'; helper.toggleClass('elfinder-drag-helper-plus', ctr); wrapper.toggleClass(clDropActive, ctr); } }) // select new files after some actions .bind('mkdir mkfile duplicate upload rename archive extract paste multiupload', function(e) { if (e.type == 'upload' && e.data._multiupload) return; var phash = fm.cwd().hash, files; unselectAll({ notrigger: true }); jQuery.each((e.data.added || []).concat(e.data.changed || []), function(i, file) { file && file.phash == phash && selectFile(file.hash); }); trigger(); }) .shortcut({ pattern :'ctrl+a', description : 'selectall', callback : selectAll }) .shortcut({ pattern :'ctrl+shift+i', description : 'selectinvert', callback : selectInvert }) .shortcut({ pattern : 'left right up down shift+left shift+right shift+up shift+down', description : 'selectfiles', type : 'keydown' , //fm.UA.Firefox || fm.UA.Opera ? 'keypress' : 'keydown', callback : function(e) { select(e.keyCode, e.shiftKey); } }) .shortcut({ pattern : 'home', description : 'selectffile', callback : function(e) { unselectAll({ notrigger: true }); scrollToView(cwd.find('[id]:first').trigger(evtSelect)); trigger(); } }) .shortcut({ pattern : 'end', description : 'selectlfile', callback : function(e) { unselectAll({ notrigger: true }); scrollToView(cwd.find('[id]:last').trigger(evtSelect)) ; trigger(); } }) .shortcut({ pattern : 'page_up', description : 'pageTurning', callback : function(e) { if (bufferExt.itemH) { wrapper.scrollTop( Math.round( wrapper.scrollTop() - (Math.floor((wrapper.height() + (list? bufferExt.itemH * -1 : 16)) / bufferExt.itemH)) * bufferExt.itemH ) ); } } }).shortcut({ pattern : 'page_down', description : 'pageTurning', callback : function(e) { if (bufferExt.itemH) { wrapper.scrollTop( Math.round( wrapper.scrollTop() + (Math.floor((wrapper.height() + (list? bufferExt.itemH * -1 : 16)) / bufferExt.itemH)) * bufferExt.itemH ) ); } } }); }); // fm.timeEnd('cwdLoad') return this; }; /* * File: /js/ui/dialog.js */ /** * @class elFinder dialog * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfinderdialog = function(opts, fm) { var platformWin = (window.navigator.platform.indexOf('Win') != -1), delta = {}, syncSize = { enabled: false, width: false, height: false, defaultSize: null }, fitSize = function(dialog) { var opts, node; if (syncSize.enabled) { node = fm.options.dialogContained? elfNode : jQuery(window); opts = { maxWidth : syncSize.width? node.width() - delta.width : null, maxHeight: syncSize.height? node.height() - delta.height : null }; Object.assign(restoreStyle, opts); dialog.css(opts).trigger('resize'); if (dialog.data('hasResizable') && (dialog.resizable('option', 'maxWidth') < opts.maxWidth || dialog.resizable('option', 'maxHeight') < opts.maxHeight)) { dialog.resizable('option', opts); } } }, syncFunc = function(e) { var dialog = e.data; syncTm && cancelAnimationFrame(syncTm); syncTm = requestAnimationFrame(function() { var opts, offset; if (syncSize.enabled) { fitSize(dialog); } }); }, checkEditing = function() { var cldialog = 'elfinder-dialog', dialogs = elfNode.children('.' + cldialog + '.' + fm.res('class', 'editing') + ':visible'); fm[dialogs.length? 'disable' : 'enable'](); }, propagationEvents = {}, syncTm, dialog, elfNode, restoreStyle; if (fm && fm.ui) { elfNode = fm.getUI(); } else { elfNode = this.closest('.elfinder'); if (! fm) { fm = elfNode.elfinder('instance'); } } if (typeof opts === 'string') { if ((dialog = this.closest('.ui-dialog')).length) { if (opts === 'open') { if (dialog.css('display') === 'none') { // Need dialog.show() and hide() to detect elements size in open() callbacks dialog.trigger('posinit').show().trigger('open').hide(); dialog.fadeIn(120, function() { fm.trigger('dialogopened', {dialog: dialog}); }); } } else if (opts === 'close' || opts === 'destroy') { dialog.stop(true); if (dialog.is(':visible') || elfNode.is(':hidden')) { dialog.trigger('close'); fm.trigger('dialogclosed', {dialog: dialog}); } if (opts === 'destroy') { dialog.remove(); fm.trigger('dialogremoved', {dialog: dialog}); } } else if (opts === 'toTop') { dialog.trigger('totop'); fm.trigger('dialogtotoped', {dialog: dialog}); } else if (opts === 'posInit') { dialog.trigger('posinit'); fm.trigger('dialogposinited', {dialog: dialog}); } else if (opts === 'tabstopsInit') { dialog.trigger('tabstopsInit'); fm.trigger('dialogtabstopsinited', {dialog: dialog}); } else if (opts === 'checkEditing') { checkEditing(); } } return this; } opts = Object.assign({}, jQuery.fn.elfinderdialog.defaults, opts); if (opts.allowMinimize && opts.allowMinimize === 'auto') { opts.allowMinimize = this.find('textarea,input').length? true : false; } opts.openMaximized = opts.allowMinimize && opts.openMaximized; if (opts.headerBtnPos && opts.headerBtnPos === 'auto') { opts.headerBtnPos = platformWin? 'right' : 'left'; } if (opts.headerBtnOrder && opts.headerBtnOrder === 'auto') { opts.headerBtnOrder = platformWin? 'close:maximize:minimize' : 'close:minimize:maximize'; } if (opts.modal && opts.allowMinimize) { opts.allowMinimize = false; } if (fm.options.dialogContained) { syncSize.width = syncSize.height = syncSize.enabled = true; } else { syncSize.width = (opts.maxWidth === 'window'); syncSize.height = (opts.maxHeight === 'window'); if (syncSize.width || syncSize.height) { syncSize.enabled = true; } } propagationEvents = fm.arrayFlip(opts.propagationEvents, true); this.filter(':not(.ui-dialog-content)').each(function() { var self = jQuery(this).addClass('ui-dialog-content ui-widget-content'), clactive = 'elfinder-dialog-active', cldialog = 'elfinder-dialog', clnotify = 'elfinder-dialog-notify', clhover = 'ui-state-hover', cltabstop = 'elfinder-tabstop', cl1stfocus = 'elfinder-focus', clmodal = 'elfinder-dialog-modal', id = parseInt(Math.random()*1000000), titlebar = jQuery('<div class="ui-dialog-titlebar ui-widget-header ui-corner-top ui-helper-clearfix"><span class="elfinder-dialog-title">'+opts.title+'</span></div>'), buttonset = jQuery('<div class="ui-dialog-buttonset"/>'), buttonpane = jQuery('<div class=" ui-helper-clearfix ui-dialog-buttonpane ui-widget-content"/>') .append(buttonset), btnWidth = 0, btnCnt = 0, tabstops = jQuery(), evCover = jQuery('<div style="width:100%;height:100%;position:absolute;top:0px;left:0px;"/>').hide(), numberToTel = function() { if (opts.optimizeNumber) { dialog.find('input[type=number]').each(function() { jQuery(this).attr('inputmode', 'numeric'); jQuery(this).attr('pattern', '[0-9]*'); }); } }, tabstopsInit = function() { tabstops = dialog.find('.'+cltabstop); if (tabstops.length) { tabstops.attr('tabindex', '-1'); if (! tabstops.filter('.'+cl1stfocus).length) { buttonset.children('.'+cltabstop+':'+(platformWin? 'first' : 'last')).addClass(cl1stfocus); } } }, tabstopNext = function(cur) { var elms = tabstops.filter(':visible:enabled'), node = cur? null : elms.filter('.'+cl1stfocus+':first'); if (! node || ! node.length) { node = elms.first(); } if (cur) { jQuery.each(elms, function(i, elm) { if (elm === cur && elms[i+1]) { node = elms.eq(i+1); return false; } }); } return node; }, tabstopPrev = function(cur) { var elms = tabstops.filter(':visible:enabled'), node = elms.last(); jQuery.each(elms, function(i, elm) { if (elm === cur && elms[i-1]) { node = elms.eq(i-1); return false; } }); return node; }, makeHeaderBtn = function() { jQuery.each(opts.headerBtnOrder.split(':').reverse(), function(i, v) { headerBtns[v] && headerBtns[v](); }); if (platformWin) { titlebar.children('.elfinder-titlebar-button').addClass('elfinder-titlebar-button-right'); } }, headerBtns = { close: function() { titlebar.prepend(jQuery('<span class="ui-widget-header ui-dialog-titlebar-close ui-corner-all elfinder-titlebar-button"><span class="ui-icon ui-icon-closethick"/></span>') .on('mousedown', function(e) { e.preventDefault(); e.stopPropagation(); self.elfinderdialog('close'); }) ); }, maximize: function() { if (opts.allowMaximize) { dialog.on('resize', function(e, data) { var full, elm; e.preventDefault(); e.stopPropagation(); if (data && data.maximize) { elm = titlebar.find('.elfinder-titlebar-full'); full = (data.maximize === 'on'); elm.children('span.ui-icon') .toggleClass('ui-icon-plusthick', ! full) .toggleClass('ui-icon-arrowreturnthick-1-s', full); if (full) { try { dialog.hasClass('ui-draggable') && dialog.draggable('disable'); dialog.hasClass('ui-resizable') && dialog.resizable('disable'); } catch(e) {} self.css('width', '100%').css('height', dialog.height() - dialog.children('.ui-dialog-titlebar').outerHeight(true) - buttonpane.outerHeight(true)); } else { self.attr('style', elm.data('style')); elm.removeData('style'); posCheck(); try { dialog.hasClass('ui-draggable') && dialog.draggable('enable'); dialog.hasClass('ui-resizable') && dialog.resizable('enable'); } catch(e) {} } dialog.trigger('resize', {init: true}); } }); titlebar.prepend(jQuery('<span class="ui-widget-header ui-corner-all elfinder-titlebar-button elfinder-titlebar-full"><span class="ui-icon ui-icon-plusthick"/></span>') .on('mousedown', function(e) { var elm = jQuery(this); e.preventDefault(); e.stopPropagation(); if (!dialog.hasClass('elfinder-maximized') && typeof elm.data('style') === 'undefined') { self.height(self.height()); elm.data('style', self.attr('style') || ''); } fm.toggleMaximize(dialog); typeof(opts.maximize) === 'function' && opts.maximize.call(self[0]); }) ); } }, minimize: function() { var btn, mnode, doffset; if (opts.allowMinimize) { btn = jQuery('<span class="ui-widget-header ui-corner-all elfinder-titlebar-button elfinder-titlebar-minimize"><span class="ui-icon ui-icon-minusthick"/></span>') .on('mousedown', function(e) { var $this = jQuery(this), tray = fm.getUI('bottomtray'), dumStyle = { width: 70, height: 24 }, dum = jQuery('<div/>').css(dumStyle).addClass(dialog.get(0).className + ' elfinder-dialog-minimized'), pos = {}; e.preventDefault(); e.stopPropagation(); if (!dialog.data('minimized')) { // minimize doffset = dialog.data('minimized', true).position(); mnode = dialog.clone().on('mousedown', function() { $this.trigger('mousedown'); }).removeClass('ui-draggable ui-resizable elfinder-frontmost'); tray.append(dum); Object.assign(pos, dum.offset(), dumStyle); dum.remove(); mnode.height(dialog.height()).children('.ui-dialog-content:first').empty(); fm.toHide(dialog.before(mnode)); mnode.children('.ui-dialog-content:first,.ui-dialog-buttonpane,.ui-resizable-handle').remove(); mnode.find('.elfinder-titlebar-minimize,.elfinder-titlebar-full').remove(); mnode.find('.ui-dialog-titlebar-close').on('mousedown', function(e) { e.stopPropagation(); e.preventDefault(); mnode.remove(); dialog.show(); self.elfinderdialog('close'); }); mnode.animate(pos, function() { mnode.attr('style', '') .css({ maxWidth: dialog.width() }) .addClass('elfinder-dialog-minimized') .appendTo(tray); checkEditing(); typeof(opts.minimize) === 'function' && opts.minimize.call(self[0]); }); } else { //restore dialog.removeData('minimized').before(mnode.css(Object.assign({'position': 'absolute'}, mnode.offset()))); fm.toFront(mnode); mnode.animate(Object.assign({ width: dialog.width(), height: dialog.height() }, doffset), function() { dialog.show(); fm.toFront(dialog); mnode.remove(); posCheck(); checkEditing(); dialog.trigger('resize', {init: true}); typeof(opts.minimize) === 'function' && opts.minimize.call(self[0]); }); } }); titlebar.on('dblclick', function(e) { jQuery(this).children('.elfinder-titlebar-minimize').trigger('mousedown'); }).prepend(btn); dialog.on('togleminimize', function() { btn.trigger('mousedown'); }); } } }, dialog = jQuery('<div class="ui-front ui-dialog ui-widget ui-widget-content ui-corner-all ui-draggable std42-dialog touch-punch '+cldialog+' '+opts.cssClass+'"/>') .hide() .append(self) .appendTo(elfNode) .draggable({ containment : fm.options.dialogContained? elfNode : null, handle : '.ui-dialog-titlebar', start : function() { evCover.show(); }, drag : function(e, ui) { var top = ui.offset.top, left = ui.offset.left; if (top < 0) { ui.position.top = ui.position.top - top; } if (left < 0) { ui.position.left = ui.position.left - left; } if (fm.options.dialogContained) { ui.position.top < 0 && (ui.position.top = 0); ui.position.left < 0 && (ui.position.left = 0); } }, stop : function(e, ui) { evCover.hide(); dialog.css({height : opts.height}); self.data('draged', true); } }) .css({ width : opts.width, height : opts.height, minWidth : opts.minWidth, minHeight : opts.minHeight, maxWidth : opts.maxWidth, maxHeight : opts.maxHeight }) .on('touchstart touchmove touchend click dblclick mouseup mouseenter mouseleave mouseout mouseover mousemove', function(e) { // stopPropagation of user action events !propagationEvents[e.type] && e.stopPropagation(); }) .on('mousedown', function(e) { !propagationEvents[e.type] && e.stopPropagation(); requestAnimationFrame(function() { if (dialog.is(':visible') && !dialog.hasClass('elfinder-frontmost')) { toFocusNode = jQuery(':focus'); if (!toFocusNode.length) { toFocusNode = void(0); } dialog.trigger('totop'); } }); }) .on('open', function() { dialog.data('margin-y', self.outerHeight(true) - self.height()); if (syncSize.enabled) { if (opts.height && opts.height !== 'auto') { dialog.trigger('resize', {init: true}); } if (!syncSize.defaultSize) { syncSize.defaultSize = { width: self.width(), height: self.height() }; } fitSize(dialog); dialog.trigger('resize').trigger('posinit'); elfNode.on('resize.'+fm.namespace, dialog, syncFunc); } if (!dialog.hasClass(clnotify)) { elfNode.children('.'+cldialog+':visible:not(.'+clnotify+')').each(function() { var d = jQuery(this), top = parseInt(d.css('top')), left = parseInt(d.css('left')), _top = parseInt(dialog.css('top')), _left = parseInt(dialog.css('left')), ct = Math.abs(top - _top) < 10, cl = Math.abs(left - _left) < 10; if (d[0] != dialog[0] && (ct || cl)) { dialog.css({ top : ct ? (top + 10) : _top, left : cl ? (left + 10) : _left }); } }); } if (dialog.data('modal')) { dialog.addClass(clmodal); fm.getUI('overlay').elfinderoverlay('show'); } dialog.trigger('totop'); opts.openMaximized && fm.toggleMaximize(dialog); fm.trigger('dialogopen', {dialog: dialog}); typeof(opts.open) == 'function' && jQuery.proxy(opts.open, self[0])(); if (opts.closeOnEscape) { jQuery(document).on('keydown.'+id, function(e) { if (e.keyCode == jQuery.ui.keyCode.ESCAPE && dialog.hasClass('elfinder-frontmost')) { self.elfinderdialog('close'); } }); } dialog.hasClass(fm.res('class', 'editing')) && checkEditing(); }) .on('close', function(e) { var dialogs, dfd; if (opts.beforeclose && typeof opts.beforeclose === 'function') { dfd = opts.beforeclose(); if (!dfd || !dfd.promise) { dfd = !dfd? jQuery.Deferred().reject() : jQuery.Deferred().resolve(); } } else { dfd = jQuery.Deferred().resolve(); } dfd.done(function() { syncSize.enabled && elfNode.off('resize.'+fm.namespace, syncFunc); if (opts.closeOnEscape) { jQuery(document).off('keyup.'+id); } if (opts.allowMaximize) { fm.toggleMaximize(dialog, false); } fm.toHide(dialog); dialog.data('modal') && fm.getUI('overlay').elfinderoverlay('hide'); if (typeof(opts.close) == 'function') { jQuery.proxy(opts.close, self[0])(); } if (opts.destroyOnClose && dialog.parent().length) { dialog.hide().remove(); } // get focus to next dialog dialogs = elfNode.children('.'+cldialog+':visible'); dialog.hasClass(fm.res('class', 'editing')) && checkEditing(); }); }) .on('totop frontmost', function() { var s = fm.storage('autoFocusDialog'); dialog.data('focusOnMouseOver', s? (s > 0) : fm.options.uiOptions.dialog.focusOnMouseOver); if (dialog.data('minimized')) { titlebar.children('.elfinder-titlebar-minimize').trigger('mousedown'); } if (!dialog.data('modal') && fm.getUI('overlay').is(':visible')) { fm.getUI('overlay').before(dialog); } else { fm.toFront(dialog); } elfNode.children('.'+cldialog+':not(.'+clmodal+')').removeClass(clactive); dialog.addClass(clactive); ! fm.UA.Mobile && (toFocusNode || tabstopNext()).trigger('focus'); toFocusNode = void(0); }) .on('posinit', function() { var css = opts.position, nodeOffset, minTop, minLeft, outerSize, win, winSize, nodeFull; if (dialog.hasClass('elfinder-maximized')) { return; } if (! css && ! dialog.data('resizing')) { nodeFull = elfNode.hasClass('elfinder-fullscreen'); dialog.css(nodeFull? { maxWidth : '100%', maxHeight : '100%', overflow : 'auto' } : restoreStyle); if (fm.UA.Mobile && !nodeFull && dialog.data('rotated') === fm.UA.Rotated) { return; } dialog.data('rotated', fm.UA.Rotated); win = jQuery(window); nodeOffset = elfNode.offset(); outerSize = { width : dialog.outerWidth(true), height: dialog.outerHeight(true) }; outerSize.right = nodeOffset.left + outerSize.width; outerSize.bottom = nodeOffset.top + outerSize.height; winSize = { scrLeft: win.scrollLeft(), scrTop : win.scrollTop(), width : win.width(), height : win.height() }; winSize.right = winSize.scrLeft + winSize.width; winSize.bottom = winSize.scrTop + winSize.height; if (fm.options.dialogContained || nodeFull) { minTop = 0; minLeft = 0; } else { minTop = nodeOffset.top * -1 + winSize.scrTop; minLeft = nodeOffset.left * -1 + winSize.scrLeft; } css = { top : outerSize.height >= winSize.height? minTop : Math.max(minTop, parseInt((elfNode.height() - outerSize.height)/2 - 42)), left : outerSize.width >= winSize.width ? minLeft : Math.max(minLeft, parseInt((elfNode.width() - outerSize.width)/2)) }; if (outerSize.right + css.left > winSize.right) { css.left = Math.max(minLeft, winSize.right - outerSize.right); } if (outerSize.bottom + css.top > winSize.bottom) { css.top = Math.max(minTop, winSize.bottom - outerSize.bottom); } } if (opts.absolute) { css.position = 'absolute'; } css && dialog.css(css); }) .on('resize', function(e, data) { var oh = 0, init = data && data.init, h, minH; if ((data && (data.minimize || data.maxmize)) || dialog.data('minimized')) { return; } e.stopPropagation(); e.preventDefault(); dialog.children('.ui-widget-header,.ui-dialog-buttonpane').each(function() { oh += jQuery(this).outerHeight(true); }); if (!init && syncSize.enabled && !e.originalEvent && !dialog.hasClass('elfinder-maximized')) { h = Math.min(syncSize.defaultSize.height, Math.max(parseInt(dialog.css('max-height')), parseInt(dialog.css('min-height'))) - oh - dialog.data('margin-y')); } else { h = dialog.height() - oh - dialog.data('margin-y'); } self.height(h); if (init) { return; } posCheck(); minH = self.height(); minH = (h < minH)? (minH + oh + dialog.data('margin-y')) : opts.minHeight; dialog.css('min-height', minH); dialog.data('hasResizable') && dialog.resizable('option', { minHeight: minH }); if (typeof(opts.resize) === 'function') { jQuery.proxy(opts.resize, self[0])(e, data); } }) .on('tabstopsInit', tabstopsInit) .on('focus', '.'+cltabstop, function() { jQuery(this).addClass(clhover).parent('label').addClass(clhover); this.id && jQuery(this).parent().find('label[for='+this.id+']').addClass(clhover); }) .on('click', 'select.'+cltabstop, function() { var node = jQuery(this); node.data('keepFocus')? node.removeData('keepFocus') : node.data('keepFocus', true); }) .on('blur', '.'+cltabstop, function() { jQuery(this).removeClass(clhover).removeData('keepFocus').parent('label').removeClass(clhover); this.id && jQuery(this).parent().find('label[for='+this.id+']').removeClass(clhover); }) .on('mouseenter mouseleave', '.'+cltabstop+',label', function(e) { var $this = jQuery(this), labelfor; if (this.nodeName === 'LABEL') { if (!$this.children('.'+cltabstop).length && (!(labelfor = $this.attr('for')) || !jQuery('#'+labelfor).hasClass(cltabstop))) { return; } } if (opts.btnHoverFocus && dialog.data('focusOnMouseOver')) { if (e.type === 'mouseenter' && ! jQuery(':focus').data('keepFocus')) { $this.trigger('focus'); } } else { $this.toggleClass(clhover, e.type == 'mouseenter'); } }) .on('keydown', '.'+cltabstop, function(e) { var $this = jQuery(this), esc, move, moveTo; if ($this.is(':focus')) { esc = e.keyCode === jQuery.ui.keyCode.ESCAPE; if (e.keyCode === jQuery.ui.keyCode.ENTER) { e.preventDefault(); $this.trigger('click'); } else if (((e.keyCode === jQuery.ui.keyCode.TAB) && e.shiftKey) || e.keyCode === jQuery.ui.keyCode.LEFT || e.keyCode == jQuery.ui.keyCode.UP) { move = 'prev'; } else if (e.keyCode === jQuery.ui.keyCode.TAB || e.keyCode == jQuery.ui.keyCode.RIGHT || e.keyCode == jQuery.ui.keyCode.DOWN) { move = 'next'; } if (move && ( ($this.is('textarea') && !(e.ctrlKey || e.metaKey)) || ($this.is('select,span.ui-slider-handle') && e.keyCode !== jQuery.ui.keyCode.TAB) || ($this.is('input:not(:checkbox,:radio)') && (!(e.ctrlKey || e.metaKey) && e.keyCode === jQuery.ui.keyCode[move === 'prev'? 'LEFT':'RIGHT'])) ) ) { e.stopPropagation(); return; } if (!esc) { e.stopPropagation(); } else if ($this.is('input:not(:checkbox,:radio),textarea')) { if ($this.val() !== '') { $this.val(''); e.stopPropagation(); } } if (move) { e.preventDefault(); (move === 'prev'? tabstopPrev : tabstopNext)(this).trigger('focus'); } } }) .data({modal: opts.modal}), posCheck = function() { var node = fm.getUI(), pos; if (node.hasClass('elfinder-fullscreen')) { pos = dialog.position(); dialog.css('top', Math.max(Math.min(Math.max(pos.top, 0), node.height() - 100), 0)); dialog.css('left', Math.max(Math.min(Math.max(pos.left, 0), node.width() - 200), 0)); } }, maxSize, toFocusNode; dialog.prepend(titlebar); makeHeaderBtn(); jQuery.each(opts.buttons, function(name, cb) { var button = jQuery('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only ' +'elfinder-btncnt-'+(btnCnt++)+' ' +cltabstop +'"><span class="ui-button-text">'+name+'</span></button>') .on('click', jQuery.proxy(cb, self[0])); if (cb._cssClass) { button.addClass(cb._cssClass); } if (platformWin) { buttonset.append(button); } else { buttonset.prepend(button); } }); if (buttonset.children().length) { dialog.append(buttonpane); dialog.show(); buttonpane.find('button').each(function(i, btn) { btnWidth += jQuery(btn).outerWidth(true); }); dialog.hide(); btnWidth += 20; if (dialog.width() < btnWidth) { dialog.width(btnWidth); } } dialog.append(evCover); if (syncSize.enabled) { delta.width = dialog.outerWidth(true) - dialog.width() + ((dialog.outerWidth() - dialog.width()) / 2); delta.height = dialog.outerHeight(true) - dialog.height() + ((dialog.outerHeight() - dialog.height()) / 2); } if (fm.options.dialogContained) { maxSize = { maxWidth: elfNode.width() - delta.width, maxHeight: elfNode.height() - delta.height }; opts.maxWidth = opts.maxWidth? Math.min(maxSize.maxWidth, opts.maxWidth) : maxSize.maxWidth; opts.maxHeight = opts.maxHeight? Math.min(maxSize.maxHeight, opts.maxHeight) : maxSize.maxHeight; dialog.css(maxSize); } restoreStyle = { maxWidth : dialog.css('max-width'), maxHeight : dialog.css('max-height'), overflow : dialog.css('overflow') }; if (opts.resizable) { dialog.resizable({ minWidth : opts.minWidth, minHeight : opts.minHeight, maxWidth : opts.maxWidth, maxHeight : opts.maxHeight, start : function() { evCover.show(); if (dialog.data('resizing') !== true && dialog.data('resizing')) { clearTimeout(dialog.data('resizing')); } dialog.data('resizing', true); }, stop : function(e, ui) { evCover.hide(); dialog.data('resizing', setTimeout(function() { dialog.data('resizing', false); }, 200)); if (syncSize.enabled) { syncSize.defaultSize = { width: self.width(), height: self.height() }; } } }).data('hasResizable', true); } numberToTel(); tabstopsInit(); typeof(opts.create) == 'function' && jQuery.proxy(opts.create, this)(); if (opts.autoOpen) { if (opts.open) { requestAnimationFrame(function() { self.elfinderdialog('open'); }); } else { self.elfinderdialog('open'); } } if (opts.resize) { fm.bind('themechange', function() { setTimeout(function() { dialog.data('margin-y', self.outerHeight(true) - self.height()); dialog.trigger('resize', {init: true}); }, 300); }); } }); return this; }; jQuery.fn.elfinderdialog.defaults = { cssClass : '', title : '', modal : false, resizable : true, autoOpen : true, closeOnEscape : true, destroyOnClose : false, buttons : {}, btnHoverFocus : true, position : null, absolute : false, width : 320, height : 'auto', minWidth : 200, minHeight : 70, maxWidth : null, maxHeight : null, allowMinimize : 'auto', allowMaximize : false, openMaximized : false, headerBtnPos : 'auto', headerBtnOrder : 'auto', optimizeNumber : true, propagationEvents : ['mousemove', 'mouseup'] }; /* * File: /js/ui/fullscreenbutton.js */ /** * @class elFinder toolbar button to switch full scrren mode. * * @author Naoki Sawada **/ jQuery.fn.elfinderfullscreenbutton = function(cmd) { return this.each(function() { var button = jQuery(this).elfinderbutton(cmd), icon = button.children('.elfinder-button-icon'), tm; cmd.change(function() { tm && cancelAnimationFrame(tm); tm = requestAnimationFrame(function() { var fullscreen = cmd.value; icon.addClass('elfinder-button-icon-fullscreen').toggleClass('elfinder-button-icon-unfullscreen', fullscreen); cmd.className = fullscreen? 'unfullscreen' : ''; }); }); }); }; /* * File: /js/ui/navbar.js */ /** * @class elfindernav - elFinder container for diretories tree and places * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfindernavbar = function(fm, opts) { this.not('.elfinder-navbar').each(function() { var nav = jQuery(this).hide().addClass('ui-state-default elfinder-navbar'), parent = nav.css('overflow', 'hidden').parent(), wz = parent.children('.elfinder-workzone').append(nav), ltr = fm.direction == 'ltr', delta, deltaW, handle, swipeHandle, autoHide, setWidth, navdock, setWzRect = function() { var cwd = fm.getUI('cwd'), wz = fm.getUI('workzone'), wzRect = wz.data('rectangle'), cwdOffset = cwd.offset(); wz.data('rectangle', Object.assign(wzRect, { cwdEdge: (fm.direction === 'ltr')? cwdOffset.left : cwdOffset.left + cwd.width() })); }, setDelta = function() { nav.css('overflow', 'hidden'); delta = Math.round(nav.outerHeight() - nav.height()); deltaW = Math.round(navdock.outerWidth() - navdock.innerWidth()); nav.css('overflow', 'auto'); }; fm.one('init', function() { navdock = fm.getUI('navdock'); var set = function() { setDelta(); fm.bind('wzresize', function() { var navdockH = 0; navdock.width(nav.outerWidth() - deltaW); if (navdock.children().length > 1) { navdockH = navdock.outerHeight(true); } nav.height(wz.height() - navdockH - delta); }).trigger('wzresize'); }; if (fm.cssloaded) { set(); } else { fm.one('cssloaded', set); } }) .one('opendone',function() { handle && handle.trigger('resize'); nav.css('overflow', 'auto'); }).bind('themechange', setDelta); if (fm.UA.Touch) { autoHide = fm.storage('autoHide') || {}; if (typeof autoHide.navbar === 'undefined') { autoHide.navbar = (opts.autoHideUA && opts.autoHideUA.length > 0 && jQuery.grep(opts.autoHideUA, function(v){ return fm.UA[v]? true : false; }).length); fm.storage('autoHide', autoHide); } if (autoHide.navbar) { fm.one('init', function() { if (nav.children().length) { fm.uiAutoHide.push(function(){ nav.stop(true, true).trigger('navhide', { duration: 'slow', init: true }); }); } }); } fm.bind('load', function() { if (nav.children().length) { swipeHandle = jQuery('<div class="elfinder-navbar-swipe-handle"/>').hide().appendTo(wz); if (swipeHandle.css('pointer-events') !== 'none') { swipeHandle.remove(); swipeHandle = null; } } }); nav.on('navshow navhide', function(e, data) { var mode = (e.type === 'navshow')? 'show' : 'hide', duration = (data && data.duration)? data.duration : 'fast', handleW = (data && data.handleW)? data.handleW : Math.max(50, fm.getUI().width() / 10); nav.stop(true, true)[mode]({ duration: duration, step : function() { fm.trigger('wzresize'); }, complete: function() { if (swipeHandle) { if (mode === 'show') { swipeHandle.stop(true, true).hide(); } else { swipeHandle.width(handleW? handleW : ''); fm.resources.blink(swipeHandle, 'slowonce'); } } fm.trigger('navbar'+ mode); data.init && fm.trigger('uiautohide'); setWzRect(); } }); autoHide.navbar = (mode !== 'show'); fm.storage('autoHide', Object.assign(fm.storage('autoHide'), {navbar: autoHide.navbar})); }).on('touchstart', function(e) { if (jQuery(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) { e.originalEvent._preventSwipeX = true; } }); } if (! fm.UA.Mobile) { handle = nav.resizable({ handles : ltr ? 'e' : 'w', minWidth : opts.minWidth || 150, maxWidth : opts.maxWidth || 500, resize : function() { fm.trigger('wzresize'); }, stop : function(e, ui) { fm.storage('navbarWidth', ui.size.width); setWzRect(); } }) .on('resize scroll', function(e) { var $this = jQuery(this), tm = $this.data('posinit'); e.preventDefault(); e.stopPropagation(); if (! ltr && e.type === 'resize') { nav.css('left', 0); } tm && cancelAnimationFrame(tm); $this.data('posinit', requestAnimationFrame(function() { var offset = (fm.UA.Opera && nav.scrollLeft())? 20 : 2; handle.css('top', 0).css({ top : parseInt(nav.scrollTop())+'px', left : ltr ? 'auto' : parseInt(nav.scrollRight() - offset) * -1, right: ltr ? parseInt(nav.scrollLeft() - offset) * -1 : 'auto' }); if (e.type === 'resize') { fm.getUI('cwd').trigger('resize'); } })); }) .children('.ui-resizable-handle').addClass('ui-front'); } if (setWidth = fm.storage('navbarWidth')) { nav.width(setWidth); } else { if (fm.UA.Mobile) { fm.one('cssloaded', function() { var set = function() { setWidth = nav.parent().width() / 2; if (nav.data('defWidth') > setWidth) { nav.width(setWidth); } else { nav.width(nav.data('defWidth')); } nav.data('width', nav.width()); fm.trigger('wzresize'); }; nav.data('defWidth', nav.width()); jQuery(window).on('resize.' + fm.namespace, set); set(); }); } } }); return this; }; /* * File: /js/ui/navdock.js */ /** * @class elfindernavdock - elFinder container for preview etc at below the navbar * * @author Naoki Sawada **/ jQuery.fn.elfindernavdock = function(fm, opts) { this.not('.elfinder-navdock').each(function() { var self = jQuery(this).hide().addClass('ui-state-default elfinder-navdock touch-punch'), node = self.parent(), wz = node.children('.elfinder-workzone').append(self), resize = function(to, h) { var curH = h || self.height(), diff = to - curH, len = Object.keys(sizeSyncs).length, calc = len? diff / len : 0, ovf; if (diff) { ovf = self.css('overflow'); self.css('overflow', 'hidden'); self.height(to); jQuery.each(sizeSyncs, function(id, n) { n.height(n.height() + calc).trigger('resize.' + fm.namespace); }); fm.trigger('wzresize'); self.css('overflow', ovf); } }, handle = jQuery('<div class="ui-front ui-resizable-handle ui-resizable-n"/>').appendTo(self), sizeSyncs = {}, resizeFn = [], initMaxHeight = (parseInt(opts.initMaxHeight) || 50) / 100, maxHeight = (parseInt(opts.maxHeight) || 90) / 100, basicHeight, hasNode; self.data('addNode', function(cNode, opts) { var wzH = fm.getUI('workzone').height(), imaxH = wzH * initMaxHeight, curH, tH, mH; opts = Object.assign({ first: false, sizeSync: true, init: false }, opts); if (!cNode.attr('id')) { cNode.attr('id', fm.namespace+'-navdock-' + (+new Date())); } opts.sizeSync && (sizeSyncs[cNode.attr('id')] = cNode); curH = self.height(); tH = curH + cNode.outerHeight(true); if (opts.first) { handle.after(cNode); } else { self.append(cNode); } hasNode = true; self.resizable('enable').height(tH).show(); fm.trigger('wzresize'); if (opts.init) { mH = fm.storage('navdockHeight'); if (mH) { tH = mH; } else { tH = tH > imaxH? imaxH : tH; } basicHeight = tH; } resize(Math.min(tH, wzH * maxHeight)); return self; }).data('removeNode', function(nodeId, appendTo) { var cNode = jQuery('#'+nodeId); delete sizeSyncs[nodeId]; self.height(self.height() - jQuery('#'+nodeId).outerHeight(true)); if (appendTo) { if (appendTo === 'detach') { cNode = cNode.detach(); } else { appendTo.append(cNode); } } else { cNode.remove(); } if (self.children().length <= 1) { hasNode = false; self.resizable('disable').height(0).hide(); } fm.trigger('wzresize'); return cNode; }); if (! opts.disabled) { fm.one('init', function() { var ovf; if (fm.getUI('navbar').children().not('.ui-resizable-handle').length) { self.data('dockEnabled', true); self.resizable({ maxHeight: fm.getUI('workzone').height() * maxHeight, handles: { n: handle }, start: function(e, ui) { ovf = self.css('overflow'); self.css('overflow', 'hidden'); fm.trigger('navdockresizestart', {event: e, ui: ui}, true); }, resize: function(e, ui) { self.css('top', ''); fm.trigger('wzresize', { inNavdockResize : true }); }, stop: function(e, ui) { fm.trigger('navdockresizestop', {event: e, ui: ui}, true); self.css('top', ''); basicHeight = ui.size.height; fm.storage('navdockHeight', basicHeight); resize(basicHeight, ui.originalSize.height); self.css('overflow', ovf); } }); fm.bind('wzresize', function(e) { var minH, maxH, h; if (self.is(':visible')) { maxH = fm.getUI('workzone').height() * maxHeight; if (! e.data || ! e.data.inNavdockResize) { h = self.height(); if (maxH < basicHeight) { if (Math.abs(h - maxH) > 1) { resize(maxH); } } else { if (Math.abs(h - basicHeight) > 1) { resize(basicHeight); } } } self.resizable('option', 'maxHeight', maxH); } }).bind('themechange', function() { var oldH = Math.round(self.height()); requestAnimationFrame(function() { var curH = Math.round(self.height()), diff = oldH - curH; if (diff !== 0) { resize(self.height(), curH - diff); } }); }); } fm.bind('navbarshow navbarhide', function(e) { self[hasNode && e.type === 'navbarshow'? 'show' : 'hide'](); }); }); } }); return this; }; /* * File: /js/ui/overlay.js */ jQuery.fn.elfinderoverlay = function(opts) { var fm = this.parent().elfinder('instance'), o, cnt, show, hide; this.filter(':not(.elfinder-overlay)').each(function() { opts = Object.assign({}, opts); jQuery(this).addClass('ui-front ui-widget-overlay elfinder-overlay') .hide() .on('mousedown', function(e) { e.preventDefault(); e.stopPropagation(); }) .data({ cnt : 0, show : typeof(opts.show) == 'function' ? opts.show : function() { }, hide : typeof(opts.hide) == 'function' ? opts.hide : function() { } }); }); if (opts == 'show') { o = this.eq(0); cnt = o.data('cnt') + 1; show = o.data('show'); fm.toFront(o); o.data('cnt', cnt); if (o.is(':hidden')) { o.show(); show(); } } if (opts == 'hide') { o = this.eq(0); cnt = o.data('cnt') - 1; hide = o.data('hide'); o.data('cnt', cnt); if (cnt <= 0) { o.hide(); hide(); } } return this; }; /* * File: /js/ui/panel.js */ jQuery.fn.elfinderpanel = function(fm) { return this.each(function() { var panel = jQuery(this).addClass('elfinder-panel ui-state-default ui-corner-all'), margin = 'margin-'+(fm.direction == 'ltr' ? 'left' : 'right'); fm.one('load', function(e) { var navbar = fm.getUI('navbar'); panel.css(margin, parseInt(navbar.outerWidth(true))); navbar.on('resize', function(e) { e.preventDefault(); e.stopPropagation(); panel.is(':visible') && panel.css(margin, parseInt(navbar.outerWidth(true))); }); }); }); }; /* * File: /js/ui/path.js */ /** * @class elFinder ui * Display current folder path in statusbar. * Click on folder name in path - open folder * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfinderpath = function(fm, options) { return this.each(function() { var query = '', target = '', mimes = [], place = 'statusbar', clHover= fm.res('class', 'hover'), prefix = 'path' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-', wzbase = jQuery('<div class="ui-widget-header ui-helper-clearfix elfinder-workzone-path"/>'), path = jQuery(this).addClass('elfinder-path').html(' ') .on('mousedown', 'span.elfinder-path-dir', function(e) { var hash = jQuery(this).attr('id').substr(prefix.length); e.preventDefault(); if (hash != fm.cwd().hash) { jQuery(this).addClass(clHover); if (query) { fm.exec('search', query, { target: hash, mime: mimes.join(' ') }); } else { fm.trigger('select', {selected : [hash]}).exec('open', hash); } } }) .prependTo(fm.getUI('statusbar').show()), roots = jQuery('<div class="elfinder-path-roots"/>').on('click', function(e) { e.stopPropagation(); e.preventDefault(); var roots = jQuery.map(fm.roots, function(h) { return fm.file(h); }), raw = []; jQuery.each(roots, function(i, f) { if (! f.phash && fm.root(fm.cwd().hash, true) !== f.hash) { raw.push({ label : fm.escape(f.i18 || f.name), icon : 'home', callback : function() { fm.exec('open', f.hash); }, options : { iconClass : f.csscls || '', iconImg : f.icon || '' } }); } }); fm.trigger('contextmenu', { raw: raw, x: e.pageX, y: e.pageY }); }).append('<span class="elfinder-button-icon elfinder-button-icon-menu" />').appendTo(wzbase), render = function(cwd) { var dirs = [], names = []; jQuery.each(fm.parents(cwd), function(i, hash) { var c = (cwd === hash)? 'elfinder-path-dir elfinder-path-cwd' : 'elfinder-path-dir', f = fm.file(hash), name = fm.escape(f.i18 || f.name); names.push(name); dirs.push('<span id="'+prefix+hash+'" class="'+c+'" title="'+names.join(fm.option('separator'))+'">'+name+'</span>'); }); return dirs.join('<span class="elfinder-path-other">'+fm.option('separator')+'</span>'); }, toWorkzone = function() { var prev; path.children('span.elfinder-path-dir').attr('style', ''); prev = fm.direction === 'ltr'? jQuery('#'+prefix + fm.cwd().hash).prevAll('span.elfinder-path-dir:first') : jQuery(); path.scrollLeft(prev.length? prev.position().left : 0); }, fit = function() { if (fm.UA.CSS.flex) { return; } var dirs = path.children('span.elfinder-path-dir'), cnt = dirs.length, m, bg = 0, ids; if (place === 'workzone' || cnt < 2) { dirs.attr('style', ''); return; } path.width(path.css('max-width')); dirs.css({maxWidth: (100/cnt)+'%', display: 'inline-block'}); m = path.width() - 9; path.children('span.elfinder-path-other').each(function() { m -= jQuery(this).width(); }); ids = []; dirs.each(function(i) { var dir = jQuery(this), w = dir.width(); m -= w; if (w < this.scrollWidth) { ids.push(i); } }); path.width(''); if (ids.length) { if (m > 0) { m = m / ids.length; jQuery.each(ids, function(i, k) { var d = jQuery(dirs[k]); d.css('max-width', d.width() + m); }); } dirs.last().attr('style', ''); } else { dirs.attr('style', ''); } }, hasUiTree, hasUiStat; fm.one('init', function() { hasUiTree = fm.getUI('tree').length; hasUiStat = fm.getUI('stat').length; if (! hasUiTree && options.toWorkzoneWithoutNavbar) { wzbase.append(path).insertBefore(fm.getUI('workzone')); place = 'workzone'; fm.bind('open', toWorkzone) .one('opendone', function() { fm.getUI().trigger('resize'); }); } }) .bind('open searchend parents', function() { var dirs = []; query = ''; target = ''; mimes = []; path.html(render(fm.cwd().hash)); if (Object.keys(fm.roots).length > 1) { path.css('margin', ''); roots.show(); } else { path.css('margin', 0); roots.hide(); } !hasUiStat && fit(); }) .bind('searchstart', function(e) { if (e.data) { query = e.data.query || ''; target = e.data.target || ''; mimes = e.data.mimes || []; } }) .bind('search', function(e) { var dirs = [], html = ''; if (target) { html = render(target); } else { html = fm.i18n('btnAll'); } path.html('<span class="elfinder-path-other">'+fm.i18n('searcresult') + ': </span>' + html); fit(); }) // on swipe to navbar show/hide .bind('navbarshow navbarhide', function() { var wz = fm.getUI('workzone'); if (this.type === 'navbarshow') { fm.unbind('open', toWorkzone); path.prependTo(fm.getUI('statusbar')); wzbase.detach(); place = 'statusbar'; } else { wzbase.append(path).insertBefore(wz); place = 'workzone'; toWorkzone(); fm.bind('open', toWorkzone); } fm.trigger('uiresize'); }) .bind('resize uistatchange', fit); }); }; /* * File: /js/ui/places.js */ /** * @class elFinder places/favorites ui * * @author Dmitry (dio) Levashov * @author Naoki Sawada **/ jQuery.fn.elfinderplaces = function(fm, opts) { return this.each(function() { var dirs = {}, c = 'class', navdir = fm.res(c, 'navdir'), collapsed = fm.res(c, 'navcollapse'), expanded = fm.res(c, 'navexpand'), hover = fm.res(c, 'hover'), clroot = fm.res(c, 'treeroot'), dropover = fm.res(c, 'adroppable'), tpl = fm.res('tpl', 'placedir'), ptpl = fm.res('tpl', 'perms'), spinner = jQuery(fm.res('tpl', 'navspinner')), suffix = opts.suffix? opts.suffix : '', key = 'places' + suffix, menuTimer = null, /** * Convert places dir node into dir hash * * @param String directory id * @return String **/ id2hash = function(id) { return id.substr(6); }, /** * Convert places dir hash into dir node id * * @param String directory id * @return String **/ hash2id = function(hash) { return 'place-'+hash; }, /** * Convert places dir hash into dir node elment (jQuery object) * * @param String directory id * @return Object **/ hash2elm = function(hash) { return jQuery(document.getElementById(hash2id(hash))); }, /** * Save current places state * * @return void **/ save = function() { var hashes = [], data = {}; hashes = jQuery.map(subtree.children().find('[id]'), function(n) { return id2hash(n.id); }); if (hashes.length) { jQuery.each(hashes.reverse(), function(i, h) { data[h] = dirs[h]; }); } else { data = null; } fm.storage(key, data); }, /** * Init dir at places * * @return void **/ init = function() { var dat, hashes; key = 'places'+(opts.suffix? opts.suffix : ''), dirs = {}; dat = fm.storage(key); if (typeof dat === 'string') { // old data type elFinder <= 2.1.12 dat = jQuery.grep(dat.split(','), function(hash) { return hash? true : false;}); jQuery.each(dat, function(i, d) { var dir = d.split('#'); dirs[dir[0]] = dir[1]? dir[1] : dir[0]; }); } else if (jQuery.isPlainObject(dat)) { dirs = dat; } // allow modify `dirs` /** * example for preset places * * elfinderInstance.bind('placesload', function(e, fm) { * //if (fm.storage(e.data.storageKey) === null) { // for first time only * if (!fm.storage(e.data.storageKey)) { // for empty places * e.data.dirs[targetHash] = fallbackName; // preset folder * } * } **/ fm.trigger('placesload', {dirs: dirs, storageKey: key}, true); hashes = Object.keys(dirs); if (hashes.length) { root.prepend(spinner); fm.request({ data : {cmd : 'info', targets : hashes}, preventDefault : true }) .done(function(data) { var exists = {}; data.files && data.files.length && fm.cache(data.files); jQuery.each(data.files, function(i, f) { var hash = f.hash; exists[hash] = f; }); jQuery.each(dirs, function(h, f) { add(exists[h] || Object.assign({notfound: true}, f)); }); if (fm.storage('placesState') > 0) { root.trigger('click'); } }) .always(function() { spinner.remove(); }); } }, /** * Return node for given dir object * * @param Object directory object * @return jQuery **/ create = function(dir, hash) { return jQuery(tpl.replace(/\{id\}/, hash2id(dir? dir.hash : hash)) .replace(/\{name\}/, fm.escape(dir? dir.i18 || dir.name : hash)) .replace(/\{cssclass\}/, dir? (fm.perms2class(dir) + (dir.notfound? ' elfinder-na' : '') + (dir.csscls? ' '+dir.csscls : '')) : '') .replace(/\{permissions\}/, (dir && (!dir.read || !dir.write || dir.notfound))? ptpl : '') .replace(/\{title\}/, (dir && dir.path)? fm.escape(dir.path) : '') .replace(/\{symlink\}/, '') .replace(/\{style\}/, (dir && dir.icon)? fm.getIconStyle(dir) : '')); }, /** * Add new node into places * * @param Object directory object * @return void **/ add = function(dir) { var node, hash; if (dir.mime !== 'directory') { return false; } hash = dir.hash; if (!fm.files().hasOwnProperty(hash)) { // update cache fm.trigger('tree', {tree: [dir]}); } node = create(dir, hash); dirs[hash] = dir; subtree.prepend(node); root.addClass(collapsed); sortBtn.toggle(subtree.children().length > 1); return true; }, /** * Remove dir from places * * @param String directory hash * @return String removed name **/ remove = function(hash) { var name = null, tgt, cnt; if (dirs[hash]) { delete dirs[hash]; tgt = hash2elm(hash); if (tgt.length) { name = tgt.text(); tgt.parent().remove(); cnt = subtree.children().length; sortBtn.toggle(cnt > 1); if (! cnt) { root.removeClass(collapsed); places.removeClass(expanded); subtree.slideToggle(false); } } } return name; }, /** * Move up dir on places * * @param String directory hash * @return void **/ moveup = function(hash) { var self = hash2elm(hash), tgt = self.parent(), prev = tgt.prev('div'), cls = 'ui-state-hover', ctm = fm.getUI('contextmenu'); menuTimer && clearTimeout(menuTimer); if (prev.length) { ctm.find(':first').data('placesHash', hash); self.addClass(cls); tgt.insertBefore(prev); prev = tgt.prev('div'); menuTimer = setTimeout(function() { self.removeClass(cls); if (ctm.find(':first').data('placesHash') === hash) { ctm.hide().empty(); } }, 1500); } if(!prev.length) { self.removeClass(cls); ctm.hide().empty(); } }, /** * Update dir at places * * @param Object directory * @param String previous hash * @return Boolean **/ update = function(dir, preHash) { var hash = dir.hash, tgt = hash2elm(preHash || hash), node = create(dir, hash); if (tgt.length > 0) { tgt.parent().replaceWith(node); dirs[hash] = dir; return true; } else { return false; } }, /** * Remove all dir from places * * @return void **/ clear = function() { subtree.empty(); root.removeClass(collapsed); places.removeClass(expanded); subtree.slideToggle(false); }, /** * Sort places dirs A-Z * * @return void **/ sort = function() { jQuery.each(dirs, function(h, f) { var dir = fm.file(h) || f, node = create(dir, h), ret = null; if (!dir) { node.hide(); } if (subtree.children().length) { jQuery.each(subtree.children(), function() { var current = jQuery(this); if ((dir.i18 || dir.name).localeCompare(current.children('.'+navdir).text()) < 0) { ret = !node.insertBefore(current); return ret; } }); if (ret !== null) { return true; } } !hash2elm(h).length && subtree.append(node); }); save(); }, // sort button sortBtn = jQuery('<span class="elfinder-button-icon elfinder-button-icon-sort elfinder-places-root-icon" title="'+fm.i18n('cmdsort')+'"/>') .hide() .on('click', function(e) { e.stopPropagation(); subtree.empty(); sort(); } ), /** * Node - wrapper for places root * * @type jQuery **/ wrapper = create({ hash : 'root-'+fm.namespace, name : fm.i18n(opts.name, 'places'), read : true, write : true }), /** * Places root node * * @type jQuery **/ root = wrapper.children('.'+navdir) .addClass(clroot) .on('click', function(e) { e.stopPropagation(); if (root.hasClass(collapsed)) { places.toggleClass(expanded); subtree.slideToggle(); fm.storage('placesState', places.hasClass(expanded)? 1 : 0); } }) .append(sortBtn), /** * Container for dirs * * @type jQuery **/ subtree = wrapper.children('.'+fm.res(c, 'navsubtree')), /** * Main places container * * @type jQuery **/ places = jQuery(this).addClass(fm.res(c, 'tree')+' elfinder-places ui-corner-all') .hide() .append(wrapper) .appendTo(fm.getUI('navbar')) .on('mouseenter mouseleave', '.'+navdir, function(e) { jQuery(this).toggleClass('ui-state-hover', (e.type == 'mouseenter')); }) .on('click', '.'+navdir, function(e) { var p = jQuery(this); if (p.data('longtap')) { e.stopPropagation(); return; } ! p.hasClass('elfinder-na') && fm.exec('open', p.attr('id').substr(6)); }) .on('contextmenu', '.'+navdir+':not(.'+clroot+')', function(e) { var self = jQuery(this), hash = self.attr('id').substr(6); e.preventDefault(); fm.trigger('contextmenu', { raw : [{ label : fm.i18n('moveUp'), icon : 'up', remain : true, callback : function() { moveup(hash); save(); } },'|',{ label : fm.i18n('rmFromPlaces'), icon : 'rm', callback : function() { remove(hash); save(); } }], 'x' : e.pageX, 'y' : e.pageY }); self.addClass('ui-state-hover'); fm.getUI('contextmenu').children().on('mouseenter', function() { self.addClass('ui-state-hover'); }); fm.bind('closecontextmenu', function() { self.removeClass('ui-state-hover'); }); }) .droppable({ tolerance : 'pointer', accept : '.elfinder-cwd-file-wrapper,.elfinder-tree-dir,.elfinder-cwd-file', hoverClass : fm.res('class', 'adroppable'), classes : { // Deprecated hoverClass jQueryUI>=1.12.0 'ui-droppable-hover': fm.res('class', 'adroppable') }, over : function(e, ui) { var helper = ui.helper, dir = jQuery.grep(helper.data('files'), function(h) { return (fm.file(h).mime === 'directory' && !dirs[h])? true : false; }); e.stopPropagation(); helper.data('dropover', helper.data('dropover') + 1); if (fm.insideWorkzone(e.pageX, e.pageY)) { if (dir.length > 0) { helper.addClass('elfinder-drag-helper-plus'); fm.trigger('unlockfiles', {files : helper.data('files'), helper: helper}); } else { jQuery(this).removeClass(dropover); } } }, out : function(e, ui) { var helper = ui.helper; e.stopPropagation(); helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0)); jQuery(this).removeData('dropover') .removeClass(dropover); }, drop : function(e, ui) { var helper = ui.helper, resolve = true; jQuery.each(helper.data('files'), function(i, hash) { var dir = fm.file(hash); if (dir && dir.mime == 'directory' && !dirs[dir.hash]) { add(dir); } else { resolve = false; } }); save(); resolve && helper.hide(); } }) // for touch device .on('touchstart', '.'+navdir+':not(.'+clroot+')', function(e) { if (e.originalEvent.touches.length > 1) { return; } var hash = jQuery(this).attr('id').substr(6), p = jQuery(this) .addClass(hover) .data('longtap', null) .data('tmlongtap', setTimeout(function(){ // long tap p.data('longtap', true); fm.trigger('contextmenu', { raw : [{ label : fm.i18n('rmFromPlaces'), icon : 'rm', callback : function() { remove(hash); save(); } }], 'x' : e.originalEvent.touches[0].pageX, 'y' : e.originalEvent.touches[0].pageY }); }, 500)); }) .on('touchmove touchend', '.'+navdir+':not(.'+clroot+')', function(e) { clearTimeout(jQuery(this).data('tmlongtap')); if (e.type == 'touchmove') { jQuery(this).removeClass(hover); } }); if (jQuery.fn.sortable) { subtree.addClass('touch-punch') .sortable({ appendTo : fm.getUI(), revert : false, helper : function(e) { var dir = jQuery(e.target).parent(); dir.children().removeClass('ui-state-hover'); return jQuery('<div class="ui-widget elfinder-place-drag elfinder-'+fm.direction+'"/>') .append(jQuery('<div class="elfinder-navbar"/>').show().append(dir.clone())); }, stop : function(e, ui) { var target = jQuery(ui.item[0]), top = places.offset().top, left = places.offset().left, width = places.width(), height = places.height(), x = e.pageX, y = e.pageY; if (!(x > left && x < left+width && y > top && y < y+height)) { remove(id2hash(target.children(':first').attr('id'))); save(); } }, update : function(e, ui) { save(); } }); } // "on regist" for command exec jQuery(this).on('regist', function(e, files){ var added = false; jQuery.each(files, function(i, dir) { if (dir && dir.mime == 'directory' && !dirs[dir.hash]) { if (add(dir)) { added = true; } } }); added && save(); }); // on fm load - show places and load files from backend fm.one('load', function() { var dat, hashes; if (fm.oldAPI) { return; } places.show().parent().show(); init(); fm.change(function(e) { var changed = false; jQuery.each(e.data.changed, function(i, file) { if (dirs[file.hash]) { if (file.mime !== 'directory') { if (remove(file.hash)) { changed = true; } } else { if (update(file)) { changed = true; } } } }); changed && save(); }) .bind('rename', function(e) { var changed = false; if (e.data.removed) { jQuery.each(e.data.removed, function(i, hash) { if (e.data.added[i]) { if (update(e.data.added[i], hash)) { changed = true; } } }); } changed && save(); }) .bind('rm paste', function(e) { var names = [], changed = false; if (e.data.removed) { jQuery.each(e.data.removed, function(i, hash) { var name = remove(hash); name && names.push(name); }); } if (names.length) { changed = true; } if (e.data.added && names.length) { jQuery.each(e.data.added, function(i, file) { if (jQuery.inArray(file.name, names) !== 1) { file.mime == 'directory' && add(file); } }); } changed && save(); }) .bind('sync netmount', function() { var ev = this, opSuffix = opts.suffix? opts.suffix : '', hashes; if (ev.type === 'sync') { // check is change of opts.suffix if (suffix !== opSuffix) { suffix = opSuffix; clear(); init(); return; } } hashes = Object.keys(dirs); if (hashes.length) { root.prepend(spinner); fm.request({ data : {cmd : 'info', targets : hashes}, preventDefault : true }) .done(function(data) { var exists = {}, updated = false, cwd = fm.cwd().hash; jQuery.each(data.files || [], function(i, file) { var hash = file.hash; exists[hash] = file; if (!fm.files().hasOwnProperty(file.hash)) { // update cache fm.trigger('tree', {tree: [file]}); } }); jQuery.each(dirs, function(h, f) { if (f.notfound === Boolean(exists[h])) { if ((f.phash === cwd && ev.type !== 'netmount') || (exists[h] && exists[h].mime !== 'directory')) { if (remove(h)) { updated = true; } } else { if (update(exists[h] || Object.assign({notfound: true}, f))) { updated = true; } } } else if (exists[h] && exists[h].phash != cwd) { // update permission of except cwd update(exists[h]); } }); updated && save(); }) .always(function() { spinner.remove(); }); } }); }); }); }; /* * File: /js/ui/searchbutton.js */ /** * @class elFinder toolbar search button widget. * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfindersearchbutton = function(cmd) { return this.each(function() { var result = false, fm = cmd.fm, disabled = fm.res('class', 'disabled'), isopts = cmd.options.incsearch || { enable: false }, sTypes = cmd.options.searchTypes, id = function(name){return fm.namespace + fm.escape(name);}, toolbar= fm.getUI('toolbar'), btnCls = fm.res('class', 'searchbtn'), button = jQuery(this) .hide() .addClass('ui-widget-content elfinder-button '+btnCls) .on('click', function(e) { e.stopPropagation(); }), getMenuOffset = function() { var fmNode = fm.getUI(), baseOffset = fmNode.offset(), buttonOffset = button.offset(); return { top : buttonOffset.top - baseOffset.top, maxHeight : fmNode.height() - 40 }; }, search = function() { input.data('inctm') && clearTimeout(input.data('inctm')); var val = jQuery.trim(input.val()), from = !jQuery('#' + id('SearchFromAll')).prop('checked'), mime = jQuery('#' + id('SearchMime')).prop('checked'), type = ''; if (from) { if (jQuery('#' + id('SearchFromVol')).prop('checked')) { from = fm.root(fm.cwd().hash); } else { from = fm.cwd().hash; } } if (mime) { mime = val; val = '.'; } if (typeSet) { type = typeSet.children('input:checked').val(); } if (val) { input.trigger('focus'); cmd.exec(val, from, mime, type).done(function() { result = true; }).fail(function() { abort(); }); } else { fm.trigger('searchend'); } }, abort = function() { input.data('inctm') && clearTimeout(input.data('inctm')); input.val('').trigger('blur'); if (result || incVal) { result = false; incVal = ''; fm.lazy(function() { fm.trigger('searchend'); }); } }, incVal = '', input = jQuery('<input type="text" size="42"/>') .on('focus', function() { // close other menus !button.hasClass('ui-state-active') && fm.getUI().click(); inFocus = true; incVal = ''; button.addClass('ui-state-active'); fm.trigger('uiresize'); opts && opts.css(getMenuOffset()).slideDown(function() { // Care for on browser window re-active button.addClass('ui-state-active'); fm.toFront(opts); }); }) .on('blur', function() { inFocus = false; if (opts) { if (!opts.data('infocus')) { opts.slideUp(function() { button.removeClass('ui-state-active'); fm.trigger('uiresize'); fm.toHide(opts); }); } else { opts.data('infocus', false); } } else { button.removeClass('ui-state-active'); } }) .appendTo(button) // to avoid fm shortcuts on arrows .on('keypress', function(e) { e.stopPropagation(); }) .on('keydown', function(e) { e.stopPropagation(); if (e.keyCode === jQuery.ui.keyCode.ENTER) { search(); } else if (e.keyCode === jQuery.ui.keyCode.ESCAPE) { e.preventDefault(); abort(); } }), opts, typeSet, cwdReady, inFocus; if (isopts.enable) { isopts.minlen = isopts.minlen || 2; isopts.wait = isopts.wait || 500; input .attr('title', fm.i18n('incSearchOnly')) .on('compositionstart', function() { input.data('composing', true); }) .on('compositionend', function() { input.removeData('composing'); input.trigger('input'); // for IE, edge }) .on('input', function() { if (! input.data('composing')) { input.data('inctm') && clearTimeout(input.data('inctm')); input.data('inctm', setTimeout(function() { var val = input.val(); if (val.length === 0 || val.length >= isopts.minlen) { (incVal !== val) && fm.trigger('incsearchstart', { query: val }); incVal = val; if (val === '' && fm.searchStatus.state > 1 && fm.searchStatus.query) { input.val(fm.searchStatus.query).trigger('select'); } } }, isopts.wait)); } }); if (fm.UA.ltIE8) { input.on('keydown', function(e) { if (e.keyCode === 229) { input.data('imetm') && clearTimeout(input.data('imetm')); input.data('composing', true); input.data('imetm', setTimeout(function() { input.removeData('composing'); }, 100)); } }) .on('keyup', function(e) { input.data('imetm') && clearTimeout(input.data('imetm')); if (input.data('composing')) { e.keyCode === jQuery.ui.keyCode.ENTER && input.trigger('compositionend'); } else { input.trigger('input'); } }); } } jQuery('<span class="ui-icon ui-icon-search" title="'+cmd.title+'"/>') .appendTo(button) .on('mousedown', function(e) { e.stopPropagation(); e.preventDefault(); if (button.hasClass('ui-state-active')) { search(); } else { input.trigger('focus'); } }); jQuery('<span class="ui-icon ui-icon-close"/>') .appendTo(button) .on('mousedown', function(e) { e.stopPropagation(); e.preventDefault(); if (input.val() === '' && !button.hasClass('ui-state-active')) { input.trigger('focus'); } else { abort(); } }); // wait when button will be added to DOM fm.bind('toolbarload', function(){ var parent = button.parent(); if (parent.length) { toolbar.prepend(button.show()); parent.remove(); // position icons for ie7 if (fm.UA.ltIE7) { var icon = button.children(fm.direction == 'ltr' ? '.ui-icon-close' : '.ui-icon-search'); icon.css({ right : '', left : parseInt(button.width())-icon.outerWidth(true) }); } } }); fm .one('init', function() { fm.getUI('cwd').on('touchstart click', function() { inFocus && input.trigger('blur'); }); }) .one('open', function() { opts = (fm.api < 2.1)? null : jQuery('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu elfinder-button-search-menu ui-corner-all"/>') .append( jQuery('<div class="buttonset"/>') .append( jQuery('<input id="'+id('SearchFromCwd')+'" name="serchfrom" type="radio" checked="checked"/><label for="'+id('SearchFromCwd')+'">'+fm.i18n('btnCwd')+'</label>'), jQuery('<input id="'+id('SearchFromVol')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromVol')+'">'+fm.i18n('btnVolume')+'</label>'), jQuery('<input id="'+id('SearchFromAll')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromAll')+'">'+fm.i18n('btnAll')+'</label>') ), jQuery('<div class="buttonset elfinder-search-type"/>') .append( jQuery('<input id="'+id('SearchName')+'" name="serchcol" type="radio" checked="checked" value="SearchName"/><label for="'+id('SearchName')+'">'+fm.i18n('btnFileName')+'</label>') ) ) .hide() .appendTo(fm.getUI()); if (opts) { if (sTypes) { typeSet = opts.find('.elfinder-search-type'); jQuery.each(cmd.options.searchTypes, function(i, v) { typeSet.append(jQuery('<input id="'+id(i)+'" name="serchcol" type="radio" value="'+fm.escape(i)+'"/><label for="'+id(i)+'">'+fm.i18n(v.name)+'</label>')); }); } opts.find('div.buttonset').buttonset(); jQuery('#'+id('SearchFromAll')).next('label').attr('title', fm.i18n('searchTarget', fm.i18n('btnAll'))); if (sTypes) { jQuery.each(sTypes, function(i, v) { if (v.title) { jQuery('#'+id(i)).next('label').attr('title', fm.i18n(v.title)); } }); } opts.on('mousedown', 'div.buttonset', function(e){ e.stopPropagation(); opts.data('infocus', true); }) .on('click', 'input', function(e) { e.stopPropagation(); jQuery.trim(input.val())? search() : input.trigger('focus'); }) .on('close', function() { input.trigger('blur'); }); } }) .bind('searchend', function() { input.val(''); }) .bind('open parents', function() { var dirs = [], volroot = fm.file(fm.root(fm.cwd().hash)); if (volroot) { jQuery.each(fm.parents(fm.cwd().hash), function(i, hash) { dirs.push(fm.file(hash).name); }); jQuery('#'+id('SearchFromCwd')).next('label').attr('title', fm.i18n('searchTarget', dirs.join(fm.option('separator')))); jQuery('#'+id('SearchFromVol')).next('label').attr('title', fm.i18n('searchTarget', volroot.name)); } }) .bind('open', function() { incVal && abort(); }) .bind('cwdinit', function() { cwdReady = false; }) .bind('cwdrender',function() { cwdReady = true; }) .bind('keydownEsc', function() { if (incVal && incVal.substr(0, 1) === '/') { incVal = ''; input.val(''); fm.trigger('searchend'); } }) .shortcut({ pattern : 'ctrl+f f3', description : cmd.title, callback : function() { input.trigger('select').trigger('focus'); } }) .shortcut({ pattern : 'a b c d e f g h i j k l m n o p q r s t u v w x y z dig0 dig1 dig2 dig3 dig4 dig5 dig6 dig7 dig8 dig9 num0 num1 num2 num3 num4 num5 num6 num7 num8 num9', description : fm.i18n('firstLetterSearch'), callback : function(e) { if (! cwdReady) { return; } var code = e.originalEvent.keyCode, next = function() { var sel = fm.selected(), key = jQuery.ui.keyCode[(!sel.length || fm.cwdHash2Elm(sel[0]).next('[id]').length)? 'RIGHT' : 'HOME']; jQuery(document).trigger(jQuery.Event('keydown', { keyCode: key, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false })); }, val; if (code >= 96 && code <= 105) { code -= 48; } val = '/' + String.fromCharCode(code); if (incVal !== val) { input.val(val); incVal = val; fm .trigger('incsearchstart', { query: val }) .one('cwdrender', next); } else{ next(); } } }); }); }; /* * File: /js/ui/sortbutton.js */ /** * @class elFinder toolbar button menu with sort variants. * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfindersortbutton = function(cmd) { return this.each(function() { var fm = cmd.fm, name = cmd.name, c = 'class', disabled = fm.res(c, 'disabled'), hover = fm.res(c, 'hover'), item = 'elfinder-button-menu-item', selected = item+'-selected', asc = selected+'-asc', desc = selected+'-desc', text = jQuery('<span class="elfinder-button-text">'+cmd.title+'</span>'), button = jQuery(this).addClass('ui-state-default elfinder-button elfinder-menubutton elfiner-button-'+name) .attr('title', cmd.title) .append('<span class="elfinder-button-icon elfinder-button-icon-'+name+'"/>', text) .on('mouseenter mouseleave', function(e) { !button.hasClass(disabled) && button.toggleClass(hover, e.type === 'mouseenter'); }) .on('click', function(e) { if (!button.hasClass(disabled)) { e.stopPropagation(); menu.is(':hidden') && fm.getUI().click(); menu.css(getMenuOffset()).slideToggle({ duration: 100, done: function(e) { fm[menu.is(':visible')? 'toFront' : 'toHide'](menu); } }); } }), hide = function() { fm.toHide(menu); }, menu = jQuery('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>') .hide() .appendTo(fm.getUI()) .on('mouseenter mouseleave', '.'+item, function(e) { jQuery(this).toggleClass(hover, e.type === 'mouseenter'); }) .on('click', function(e) { e.preventDefault(); e.stopPropagation(); }) .on('close', hide), update = function() { menu.children('[rel]').removeClass(selected+' '+asc+' '+desc) .filter('[rel="'+fm.sortType+'"]') .addClass(selected+' '+(fm.sortOrder == 'asc' ? asc : desc)); menu.children('.elfinder-sort-stick').toggleClass(selected, fm.sortStickFolders); menu.children('.elfinder-sort-tree').toggleClass(selected, fm.sortAlsoTreeview); }, getMenuOffset = function() { var baseOffset = fm.getUI().offset(), buttonOffset = button.offset(); return { top : buttonOffset.top - baseOffset.top, left : buttonOffset.left - baseOffset.left }; }, tm; text.hide(); jQuery.each(fm.sortRules, function(name, value) { menu.append(jQuery('<div class="'+item+'" rel="'+name+'"><span class="ui-icon ui-icon-arrowthick-1-n"/><span class="ui-icon ui-icon-arrowthick-1-s"/>'+fm.i18n('sort'+name)+'</div>').data('type', name)); }); menu.children().on('click', function(e) { cmd.exec([], jQuery(this).removeClass(hover).attr('rel')); }); jQuery('<div class="'+item+' '+item+'-separated elfinder-sort-ext elfinder-sort-stick"><span class="ui-icon ui-icon-check"/>'+fm.i18n('sortFoldersFirst')+'</div>') .appendTo(menu) .on('click', function() { cmd.exec([], 'stick'); }); fm.one('init', function() { if (fm.ui.tree && fm.options.sortAlsoTreeview !== null) { jQuery('<div class="'+item+' '+item+'-separated elfinder-sort-ext elfinder-sort-tree"><span class="ui-icon ui-icon-check"/>'+fm.i18n('sortAlsoTreeview')+'</div>') .appendTo(menu) .on('click', function() { cmd.exec([], 'tree'); }); } }) .bind('disable select', hide) .bind('open', function() { menu.children('[rel]').each(function() { var $this = jQuery(this); $this.toggle(fm.sorters[$this.attr('rel')]); }); }).bind('sortchange', update).getUI().on('click', hide); if (menu.children().length > 1) { cmd.change(function() { tm && cancelAnimationFrame(tm); tm = requestAnimationFrame(function() { button.toggleClass(disabled, cmd.disabled()); update(); }); }) .change(); } else { button.addClass(disabled); } }); }; /* * File: /js/ui/stat.js */ /** * @class elFinder ui * Display number of files/selected files and its size in statusbar * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfinderstat = function(fm) { return this.each(function() { var size = jQuery(this).addClass('elfinder-stat-size'), sel = jQuery('<div class="elfinder-stat-selected"/>') .on('click', 'a', function(e) { var hash = jQuery(this).data('hash'); e.preventDefault(); fm.exec('opendir', [ hash ]); }), titleitems = fm.i18n('items'), titlesel = fm.i18n('selected'), titlesize = fm.i18n('size'), setstat = function(files) { var c = 0, s = 0, cwd = fm.cwd(), calc = true, hasSize = true; if (cwd.sizeInfo || cwd.size) { s = cwd.size; calc = false; } jQuery.each(files, function(i, file) { c++; if (calc) { s += parseInt(file.size) || 0; if (hasSize === true && file.mime === 'directory' && !file.sizeInfo) { hasSize = false; } } }); size.html(titleitems+': <span class="elfinder-stat-incsearch"></span>'+c+', <span class="elfinder-stat-size'+(hasSize? ' elfinder-stat-size-recursive' : '')+'">'+fm.i18n(hasSize? 'sum' : 'size')+': '+fm.formatSize(s)+'</span>') .attr('title', size.text()); fm.trigger('uistatchange'); }, setIncsearchStat = function(data) { size.find('span.elfinder-stat-incsearch').html(data? data.hashes.length + ' / ' : ''); size.attr('title', size.text()); fm.trigger('uistatchange'); }, setSelect = function(files) { var s = 0, c = 0, dirs = [], path, file; if (files.length === 1) { file = files[0]; s = file.size; if (fm.searchStatus.state === 2) { path = fm.escape(file.path? file.path.replace(/\/[^\/]*$/, '') : '..'); dirs.push('<a href="#elf_'+file.phash+'" data-hash="'+file.hash+'" title="'+path+'">'+path+'</a>'); } dirs.push(fm.escape(file.i18 || file.name)); sel.html(dirs.join('/') + (s > 0 ? ', '+fm.formatSize(s) : '')); } else if (files.length) { jQuery.each(files, function(i, file) { c++; s += parseInt(file.size)||0; }); sel.html(c ? titlesel+': '+c+', '+titlesize+': '+fm.formatSize(s) : ' '); } else { sel.html(''); } sel.attr('title', sel.text()); fm.trigger('uistatchange'); }; fm.getUI('statusbar').prepend(size).append(sel).show(); if (fm.UA.Mobile && jQuery.fn.tooltip) { fm.getUI('statusbar').tooltip({ classes: { 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow' }, tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow', track: true }); } fm .bind('cwdhasheschange', function(e) { setstat(jQuery.map(e.data, function(h) { return fm.file(h); })); }) .change(function(e) { var files = e.data.changed || [], cwdHash = fm.cwd().hash; jQuery.each(files, function() { if (this.hash === cwdHash) { if (this.size) { size.children('.elfinder-stat-size').addClass('elfinder-stat-size-recursive').html(fm.i18n('sum')+': '+fm.formatSize(this.size)); size.attr('title', size.text()); } return false; } }); }) .select(function() { setSelect(fm.selectedFiles()); }) .bind('open', function() { setSelect([]); }) .bind('incsearch', function(e) { setIncsearchStat(e.data); }) .bind('incsearchend', function() { setIncsearchStat(); }) ; }); }; /* * File: /js/ui/toast.js */ /** * @class elFinder toast * * This was created inspired by the toastr. Thanks to developers of toastr. * CodeSeven/toastr: http://johnpapa.net <https://github.com/CodeSeven/toastr> * * @author Naoki Sawada **/ jQuery.fn.elfindertoast = function(opts, fm) { var defOpts = Object.assign({ mode: 'success', // or 'info', 'warning' and 'error' msg: '', showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery showDuration: 300, showEasing: 'swing', //swing and linear are built into jQuery onShown: undefined, hideMethod: 'fadeOut', hideDuration: 1500, hideEasing: 'swing', onHidden: undefined, timeOut: 3000, extNode: undefined, button: undefined, width: undefined }, jQuery.isPlainObject(fm.options.uiOptions.toast.defaults)? fm.options.uiOptions.toast.defaults : {}); return this.each(function() { opts = Object.assign({}, defOpts, opts || {}); var self = jQuery(this), show = function(notm) { self.stop(); fm.toFront(self); self[opts.showMethod]({ duration: opts.showDuration, easing: opts.showEasing, complete: function() { opts.onShown && opts.onShown(); if (!notm && opts.timeOut) { rmTm = setTimeout(rm, opts.timeOut); } } }); }, rm = function() { self[opts.hideMethod]({ duration: opts.hideDuration, easing: opts.hideEasing, complete: function() { opts.onHidden && opts.onHidden(); self.remove(); } }); }, rmTm; self.on('click', function(e) { e.stopPropagation(); e.preventDefault(); rmTm && clearTimeout(rmTm); opts.onHidden && opts.onHidden(); self.stop().remove(); }).on('mouseenter mouseleave', function(e) { if (opts.timeOut) { rmTm && clearTimeout(rmTm); rmTm = null; if (e.type === 'mouseenter') { show(true); } else { rmTm = setTimeout(rm, opts.timeOut); } } }).hide().addClass('toast-' + opts.mode).append(jQuery('<div class="elfinder-toast-msg"/>').html(opts.msg.replace(/%([a-zA-Z0-9]+)%/g, function(m, m1) { return fm.i18n(m1); }))); if (opts.extNode) { self.append(opts.extNode); } if (opts.button) { self.append( jQuery('<button class="ui-button ui-widget ui-state-default ui-corner-all elfinder-tabstop"/>') .append(jQuery('<span class="ui-button-text"/>').text(fm.i18n(opts.button.text))) .on('mouseenter mouseleave', function(e) { jQuery(this).toggleClass('ui-state-hover', e.type == 'mouseenter'); }) .on('click', opts.button.click || function(){}) ); } if (opts.width) { self.css('max-width', opts.width); } show(); }); }; /* * File: /js/ui/toolbar.js */ /** * @class elFinder toolbar * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfindertoolbar = function(fm, opts) { this.not('.elfinder-toolbar').each(function() { var commands = fm._commands, self = jQuery(this).addClass('ui-helper-clearfix ui-widget-header elfinder-toolbar'), options = { // default options displayTextLabel: false, labelExcludeUA: ['Mobile'], autoHideUA: ['Mobile'], showPreferenceButton: 'none' }, filter = function(opts) { return jQuery.grep(opts, function(v) { if (jQuery.isPlainObject(v)) { options = Object.assign(options, v); return false; } return true; }); }, render = function(disabled){ var name,cmdPref; jQuery.each(buttons, function(i, b) { b.detach(); }); self.empty(); l = panels.length; while (l--) { if (panels[l]) { panel = jQuery('<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>'); i = panels[l].length; while (i--) { name = panels[l][i]; if ((!disabled || !disabled[name]) && (cmd = commands[name])) { button = 'elfinder'+cmd.options.ui; if (! buttons[name] && jQuery.fn[button]) { buttons[name] = jQuery('<div/>')[button](cmd); } if (buttons[name]) { buttons[name].children('.elfinder-button-text')[textLabel? 'show' : 'hide'](); panel.prepend(buttons[name]); } } } panel.children().length && self.prepend(panel); panel.children(':gt(0)').before('<span class="ui-widget-content elfinder-toolbar-button-separator"/>'); } } if (cmdPref = commands['preference']) { //cmdPref.state = !self.children().length? 0 : -1; if (options.showPreferenceButton === 'always' || (!self.children().length && options.showPreferenceButton === 'auto')) { //cmdPref.state = 0; panel = jQuery('<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>'); name = 'preference'; button = 'elfinder'+cmd.options.ui; buttons[name] = jQuery('<div/>')[button](cmdPref); buttons[name].children('.elfinder-button-text')[textLabel? 'show' : 'hide'](); panel.prepend(buttons[name]); self.append(panel); } } (! self.data('swipeClose') && self.children().length)? self.show() : self.hide(); prevHeight = self[0].clientHeight; fm.trigger('toolbarload').trigger('uiresize'); }, buttons = {}, panels = filter(opts || []), dispre = null, uiCmdMapPrev = '', prevHeight = 0, contextRaw = [], l, i, cmd, panel, button, swipeHandle, autoHide, textLabel, resizeTm; // normalize options options.showPreferenceButton = options.showPreferenceButton.toLowerCase(); if (options.displayTextLabel !== 'none') { // correction of options.displayTextLabel textLabel = fm.storage('toolbarTextLabel'); if (textLabel === null) { textLabel = (options.displayTextLabel && (! options.labelExcludeUA || ! options.labelExcludeUA.length || ! jQuery.grep(options.labelExcludeUA, function(v){ return fm.UA[v]? true : false; }).length)); } else { textLabel = (textLabel == 1); } contextRaw.push({ label : fm.i18n('textLabel'), icon : 'text', callback : function() { textLabel = ! textLabel; self.css('height', '').find('.elfinder-button-text')[textLabel? 'show':'hide'](); fm.trigger('uiresize').storage('toolbarTextLabel', textLabel? '1' : '0'); }, }); } if (options.preferenceInContextmenu && commands['preference']) { contextRaw.push({ label : fm.i18n('toolbarPref'), icon : 'preference', callback : function() { fm.exec('preference', void(0), {tab: 'toolbar'}); } }); } // add contextmenu if (contextRaw.length) { self.on('contextmenu', function(e) { e.stopPropagation(); e.preventDefault(); fm.trigger('contextmenu', { raw: contextRaw, x: e.pageX, y: e.pageY }); }).on('touchstart', function(e) { if (e.originalEvent.touches.length > 1) { return; } self.data('tmlongtap') && clearTimeout(self.data('tmlongtap')); self.removeData('longtap') .data('longtap', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY}) .data('tmlongtap', setTimeout(function() { self.removeData('longtapTm') .trigger({ type: 'contextmenu', pageX: self.data('longtap').x, pageY: self.data('longtap').y }) .data('longtap', {longtap: true}); }, 500)); }).on('touchmove touchend', function(e) { if (self.data('tmlongtap')) { if (e.type === 'touchend' || ( Math.abs(self.data('longtap').x - e.originalEvent.touches[0].pageX) + Math.abs(self.data('longtap').y - e.originalEvent.touches[0].pageY)) > 4) clearTimeout(self.data('tmlongtap')); self.removeData('longtapTm'); } }).on('click', function(e) { if (self.data('longtap') && self.data('longtap').longtap) { e.stopImmediatePropagation(); e.preventDefault(); } }).on('touchend click', '.elfinder-button', function(e) { if (self.data('longtap') && self.data('longtap').longtap) { e.stopImmediatePropagation(); e.preventDefault(); } } ); } self.prev().length && self.parent().prepend(this); render(); fm.bind('open sync select toolbarpref', function() { var disabled = Object.assign({}, fm.option('disabledFlip')), userHides = fm.storage('toolbarhides'), doRender, sel, disabledKeys; if (! userHides && Array.isArray(options.defaultHides)) { userHides = {}; jQuery.each(options.defaultHides, function() { userHides[this] = true; }); fm.storage('toolbarhides', userHides); } if (this.type === 'select') { if (fm.searchStatus.state < 2) { return; } sel = fm.selected(); if (sel.length) { disabled = fm.getDisabledCmds(sel, true); } } jQuery.each(userHides, function(n) { if (!disabled[n]) { disabled[n] = true; } }); if (Object.keys(fm.commandMap).length) { jQuery.each(fm.commandMap, function(from, to){ if (to === 'hidden') { disabled[from] = true; } }); } disabledKeys = Object.keys(disabled); if (!dispre || dispre.toString() !== disabledKeys.sort().toString()) { render(disabledKeys.length? disabled : null); doRender = true; } dispre = disabledKeys.sort(); if (doRender || uiCmdMapPrev !== JSON.stringify(fm.commandMap)) { uiCmdMapPrev = JSON.stringify(fm.commandMap); if (! doRender) { // reset toolbar jQuery.each(jQuery('div.elfinder-button'), function(){ var origin = jQuery(this).data('origin'); if (origin) { jQuery(this).after(origin).detach(); } }); } if (Object.keys(fm.commandMap).length) { jQuery.each(fm.commandMap, function(from, to){ var cmd = fm._commands[to], button = cmd? 'elfinder'+cmd.options.ui : null, btn; if (button && jQuery.fn[button]) { btn = buttons[from]; if (btn) { if (! buttons[to] && jQuery.fn[button]) { buttons[to] = jQuery('<div/>')[button](cmd); if (buttons[to]) { buttons[to].children('.elfinder-button-text')[textLabel? 'show' : 'hide'](); if (cmd.extendsCmd) { buttons[to].children('span.elfinder-button-icon').addClass('elfinder-button-icon-' + cmd.extendsCmd); } } } if (buttons[to]) { btn.after(buttons[to]); buttons[to].data('origin', btn.detach()); } } } }); } } }).bind('resize', function(e) { resizeTm && cancelAnimationFrame(resizeTm); resizeTm = requestAnimationFrame(function() { var h = self[0].clientHeight; if (prevHeight !== h) { prevHeight = h; fm.trigger('uiresize'); } }); }); if (fm.UA.Touch) { autoHide = fm.storage('autoHide') || {}; if (typeof autoHide.toolbar === 'undefined') { autoHide.toolbar = (options.autoHideUA && options.autoHideUA.length > 0 && jQuery.grep(options.autoHideUA, function(v){ return fm.UA[v]? true : false; }).length); fm.storage('autoHide', autoHide); } if (autoHide.toolbar) { fm.one('init', function() { fm.uiAutoHide.push(function(){ self.stop(true, true).trigger('toggle', { duration: 500, init: true }); }); }); } fm.bind('load', function() { swipeHandle = jQuery('<div class="elfinder-toolbar-swipe-handle"/>').hide().appendTo(fm.getUI()); if (swipeHandle.css('pointer-events') !== 'none') { swipeHandle.remove(); swipeHandle = null; } }); self.on('toggle', function(e, data) { var wz = fm.getUI('workzone'), toshow= self.is(':hidden'), wzh = wz.height(), h = self.height(), tbh = self.outerHeight(true), delta = tbh - h, opt = Object.assign({ step: function(now) { wz.height(wzh + (toshow? (now + delta) * -1 : h - now)); fm.trigger('resize'); }, always: function() { requestAnimationFrame(function() { self.css('height', ''); fm.trigger('uiresize'); if (swipeHandle) { if (toshow) { swipeHandle.stop(true, true).hide(); } else { swipeHandle.height(data.handleH? data.handleH : ''); fm.resources.blink(swipeHandle, 'slowonce'); } } toshow && self.scrollTop('0px'); data.init && fm.trigger('uiautohide'); }); } }, data); self.data('swipeClose', ! toshow).stop(true, true).animate({height : 'toggle'}, opt); autoHide.toolbar = !toshow; fm.storage('autoHide', Object.assign(fm.storage('autoHide'), {toolbar: autoHide.toolbar})); }).on('touchstart', function(e) { if (self.scrollBottom() > 5) { e.originalEvent._preventSwipeY = true; } }); } }); return this; }; /* * File: /js/ui/tree.js */ /** * @class elFinder folders tree * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfindertree = function(fm, opts) { var treeclass = fm.res('class', 'tree'); this.not('.'+treeclass).each(function() { var c = 'class', mobile = fm.UA.Mobile, /** * Root directory class name * * @type String */ root = fm.res(c, 'treeroot'), /** * Open root dir if not opened yet * * @type Boolean */ openRoot = opts.openRootOnLoad, /** * Open current work dir if not opened yet * * @type Boolean */ openCwd = opts.openCwdOnOpen, /** * Auto loading current directory parents and do expand their node * * @type Boolean */ syncTree = openCwd || opts.syncTree, /** * Subtree class name * * @type String */ subtree = fm.res(c, 'navsubtree'), /** * Directory class name * * @type String */ navdir = fm.res(c, 'treedir'), /** * Directory CSS selector * * @type String */ selNavdir = 'span.' + navdir, /** * Collapsed arrow class name * * @type String */ collapsed = fm.res(c, 'navcollapse'), /** * Expanded arrow class name * * @type String */ expanded = fm.res(c, 'navexpand'), /** * Class name to mark arrow for directory with already loaded children * * @type String */ loaded = 'elfinder-subtree-loaded', /** * Class name to mark need subdirs request * * @type String */ chksubdir = 'elfinder-subtree-chksubdir', /** * Arraw class name * * @type String */ arrow = fm.res(c, 'navarrow'), /** * Current directory class name * * @type String */ active = fm.res(c, 'active'), /** * Droppable dirs dropover class * * @type String */ dropover = fm.res(c, 'adroppable'), /** * Hover class name * * @type String */ hover = fm.res(c, 'hover'), /** * Disabled dir class name * * @type String */ disabled = fm.res(c, 'disabled'), /** * Draggable dir class name * * @type String */ draggable = fm.res(c, 'draggable'), /** * Droppable dir class name * * @type String */ droppable = fm.res(c, 'droppable'), /** * root wrapper class * * @type String */ wrapperRoot = 'elfinder-navbar-wrapper-root', /** * Un-disabled cmd `paste` volume's root wrapper class * * @type String */ pastable = 'elfinder-navbar-wrapper-pastable', /** * Un-disabled cmd `upload` volume's root wrapper class * * @type String */ uploadable = 'elfinder-navbar-wrapper-uploadable', /** * Is position x inside Navbar * * @param x Numbar * * @return */ insideNavbar = function(x) { var left = navbar.offset().left; return left <= x && x <= left + navbar.width(); }, /** * To call subdirs elements queue * * @type Object */ subdirsQue = {}, /** * To exec subdirs elements ids * */ subdirsExecQue = [], /** * Request subdirs to backend * * @param id String * * @return Deferred */ subdirs = function(ids) { var targets = []; jQuery.each(ids, function(i, id) { subdirsQue[id] && targets.push(fm.navId2Hash(id)); delete subdirsQue[id]; }); if (targets.length) { return fm.request({ data: { cmd: 'subdirs', targets: targets, preventDefault : true } }).done(function(res) { if (res && res.subdirs) { jQuery.each(res.subdirs, function(hash, subdirs) { var elm = fm.navHash2Elm(hash); elm.removeClass(chksubdir); elm[subdirs? 'addClass' : 'removeClass'](collapsed); }); } }); } }, subdirsJobRes = null, /** * To check target element is in window of subdirs * * @return void */ checkSubdirs = function() { var ids = Object.keys(subdirsQue); if (ids.length) { subdirsJobRes && subdirsJobRes._abort(); execSubdirsTm && clearTimeout(execSubdirsTm); subdirsExecQue = []; subdirsJobRes = fm.asyncJob(function(id) { return fm.isInWindow(jQuery('#'+id))? id : null; }, ids, { numPerOnce: 200 }) .done(function(arr) { if (arr.length) { subdirsExecQue = arr; execSubdirs(); } }); } }, subdirsPending = 0, execSubdirsTm, /** * Exec subdirs as batch request * * @return void */ execSubdirs = function() { var cnt = opts.subdirsMaxConn - subdirsPending, atOnce = fm.maxTargets? Math.min(fm.maxTargets, opts.subdirsAtOnce) : opts.subdirsAtOnce, i, ids; execSubdirsTm && cancelAnimationFrame(execSubdirsTm); if (subdirsExecQue.length) { if (cnt > 0) { for (i = 0; i < cnt; i++) { if (subdirsExecQue.length) { subdirsPending++; subdirs(subdirsExecQue.splice(0, atOnce)).always(function() { subdirsPending--; execSubdirs(); }); } } } else { execSubdirsTm = requestAnimationFrame(function() { subdirsExecQue.length && execSubdirs(); }); } } }, drop = fm.droppable.drop, /** * Droppable options * * @type Object */ droppableopts = jQuery.extend(true, {}, fm.droppable, { // show subfolders on dropover over : function(e, ui) { var dst = jQuery(this), helper = ui.helper, cl = hover+' '+dropover, hash, status; e.stopPropagation(); helper.data('dropover', helper.data('dropover') + 1); dst.data('dropover', true); if (ui.helper.data('namespace') !== fm.namespace || ! fm.insideWorkzone(e.pageX, e.pageY)) { dst.removeClass(cl); helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus'); return; } if (! insideNavbar(e.clientX)) { dst.removeClass(cl); return; } helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus'); dst.addClass(hover); if (dst.is('.'+collapsed+':not(.'+expanded+')')) { dst.data('expandTimer', setTimeout(function() { dst.is('.'+collapsed+'.'+hover) && dst.children('.'+arrow).trigger('click'); }, 500)); } if (dst.is('.elfinder-ro,.elfinder-na')) { dst.removeClass(dropover); //helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus'); return; } hash = fm.navId2Hash(dst.attr('id')); dst.data('dropover', hash); jQuery.each(ui.helper.data('files'), function(i, h) { if (h === hash || (fm.file(h).phash === hash && !ui.helper.hasClass('elfinder-drag-helper-plus'))) { dst.removeClass(cl); return false; // break jQuery.each } }); if (helper.data('locked')) { status = 'elfinder-drag-helper-plus'; } else { status = 'elfinder-drag-helper-move'; if (e.shiftKey || e.ctrlKey || e.metaKey) { status += ' elfinder-drag-helper-plus'; } } dst.hasClass(dropover) && helper.addClass(status); requestAnimationFrame(function(){ dst.hasClass(dropover) && helper.addClass(status); }); }, out : function(e, ui) { var dst = jQuery(this), helper = ui.helper; e.stopPropagation(); if (insideNavbar(e.clientX)) { helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus'); } helper.data('dropover', Math.max(helper.data('dropover') - 1, 0)); dst.data('expandTimer') && clearTimeout(dst.data('expandTimer')); dst.removeData('dropover') .removeClass(hover+' '+dropover); }, deactivate : function() { jQuery(this).removeData('dropover') .removeClass(hover+' '+dropover); }, drop : function(e, ui) { insideNavbar(e.clientX) && drop.call(this, e, ui); } }), spinner = jQuery(fm.res('tpl', 'navspinner')), /** * Directory html template * * @type String */ tpl = fm.res('tpl', 'navdir'), /** * Permissions marker html template * * @type String */ ptpl = fm.res('tpl', 'perms'), /** * Lock marker html template * * @type String */ ltpl = fm.res('tpl', 'lock'), /** * Symlink marker html template * * @type String */ stpl = fm.res('tpl', 'symlink'), /** * Directory hashes that has more pages * * @type Object */ hasMoreDirs = {}, /** * Html template replacement methods * * @type Object */ replace = { id : function(dir) { return fm.navHash2Id(dir.hash); }, name : function(dir) { return fm.escape(dir.i18 || dir.name); }, cssclass : function(dir) { var cname = (dir.phash && ! dir.isroot ? '' : root)+' '+navdir+' '+fm.perms2class(dir); dir.dirs && !dir.link && (cname += ' ' + collapsed) && dir.dirs == -1 && (cname += ' ' + chksubdir); opts.getClass && (cname += ' ' + opts.getClass(dir)); dir.csscls && (cname += ' ' + fm.escape(dir.csscls)); return cname; }, root : function(dir) { var cls = ''; if (!dir.phash || dir.isroot) { cls += ' '+wrapperRoot; if (!dir.disabled || dir.disabled.length < 1) { cls += ' '+pastable+' '+uploadable; } else { if (jQuery.inArray('paste', dir.disabled) === -1) { cls += ' '+pastable; } if (jQuery.inArray('upload', dir.disabled) === -1) { cls += ' '+uploadable; } } return cls; } else { return ''; } }, permissions : function(dir) { return !dir.read || !dir.write ? ptpl : ''; }, symlink : function(dir) { return dir.alias ? stpl : ''; }, style : function(dir) { return dir.icon ? fm.getIconStyle(dir) : ''; } }, /** * Return html for given dir * * @param Object directory * @return String */ itemhtml = function(dir) { return tpl.replace(/(?:\{([a-z]+)\})/ig, function(m, key) { var res = replace[key] ? replace[key](dir) : (dir[key] || ''); if (key === 'id' && dir.dirs == -1) { subdirsQue[res] = res; } return res; }); }, /** * Return only dirs from files list * * @param Array files list * @param Boolean do check exists * @return Array */ filter = function(files, checkExists) { return jQuery.map(files || [], function(f) { return (f.mime === 'directory' && (!checkExists || fm.navHash2Elm(f.hash).length)) ? f : null; }); }, /** * Find parent subtree for required directory * * @param String dir hash * @return jQuery */ findSubtree = function(hash) { return hash ? fm.navHash2Elm(hash).next('.'+subtree) : tree; }, /** * Find directory (wrapper) in required node * before which we can insert new directory * * @param jQuery parent directory * @param Object new directory * @return jQuery */ findSibling = function(subtree, dir) { var node = subtree.children(':first'), info; while (node.length) { info = fm.file(fm.navId2Hash(node.children('[id]').attr('id'))); if ((info = fm.file(fm.navId2Hash(node.children('[id]').attr('id')))) && compare(dir, info) < 0) { return node; } node = node.next(); } return subtree.children('button.elfinder-navbar-pager-next'); }, /** * Add new dirs in tree * * @param Array dirs list * @return void */ updateTree = function(dirs) { var length = dirs.length, orphans = [], i = length, tgts = jQuery(), done = {}, cwd = fm.cwd(), append = function(parent, dirs, start, direction) { var hashes = {}, curStart = 0, max = fm.newAPI? Math.min(10000, Math.max(10, opts.subTreeMax)) : 10000, setHashes = function() { hashes = {}; jQuery.each(dirs, function(i, d) { hashes[d.hash] = i; }); }, change = function(mode) { if (mode === 'prepare') { jQuery.each(dirs, function(i, d) { d.node && parent.append(d.node.hide()); }); } else if (mode === 'done') { jQuery.each(dirs, function(i, d) { d.node && d.node.detach().show(); }); } }, update = function(e, data) { var i, changed; e.stopPropagation(); if (data.select) { render(getStart(data.select)); return; } if (data.change) { change(data.change); return; } if (data.removed && data.removed.length) { dirs = jQuery.grep(dirs, function(d) { if (data.removed.indexOf(d.hash) === -1) { return true; } else { !changed && (changed = true); return false; } }); } if (data.added && data.added.length) { dirs = dirs.concat(jQuery.grep(data.added, function(d) { if (hashes[d.hash] === void(0)) { !changed && (changed = true); return true; } else { return false; } })); } if (changed) { dirs.sort(compare); setHashes(); render(curStart); } }, getStart = function(target) { if (hashes[target] !== void(0)) { return Math.floor(hashes[target] / max) * max; } return void(0); }, target = fm.navId2Hash(parent.prev('[id]').attr('id')), render = function(start, direction) { var html = [], nodes = {}, total, page, s, parts, prev, next, prevBtn, nextBtn; delete hasMoreDirs[target]; curStart = start; parent.off('update.'+fm.namespace, update); if (dirs.length > max) { parent.on('update.'+fm.namespace, update); if (start === void(0)) { s = 0; setHashes(); start = getStart(cwd.hash); if (start === void(0)) { start = 0; } } parts = dirs.slice(start, start + max); hasMoreDirs[target] = parent; prev = start? Math.max(-1, start - max) : -1; next = (start + max >= dirs.length)? 0 : start + max; total = Math.ceil(dirs.length/max); page = Math.ceil(start/max); } jQuery.each(parts || dirs, function(i, d) { html.push(itemhtml(d)); if (d.node) { nodes[d.hash] = d.node; } }); if (prev > -1) { prevBtn = jQuery('<button class="elfinder-navbar-pager elfinder-navbar-pager-prev"/>') .text(fm.i18n('btnPrevious', page, total)) .button({ icons: { primary: "ui-icon-caret-1-n" } }) .on('click', function(e) { e.preventDefault(); e.stopPropagation(); render(prev, 'up'); }); } else { prevBtn = jQuery(); } if (next) { nextBtn = jQuery('<button class="elfinder-navbar-pager elfinder-navbar-pager-next"/>') .text(fm.i18n('btnNext', page + 2, total)) .button({ icons: { primary: "ui-icon-caret-1-s" } }) .on('click', function(e) { e.preventDefault(); e.stopPropagation(); render(next, 'down'); }); } else { nextBtn = jQuery(); } detach(); parent.empty()[parts? 'addClass' : 'removeClass']('elfinder-navbar-hasmore').append(prevBtn, html.join(''), nextBtn); jQuery.each(nodes, function(h, n) { fm.navHash2Elm(h).parent().replaceWith(n); }); if (direction) { autoScroll(fm.navHash2Id(parts[direction === 'up'? parts.length - 1 : 0].hash)); } ! mobile && fm.lazy(function() { updateDroppable(null, parent); }); }, detach = function() { jQuery.each(parent.children('.elfinder-navbar-wrapper'), function(i, elm) { var n = jQuery(elm), ch = n.children('[id]:first'), h, c; if (ch.hasClass(loaded)) { h = fm.navId2Hash(ch.attr('id')); if (h && (c = hashes[h]) !== void(0)) { dirs[c].node = n.detach(); } } }); }; render(); }, dir, html, parent, sibling, init, atonce = {}, updates = [], base, node, firstVol = true; // check for netmount volume while (i--) { dir = dirs[i]; if (done[dir.hash] || fm.navHash2Elm(dir.hash).length) { continue; } done[dir.hash] = true; if ((parent = findSubtree(dir.phash)).length) { if (dir.phash && ((init = !parent.children().length) || parent.hasClass('elfinder-navbar-hasmore') || (sibling = findSibling(parent, dir)).length)) { if (init) { if (!atonce[dir.phash]) { atonce[dir.phash] = []; } atonce[dir.phash].push(dir); } else { if (sibling) { node = itemhtml(dir); sibling.before(node); ! mobile && (tgts = tgts.add(node)); } else { updates.push(dir); } } } else { node = itemhtml(dir); parent[firstVol || dir.phash ? 'append' : 'prepend'](node); firstVol = false; if (!dir.phash || dir.isroot) { base = fm.navHash2Elm(dir.hash).parent(); } ! mobile && updateDroppable(null, base); } } else { orphans.push(dir); } } // When init, html append at once if (Object.keys(atonce).length){ jQuery.each(atonce, function(p, dirs){ var parent = findSubtree(p), html = []; dirs.sort(compare); append(parent, dirs); }); } if (updates.length) { parent.trigger('update.' + fm.namespace, { added : updates }); } if (orphans.length && orphans.length < length) { updateTree(orphans); return; } ! mobile && tgts.length && fm.lazy(function() { updateDroppable(tgts); }); }, /** * sort function by dir.name * */ compare = function(dir1, dir2) { if (! fm.sortAlsoTreeview) { return fm.sortRules.name(dir1, dir2); } else { var asc = fm.sortOrder == 'asc', type = fm.sortType, rules = fm.sortRules, res; res = asc? rules[fm.sortType](dir1, dir2) : rules[fm.sortType](dir2, dir1); return type !== 'name' && res === 0 ? res = asc ? rules.name(dir1, dir2) : rules.name(dir2, dir1) : res; } }, /** * Timer ID of autoScroll * * @type Integer */ autoScrTm, /** * Auto scroll to cwd * * @return Object jQuery Deferred */ autoScroll = function(target) { var dfrd = jQuery.Deferred(), current, parent, top, treeH, bottom, tgtTop; autoScrTm && clearTimeout(autoScrTm); autoScrTm = setTimeout(function() { current = jQuery(document.getElementById((target || fm.navHash2Id(fm.cwd().hash)))); if (current.length) { // expand parents directory (openCwd? current : current.parent()).parents('.elfinder-navbar-wrapper').children('.'+loaded).addClass(expanded).next('.'+subtree).show(); parent = tree.parent().stop(false, true); top = parent.offset().top; treeH = parent.height(); bottom = top + treeH - current.outerHeight(); tgtTop = current.offset().top; if (tgtTop < top || tgtTop > bottom) { parent.animate({ scrollTop : parent.scrollTop() + tgtTop - top - treeH / 3 }, { duration : opts.durations.autoScroll, complete : function() { dfrd.resolve(); } }); } else { dfrd.resolve(); } } else { dfrd.reject(); } }, 100); return dfrd; }, /** * Get hashes array of items of the bottom of the leaf root back from the target * * @param Object elFinder item(directory) object * @return Array hashes */ getEnds = function(d) { var cur = d || fm.cwd(), res = cur.hash? [ cur.hash ] : [], phash, root, dir; root = fm.root(cur.hash); dir = fm.file(root); while (dir && (phash = dir.phash)) { res.unshift(phash); root = fm.root(phash); dir = fm.file(root); if (fm.navHash2Elm(dir.hash).hasClass(loaded)) { break; } } return res; }, /** * Select pages back in order to display the target * * @param Object elFinder item(directory) object * @return Object jQuery node object of target node */ selectPages = function(current) { var cur = current || fm.cwd(), curHash = cur.hash, node = fm.navHash2Elm(curHash); if (!node.length) { while(cur && cur.phash) { if (hasMoreDirs[cur.phash] && !fm.navHash2Elm(cur.hash).length) { hasMoreDirs[cur.phash].trigger('update.'+fm.namespace, { select : cur.hash }); } cur = fm.file(cur.phash); } node = fm.navHash2Elm(curHash); } return node; }, /** * Flag indicating that synchronization is currently in progress * * @type Boolean */ syncing, /** * Mark current directory as active * If current directory is not in tree - load it and its parents * * @param Array directory objects of cwd * @param Boolean do auto scroll * @return Object jQuery Deferred */ sync = function(cwdDirs, aScr) { var cwd = fm.cwd(), cwdhash = cwd.hash, autoScr = aScr === void(0)? syncTree : aScr, loadParents = function(dir) { var dfd = jQuery.Deferred(), reqs = [], ends = getEnds(dir), makeReq = function(cmd, h, until) { var data = { cmd : cmd, target : h }; if (until) { data.until = until; } return fm.request({ data : data, preventFail : true }); }, baseHash, baseId; reqs = jQuery.map(ends, function(h) { var d = fm.file(h), isRoot = d? fm.isRoot(d) : false, node = fm.navHash2Elm(h), getPhash = function(h, dep) { var d, ph, depth = dep || 1; ph = (d = fm.file(h))? d.phash : false; if (ph && depth > 1) { return getPhash(ph, --depth); } return ph; }, until, closest = (function() { var phash = getPhash(h); until = phash; while (phash) { if (fm.navHash2Elm(phash).hasClass(loaded)) { break; } until = phash; phash = getPhash(phash); } if (!phash) { until = void(0); phash = fm.root(h); } return phash; })(), cmd; if (!node.hasClass(loaded) && (isRoot || !d || !fm.navHash2Elm(d.phash).hasClass(loaded))) { if (isRoot || closest === getPhash(h) || closest === getPhash(h, 2)) { until = void(0); cmd = 'tree'; if (!isRoot) { h = getPhash(h); } } else { cmd = 'parents'; } if (!baseHash) { baseHash = (cmd === 'tree')? h : closest; } return makeReq(cmd, h, until); } return null; }); if (reqs.length) { selectPages(fm.file(baseHash)); baseId = fm.navHash2Id(baseHash); autoScr && autoScroll(baseId); baseNode = jQuery('#'+baseId); spinner = jQuery(fm.res('tpl', 'navspinner')).insertBefore(baseNode.children('.'+arrow)); baseNode.removeClass(collapsed); jQuery.when.apply($, reqs) .done(function() { var res = {},data, treeDirs, dirs, argLen, i; argLen = arguments.length; if (argLen > 0) { for (i = 0; i < argLen; i++) { data = arguments[i].tree || []; res[ends[i]] = Object.assign([], filter(data)); } } dfd.resolve(res); }) .fail(function() { dfd.reject(); }); return dfd; } else { return dfd.resolve(); } }, done= function(res, dfrd) { var open = function() { if (openRoot && baseNode) { findSubtree(baseNode.hash).show().prev(selNavdir).addClass(expanded); openRoot = false; } if (autoScr) { autoScroll().done(checkSubdirs); } else { checkSubdirs(); } }, current; if (res) { jQuery.each(res, function(endHash, dirs) { dirs && updateTree(dirs); selectPages(fm.file(endHash)); dirs && updateArrows(dirs, loaded); }); } if (cwdDirs) { (fm.api < 2.1) && cwdDirs.push(cwd); updateTree(cwdDirs); } // set current node current = selectPages(); if (!current.hasClass(active)) { tree.find(selNavdir+'.'+active).removeClass(active); current.addClass(active); } // mark as loaded to cwd parents current.parents('.elfinder-navbar-wrapper').children('.'+navdir).addClass(loaded); if (res) { fm.lazy(open).done(function() { dfrd.resolve(); }); } else { open(); dfrd.resolve(); } }, rmSpinner = function(fail) { if (baseNode) { spinner.remove(); baseNode.addClass(collapsed + (fail? '' : (' ' + loaded))); } }, dfrd = jQuery.Deferred(), baseNode, spinner; if (!fm.navHash2Elm(cwdhash).length) { syncing = true; loadParents() .done(function(res) { done(res, dfrd); rmSpinner(); }) .fail(function() { rmSpinner(true); dfrd.reject(); }) .always(function() { syncing = false; }); } else { done(void(0), dfrd); } // trigger 'treesync' with my jQuery.Deferred fm.trigger('treesync', dfrd); return dfrd; }, /** * Make writable and not root dirs droppable * * @return void */ updateDroppable = function(target, node) { var limit = 100, next; if (!target) { if (!node || node.closest('div.'+wrapperRoot).hasClass(uploadable)) { (node || tree.find('div.'+uploadable)).find(selNavdir+':not(.elfinder-ro,.elfinder-na)').addClass('native-droppable'); } if (!node || node.closest('div.'+wrapperRoot).hasClass(pastable)) { target = (node || tree.find('div.'+pastable)).find(selNavdir+':not(.'+droppable+')'); } else { target = jQuery(); } if (node) { // check leaf roots node.children('div.'+wrapperRoot).each(function() { updateDroppable(null, jQuery(this)); }); } } // make droppable on async if (target.length) { fm.asyncJob(function(elm) { jQuery(elm).droppable(droppableopts); }, jQuery.makeArray(target), { interval : 20, numPerOnce : 100 }); } }, /** * Check required folders for subfolders and update arrow classes * * @param Array folders to check * @param String css class * @return void */ updateArrows = function(dirs, cls) { var sel = cls == loaded ? '.'+collapsed+':not(.'+loaded+')' : ':not(.'+collapsed+')'; jQuery.each(dirs, function(i, dir) { fm.navHash2Elm(dir.phash).filter(sel) .filter(function() { return jQuery.grep(jQuery(this).next('.'+subtree).children(), function(n) { return (jQuery(n).children().hasClass(root))? false : true; }).length > 0; }) .addClass(cls); }); }, /** * Navigation tree * * @type JQuery */ tree = jQuery(this).addClass(treeclass) // make dirs draggable and toggle hover class .on('mouseenter mouseleave', selNavdir, function(e) { var enter = (e.type === 'mouseenter'); if (enter && scrolling) { return; } var link = jQuery(this); if (!link.hasClass(dropover+' '+disabled)) { if (!mobile && enter && !link.data('dragRegisted') && !link.hasClass(root+' '+draggable+' elfinder-na elfinder-wo')) { link.data('dragRegisted', true); if (fm.isCommandEnabled('copy', fm.navId2Hash(link.attr('id')))) { link.draggable(fm.draggable); } } link.toggleClass(hover, enter); } }) // native drag enter .on('dragenter', selNavdir, function(e) { if (e.originalEvent.dataTransfer) { var dst = jQuery(this); dst.addClass(hover); if (dst.is('.'+collapsed+':not(.'+expanded+')')) { dst.data('expandTimer', setTimeout(function() { dst.is('.'+collapsed+'.'+hover) && dst.children('.'+arrow).trigger('click'); }, 500)); } } }) // native drag leave .on('dragleave', selNavdir, function(e) { if (e.originalEvent.dataTransfer) { var dst = jQuery(this); dst.data('expandTimer') && clearTimeout(dst.data('expandTimer')); dst.removeClass(hover); } }) // open dir or open subfolders in tree .on('click', selNavdir, function(e) { var link = jQuery(this), hash = fm.navId2Hash(link.attr('id')), file = fm.file(hash); if (link.data('longtap')) { link.removeData('longtap'); e.stopPropagation(); return; } if (!link.hasClass(active)) { tree.find(selNavdir+'.'+active).removeClass(active); link.addClass(active); } if (hash != fm.cwd().hash && !link.hasClass(disabled)) { fm.exec('open', hash).done(function() { fm.one('opendone', function() { fm.select({selected: [hash], origin: 'navbar'}); }); }); } else { if (link.hasClass(collapsed)) { link.children('.'+arrow).trigger('click'); } fm.select({selected: [hash], origin: 'navbar'}); } }) // for touch device .on('touchstart', selNavdir, function(e) { if (e.originalEvent.touches.length > 1) { return; } var evt = e.originalEvent, p; if (e.target.nodeName === 'INPUT') { e.stopPropagation(); return; } p = jQuery(this).addClass(hover) .removeData('longtap') .data('tmlongtap', setTimeout(function(e){ // long tap p.data('longtap', true); fm.trigger('contextmenu', { 'type' : 'navbar', 'targets' : [fm.navId2Hash(p.attr('id'))], 'x' : evt.touches[0].pageX, 'y' : evt.touches[0].pageY }); }, 500)); }) .on('touchmove touchend', selNavdir, function(e) { if (e.target.nodeName === 'INPUT') { e.stopPropagation(); return; } clearTimeout(jQuery(this).data('tmlongtap')); if (e.type == 'touchmove') { jQuery(this).removeClass(hover); } }) // toggle subfolders in tree .on('click', selNavdir+'.'+collapsed+' .'+arrow, function(e) { var arrow = jQuery(this), link = arrow.parent(selNavdir), stree = link.next('.'+subtree), dfrd = jQuery.Deferred(), slideTH = 30, cnt; e.stopPropagation(); if (link.hasClass(loaded)) { link.toggleClass(expanded); fm.lazy(function() { cnt = link.hasClass(expanded)? stree.children().length + stree.find('div.elfinder-navbar-subtree[style*=block]').children().length : stree.find('div:visible').length; if (cnt > slideTH) { stree.toggle(); fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1); checkSubdirs(); } else { stree.stop(true, true)[link.hasClass(expanded)? 'slideDown' : 'slideUp'](opts.durations.slideUpDown, function(){ fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1); checkSubdirs(); }); } }).always(function() { dfrd.resolve(); }); } else { spinner.insertBefore(arrow); link.removeClass(collapsed); fm.request({cmd : 'tree', target : fm.navId2Hash(link.attr('id'))}) .done(function(data) { updateTree(Object.assign([], filter(data.tree))); if (stree.children().length) { link.addClass(collapsed+' '+expanded); if (stree.children().length > slideTH) { stree.show(); fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1); checkSubdirs(); } else { stree.stop(true, true).slideDown(opts.durations.slideUpDown, function(){ fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1); checkSubdirs(); }); } } }) .always(function(data) { spinner.remove(); link.addClass(loaded); fm.one('treedone', function() { dfrd.resolve(); }); }); } arrow.data('dfrd', dfrd); }) .on('contextmenu', selNavdir, function(e) { var self = jQuery(this); // now dirname editing if (self.find('input:text').length) { e.stopPropagation(); return; } e.preventDefault(); fm.trigger('contextmenu', { 'type' : 'navbar', 'targets' : [fm.navId2Hash(jQuery(this).attr('id'))], 'x' : e.pageX, 'y' : e.pageY }); self.addClass('ui-state-hover'); fm.getUI('contextmenu').children().on('mouseenter', function() { self.addClass('ui-state-hover'); }); fm.bind('closecontextmenu', function() { self.removeClass('ui-state-hover'); }); }) .on('scrolltoview', selNavdir, function(e, data) { var self = jQuery(this); autoScroll(self.attr('id')).done(function() { if (!data || data.blink === 'undefined' || data.blink) { fm.resources.blink(self, 'lookme'); } }); }) // prepend fake dir .on('create.'+fm.namespace, function(e, item) { var pdir = findSubtree(item.phash), lock = item.move || false, dir = jQuery(itemhtml(item)).addClass('elfinder-navbar-wrapper-tmp'), selected = fm.selected(); lock && selected.length && fm.trigger('lockfiles', {files: selected}); pdir.prepend(dir); }), scrolling = false, navbarScrTm, // move tree into navbar navbar = fm.getUI('navbar').append(tree).show().on('scroll', function() { scrolling = true; navbarScrTm && cancelAnimationFrame(navbarScrTm); navbarScrTm = requestAnimationFrame(function() { scrolling = false; checkSubdirs(); }); }), prevSortTreeview = fm.sortAlsoTreeview; fm.open(function(e) { var data = e.data, dirs = filter(data.files), contextmenu = fm.getUI('contextmenu'); data.init && tree.empty(); if (fm.UA.iOS) { navbar.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch'); } if (dirs.length) { fm.lazy(function() { if (!contextmenu.data('cmdMaps')) { contextmenu.data('cmdMaps', {}); } updateTree(dirs); updateArrows(dirs, loaded); sync(dirs); }); } else { sync(); } }) // add new dirs .add(function(e) { var dirs = filter(e.data.added); if (dirs.length) { updateTree(dirs); updateArrows(dirs, collapsed); } }) // update changed dirs .change(function(e) { // do ot perfome while syncing if (syncing) { return; } var dirs = filter(e.data.changed, true), length = dirs.length, l = length, tgts = jQuery(), changed = {}, dir, phash, node, tmp, realParent, reqParent, realSibling, reqSibling, isExpanded, isLoaded, parent, subdirs; jQuery.each(hasMoreDirs, function(h, node) { node.trigger('update.'+fm.namespace, { change: 'prepare' }); }); while (l--) { dir = dirs[l]; phash = dir.phash; if ((node = fm.navHash2Elm(dir.hash)).length) { parent = node.parent(); if (phash) { realParent = node.closest('.'+subtree); reqParent = findSubtree(phash); realSibling = node.parent().next(); reqSibling = findSibling(reqParent, dir); if (!reqParent.length) { continue; } if (reqParent[0] !== realParent[0] || realSibling.get(0) !== reqSibling.get(0)) { reqSibling.length ? reqSibling.before(parent) : reqParent.append(parent); } } isExpanded = node.hasClass(expanded); isLoaded = node.hasClass(loaded); tmp = jQuery(itemhtml(dir)); node.replaceWith(tmp.children(selNavdir)); ! mobile && updateDroppable(null, parent); if (dir.dirs && (isExpanded || isLoaded) && (node = fm.navHash2Elm(dir.hash)) && node.next('.'+subtree).children().length) { isExpanded && node.addClass(expanded); isLoaded && node.addClass(loaded); } subdirs |= dir.dirs == -1; } } // to check subdirs if (subdirs) { checkSubdirs(); } jQuery.each(hasMoreDirs, function(h, node) { node.trigger('update.'+fm.namespace, { change: 'done' }); }); length && sync(void(0), false); }) // remove dirs .remove(function(e) { var dirs = e.data.removed, l = dirs.length, node, stree, removed; jQuery.each(hasMoreDirs, function(h, node) { node.trigger('update.'+fm.namespace, { removed : dirs }); node.trigger('update.'+fm.namespace, { change: 'prepare' }); }); while (l--) { if ((node = fm.navHash2Elm(dirs[l])).length) { removed = true; stree = node.closest('.'+subtree); node.parent().detach(); if (!stree.children().length) { stree.hide().prev(selNavdir).removeClass(collapsed+' '+expanded+' '+loaded); } } } removed && fm.getUI('navbar').children('.ui-resizable-handle').trigger('resize'); jQuery.each(hasMoreDirs, function(h, node) { node.trigger('update.'+fm.namespace, { change: 'done' }); }); }) // lock/unlock dirs while moving .bind('lockfiles unlockfiles', function(e) { var lock = e.type == 'lockfiles', helperLocked = e.data.helper? e.data.helper.data('locked') : false, act = (lock && !helperLocked) ? 'disable' : 'enable', dirs = jQuery.grep(e.data.files||[], function(h) { var dir = fm.file(h); return dir && dir.mime == 'directory' ? true : false; }); jQuery.each(dirs, function(i, hash) { var dir = fm.navHash2Elm(hash); if (dir.length && !helperLocked) { dir.hasClass(draggable) && dir.draggable(act); dir.hasClass(droppable) && dir.droppable(act); dir[lock ? 'addClass' : 'removeClass'](disabled); } }); }) .bind('sortchange', function() { if (fm.sortAlsoTreeview || prevSortTreeview !== fm.sortAlsoTreeview) { var dirs, ends = [], endsMap = {}, endsVid = {}, topVid = '', single = false, current; fm.lazy(function() { dirs = filter(fm.files()); prevSortTreeview = fm.sortAlsoTreeview; tree.empty(); // append volume roots at first updateTree(jQuery.map(fm.roots, function(h) { var dir = fm.file(h); return dir && !dir.phash? dir : null; })); if (!Object.keys(hasMoreDirs).length) { updateTree(dirs); current = selectPages(); updateArrows(dirs, loaded); } else { ends = getEnds(); if (ends.length > 1) { jQuery.each(ends, function(i, end) { var vid = fm.file(fm.root(end)).volumeid; if (i === 0) { topVid = vid; } endsVid[vid] = end; endsMap[end] = []; }); jQuery.each(dirs, function(i, d) { if (!d.volumeid) { single = true; return false; } endsMap[endsVid[d.volumeid] || endsVid[topVid]].push(d); }); } else { single = true; } if (single) { jQuery.each(ends, function(i, endHash) { updateTree(dirs); current = selectPages(fm.file(endHash)); updateArrows(dirs, loaded); }); } else { jQuery.each(endsMap, function(endHash, dirs) { updateTree(dirs); current = selectPages(fm.file(endHash)); updateArrows(dirs, loaded); }); } } sync(); }, 100); } }); }); return this; }; /* * File: /js/ui/uploadButton.js */ /** * @class elFinder toolbar's button tor upload file * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfinderuploadbutton = function(cmd) { return this.each(function() { var fm = cmd.fm, button = jQuery(this).elfinderbutton(cmd) .off('click'), form = jQuery('<form/>').appendTo(button), input = jQuery('<input type="file" multiple="true" title="'+cmd.fm.i18n('selectForUpload')+'"/>') .on('change', function() { var _input = jQuery(this); if (_input.val()) { fm.exec('upload', {input : _input.remove()[0]}, void(0), fm.cwd().hash); input.clone(true).appendTo(form); } }) .on('dragover', function(e) { e.originalEvent.dataTransfer.dropEffect = 'copy'; }), tm; form.append(input.clone(true)); cmd.change(function() { tm && cancelAnimationFrame(tm); tm = requestAnimationFrame(function() { var toShow = cmd.disabled(); if (form.is('visible')) { !toShow && form.hide(); } else { toShow && form.show(); } }); }) .change(); }); }; /* * File: /js/ui/viewbutton.js */ /** * @class elFinder toolbar button to switch current directory view. * * @author Dmitry (dio) Levashov **/ jQuery.fn.elfinderviewbutton = function(cmd) { return this.each(function() { var button = jQuery(this).elfinderbutton(cmd), icon = button.children('.elfinder-button-icon'), text = button.children('.elfinder-button-text'), tm; cmd.change(function() { tm && cancelAnimationFrame(tm); tm = requestAnimationFrame(function() { var icons = cmd.value == 'icons'; icon.toggleClass('elfinder-button-icon-view-list', icons); cmd.className = icons? 'view-list' : ''; cmd.title = cmd.fm.i18n(icons ? 'viewlist' : 'viewicons'); button.attr('title', cmd.title); text.html(cmd.title); }); }); }); }; /* * File: /js/ui/workzone.js */ /** * @class elfinderworkzone - elFinder container for nav and current directory * @author Dmitry (dio) Levashov **/ jQuery.fn.elfinderworkzone = function(fm) { var cl = 'elfinder-workzone'; this.not('.'+cl).each(function() { var wz = jQuery(this).addClass(cl), prevH = Math.round(wz.height()), parent = wz.parent(), setDelta = function() { wdelta = wz.outerHeight(true) - wz.height(); }, fitsize = function(e) { var height = parent.height() - wdelta, style = parent.attr('style'), curH = Math.round(wz.height()); if (e) { e.preventDefault(); e.stopPropagation(); } parent.css('overflow', 'hidden') .children(':visible:not(.'+cl+')').each(function() { var ch = jQuery(this); if (ch.css('position') != 'absolute' && ch.css('position') != 'fixed') { height -= ch.outerHeight(true); } }); parent.attr('style', style || ''); height = Math.max(0, Math.round(height)); if (prevH !== height || curH !== height) { prevH = Math.round(wz.height()); wz.height(height); fm.trigger('wzresize'); } }, cssloaded = function() { wdelta = wz.outerHeight(true) - wz.height(); fitsize(); }, wdelta; setDelta(); parent.on('resize.' + fm.namespace, fitsize); fm.one('cssloaded', cssloaded) .bind('uiresize', fitsize) .bind('themechange', setDelta); }); return this; }; /* * File: /js/commands/archive.js */ /** * @class elFinder command "archive" * Archive selected files * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.archive = function() { var self = this, fm = self.fm, mimes = [], dfrd; this.variants = []; this.disableOnSearch = false; this.nextAction = {}; /** * Update mimes on open/reload * * @return void **/ fm.bind('open reload', function() { self.variants = []; jQuery.each((mimes = fm.option('archivers')['create'] || []), function(i, mime) { self.variants.push([mime, fm.mime2kind(mime)]); }); self.change(); }); this.getstate = function(select) { var sel = this.files(select), cnt = sel.length, chk = (cnt && ! fm.isRoot(sel[0]) && (fm.file(sel[0].phash) || {}).write && ! jQuery.grep(sel, function(f){ return f.read ? false : true; }).length), cwdId; if (chk && fm.searchStatus.state > 1) { cwdId = fm.cwd().volumeid; chk = (cnt === jQuery.grep(sel, function(f) { return f.read && f.hash.indexOf(cwdId) === 0 ? true : false; }).length); } return chk && !this._disabled && mimes.length && (cnt || (dfrd && dfrd.state() == 'pending')) ? 0 : -1; }; this.exec = function(hashes, type) { var files = this.files(hashes), cnt = files.length, mime = type || mimes[0], cwd = fm.file(files[0].phash) || null, error = ['errArchive', 'errPerm', 'errCreatingTempDir', 'errFtpDownloadFile', 'errFtpUploadFile', 'errFtpMkdir', 'errArchiveExec', 'errExtractExec', 'errRm'], i, open; dfrd = jQuery.Deferred().fail(function(error) { error && fm.error(error); }); if (! (cnt && mimes.length && jQuery.inArray(mime, mimes) !== -1)) { return dfrd.reject(); } if (!cwd.write) { return dfrd.reject(error); } for (i = 0; i < cnt; i++) { if (!files[i].read) { return dfrd.reject(error); } } self.mime = mime; self.prefix = ((cnt > 1)? 'Archive' : files[0].name) + (fm.option('archivers')['createext']? '.' + fm.option('archivers')['createext'][mime] : ''); self.data = {targets : self.hashes(hashes), type : mime}; if (fm.cwd().hash !== cwd.hash) { open = fm.exec('open', cwd.hash).done(function() { fm.one('cwdrender', function() { fm.selectfiles({files : hashes}); dfrd = jQuery.proxy(fm.res('mixin', 'make'), self)(); }); }); } else { fm.selectfiles({files : hashes}); dfrd = jQuery.proxy(fm.res('mixin', 'make'), self)(); } return dfrd; }; }; /* * File: /js/commands/back.js */ /** * @class elFinder command "back" * Open last visited folder * * @author Dmitry (dio) Levashov **/ (elFinder.prototype.commands.back = function() { this.alwaysEnabled = true; this.updateOnSelect = false; this.shortcuts = [{ pattern : 'ctrl+left backspace' }]; this.getstate = function() { return this.fm.history.canBack() ? 0 : -1; }; this.exec = function() { return this.fm.history.back(); }; }).prototype = { forceLoad : true }; // this is required command /* * File: /js/commands/chmod.js */ /** * @class elFinder command "chmod". * Chmod files. * * @type elFinder.command * @author Naoki Sawada */ elFinder.prototype.commands.chmod = function() { this.updateOnSelect = false; var fm = this.fm, level = { 0 : 'owner', 1 : 'group', 2 : 'other' }, msg = { read : fm.i18n('read'), write : fm.i18n('write'), execute : fm.i18n('execute'), perm : fm.i18n('perm'), kind : fm.i18n('kind'), files : fm.i18n('files') }, isPerm = function(perm){ return (!isNaN(parseInt(perm, 8) && parseInt(perm, 8) <= 511) || perm.match(/^([r-][w-][x-]){3}$/i)); }; this.tpl = { main : '<div class="ui-helper-clearfix elfinder-info-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}</div>' +'{dataTable}', itemTitle : '<strong>{name}</strong><span id="elfinder-info-kind">{kind}</span>', groupTitle : '<strong>{items}: {num}</strong>', dataTable : '<table id="{id}-table-perm"><tr><td>{0}</td><td>{1}</td><td>{2}</td></tr></table>' +'<div class="">'+msg.perm+': <input class="elfinder-tabstop elfinder-focus" id="{id}-perm" type="text" size="4" maxlength="3" value="{value}"></div>', fieldset : '<fieldset id="{id}-fieldset-{level}"><legend>{f_title}{name}</legend>' +'<input type="checkbox" value="4" class="elfinder-tabstop" id="{id}-read-{level}-perm"{checked-r}> <label for="{id}-read-{level}-perm">'+msg.read+'</label><br>' +'<input type="checkbox" value="6" class="elfinder-tabstop" id="{id}-write-{level}-perm"{checked-w}> <label for="{id}-write-{level}-perm">'+msg.write+'</label><br>' +'<input type="checkbox" value="5" class="elfinder-tabstop" id="{id}-execute-{level}-perm"{checked-x}> <label for="{id}-execute-{level}-perm">'+msg.execute+'</label><br>' }; this.shortcuts = [{ //pattern : 'ctrl+p' }]; this.getstate = function(sel) { var fm = this.fm; sel = sel || fm.selected(); if (sel.length == 0) { sel = [ fm.cwd().hash ]; } return this.checkstate(this.files(sel)) ? 0 : -1; }; this.checkstate = function(sel) { var cnt = sel.length; if (!cnt) return false; var chk = jQuery.grep(sel, function(f) { return (f.isowner && f.perm && isPerm(f.perm) && (cnt == 1 || f.mime != 'directory')) ? true : false; }).length; return (cnt == chk)? true : false; }; this.exec = function(select) { var hashes = this.hashes(select), files = this.files(hashes); if (! files.length) { hashes = [ this.fm.cwd().hash ]; files = this.files(hashes); } var fm = this.fm, dfrd = jQuery.Deferred().always(function() { fm.enable(); }), tpl = this.tpl, cnt = files.length, file = files[0], id = fm.namespace + '-perm-' + file.hash, view = tpl.main, checked = ' checked="checked"', buttons = function() { var buttons = {}; buttons[fm.i18n('btnApply')] = save; buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); }; return buttons; }, save = function() { var perm = jQuery.trim(jQuery('#'+id+'-perm').val()), reqData; if (!isPerm(perm)) return false; dialog.elfinderdialog('close'); reqData = { cmd : 'chmod', targets : hashes, mode : perm }; fm.request({ data : reqData, notify : {type : 'chmod', cnt : cnt} }) .fail(function(error) { dfrd.reject(error); }) .done(function(data) { if (data.changed && data.changed.length) { data.undo = { cmd : 'chmod', callback : function() { var reqs = []; jQuery.each(prevVals, function(perm, hashes) { reqs.push(fm.request({ data : {cmd : 'chmod', targets : hashes, mode : perm}, notify : {type : 'undo', cnt : hashes.length} })); }); return jQuery.when.apply(null, reqs); } }; data.redo = { cmd : 'chmod', callback : function() { return fm.request({ data : reqData, notify : {type : 'redo', cnt : hashes.length} }); } }; } dfrd.resolve(data); }); }, setperm = function() { var perm = ''; var _perm; for (var i = 0; i < 3; i++){ _perm = 0; if (jQuery("#"+id+"-read-"+level[i]+'-perm').is(':checked')) { _perm = (_perm | 4); } if (jQuery("#"+id+"-write-"+level[i]+'-perm').is(':checked')) { _perm = (_perm | 2); } if (jQuery("#"+id+"-execute-"+level[i]+'-perm').is(':checked')) { _perm = (_perm | 1); } perm += _perm.toString(8); } jQuery('#'+id+'-perm').val(perm); }, setcheck = function(perm) { var _perm; for (var i = 0; i < 3; i++){ _perm = parseInt(perm.slice(i, i+1), 8); jQuery("#"+id+"-read-"+level[i]+'-perm').prop("checked", false); jQuery("#"+id+"-write-"+level[i]+'-perm').prop("checked", false); jQuery("#"+id+"-execute-"+level[i]+'-perm').prop("checked", false); if ((_perm & 4) == 4) { jQuery("#"+id+"-read-"+level[i]+'-perm').prop("checked", true); } if ((_perm & 2) == 2) { jQuery("#"+id+"-write-"+level[i]+'-perm').prop("checked", true); } if ((_perm & 1) == 1) { jQuery("#"+id+"-execute-"+level[i]+'-perm').prop("checked", true); } } setperm(); }, makeperm = function(files) { var perm = '777', ret = '', chk, _chk, _perm; var len = files.length; for (var i2 = 0; i2 < len; i2++) { chk = getPerm(files[i2].perm); if (! prevVals[chk]) { prevVals[chk] = []; } prevVals[chk].push(files[i2].hash); ret = ''; for (var i = 0; i < 3; i++){ _chk = parseInt(chk.slice(i, i+1), 8); _perm = parseInt(perm.slice(i, i+1), 8); if ((_chk & 4) != 4 && (_perm & 4) == 4) { _perm -= 4; } if ((_chk & 2) != 2 && (_perm & 2) == 2) { _perm -= 2; } if ((_chk & 1) != 1 && (_perm & 1) == 1) { _perm -= 1; } ret += _perm.toString(8); } perm = ret; } return perm; }, makeName = function(name) { return name? ':'+name : ''; }, makeDataTable = function(perm, f) { var _perm, fieldset; var value = ''; var dataTable = tpl.dataTable; for (var i = 0; i < 3; i++){ _perm = parseInt(perm.slice(i, i+1), 8); value += _perm.toString(8); fieldset = tpl.fieldset.replace('{f_title}', fm.i18n(level[i])).replace('{name}', makeName(f[level[i]])).replace(/\{level\}/g, level[i]); dataTable = dataTable.replace('{'+i+'}', fieldset) .replace('{checked-r}', ((_perm & 4) == 4)? checked : '') .replace('{checked-w}', ((_perm & 2) == 2)? checked : '') .replace('{checked-x}', ((_perm & 1) == 1)? checked : ''); } dataTable = dataTable.replace('{value}', value).replace('{valueCaption}', msg['perm']); return dataTable; }, getPerm = function(perm){ if (isNaN(parseInt(perm, 8))) { var mode_array = perm.split(''); var a = []; for (var i = 0, l = mode_array.length; i < l; i++) { if (i === 0 || i === 3 || i === 6) { if (mode_array[i].match(/[r]/i)) { a.push(1); } else if (mode_array[i].match(/[-]/)) { a.push(0); } } else if ( i === 1 || i === 4 || i === 7) { if (mode_array[i].match(/[w]/i)) { a.push(1); } else if (mode_array[i].match(/[-]/)) { a.push(0); } } else { if (mode_array[i].match(/[x]/i)) { a.push(1); } else if (mode_array[i].match(/[-]/)) { a.push(0); } } } a.splice(3, 0, ","); a.splice(7, 0, ","); var b = a.join(""); var b_array = b.split(","); var c = []; for (var j = 0, m = b_array.length; j < m; j++) { var p = parseInt(b_array[j], 2).toString(8); c.push(p); } perm = c.join(''); } else { perm = parseInt(perm, 8).toString(8); } return perm; }, opts = { title : this.title, width : 'auto', buttons : buttons(), close : function() { jQuery(this).elfinderdialog('destroy'); } }, dialog = fm.getUI().find('#'+id), prevVals = {}, tmb = '', title, dataTable; if (dialog.length) { dialog.elfinderdialog('toTop'); return jQuery.Deferred().resolve(); } view = view.replace('{class}', cnt > 1 ? 'elfinder-cwd-icon-group' : fm.mime2class(file.mime)); if (cnt > 1) { title = tpl.groupTitle.replace('{items}', fm.i18n('items')).replace('{num}', cnt); } else { title = tpl.itemTitle.replace('{name}', file.name).replace('{kind}', fm.mime2kind(file)); tmb = fm.tmb(file); } dataTable = makeDataTable(makeperm(files), files.length == 1? files[0] : {}); view = view.replace('{title}', title).replace('{dataTable}', dataTable).replace(/{id}/g, id); dialog = this.fmDialog(view, opts); dialog.attr('id', id); // load thumbnail if (tmb) { jQuery('<img/>') .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); }) .attr('src', tmb.url); } jQuery('#' + id + '-table-perm :checkbox').on('click', function(){setperm('perm');}); jQuery('#' + id + '-perm').on('keydown', function(e) { var c = e.keyCode; if (c == jQuery.ui.keyCode.ENTER) { e.stopPropagation(); save(); return; } }).on('focus', function(e){ jQuery(this).trigger('select'); }).on('keyup', function(e) { if (jQuery(this).val().length == 3) { jQuery(this).trigger('select'); setcheck(jQuery(this).val()); } }); return dfrd; }; }; /* * File: /js/commands/colwidth.js */ /** * @class elFinder command "colwidth" * CWD list table columns width to auto * * @author Naoki Sawada **/ elFinder.prototype.commands.colwidth = function() { this.alwaysEnabled = true; this.updateOnSelect = false; this.getstate = function() { return this.fm.getUI('cwd').find('table').css('table-layout') === 'fixed' ? 0 : -1; }; this.exec = function() { this.fm.getUI('cwd').trigger('colwidth'); return jQuery.Deferred().resolve(); }; }; /* * File: /js/commands/copy.js */ /** * @class elFinder command "copy". * Put files in filemanager clipboard. * * @type elFinder.command * @author Dmitry (dio) Levashov */ elFinder.prototype.commands.copy = function() { this.shortcuts = [{ pattern : 'ctrl+c ctrl+insert' }]; this.getstate = function(select) { var sel = this.files(select), cnt = sel.length; return cnt && jQuery.grep(sel, function(f) { return f.read ? true : false; }).length == cnt ? 0 : -1; }; this.exec = function(hashes) { var fm = this.fm, dfrd = jQuery.Deferred() .fail(function(error) { fm.error(error); }); jQuery.each(this.files(hashes), function(i, file) { if (! file.read) { return !dfrd.reject(['errCopy', file.name, 'errPerm']); } }); return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes))); }; }; /* * File: /js/commands/cut.js */ /** * @class elFinder command "copy". * Put files in filemanager clipboard. * * @type elFinder.command * @author Dmitry (dio) Levashov */ elFinder.prototype.commands.cut = function() { var fm = this.fm; this.shortcuts = [{ pattern : 'ctrl+x shift+insert' }]; this.getstate = function(select) { var sel = this.files(select), cnt = sel.length; return cnt && jQuery.grep(sel, function(f) { return f.read && ! f.locked && ! fm.isRoot(f) ? true : false; }).length == cnt ? 0 : -1; }; this.exec = function(hashes) { var dfrd = jQuery.Deferred() .fail(function(error) { fm.error(error); }); jQuery.each(this.files(hashes), function(i, file) { if (!(file.read && ! file.locked && ! fm.isRoot(file)) ) { return !dfrd.reject(['errCopy', file.name, 'errPerm']); } if (file.locked) { return !dfrd.reject(['errLocked', file.name]); } }); return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes), true)); }; }; /* * File: /js/commands/download.js */ /** * @class elFinder command "download". * Download selected files. * Only for new api * * @author Dmitry (dio) Levashov, dio@std42.ru **/ elFinder.prototype.commands.zipdl = function() {}; elFinder.prototype.commands.download = function() { var self = this, fm = this.fm, czipdl = null, zipOn = false, mixed = false, dlntf = false, cpath = window.location.pathname || '/', filter = function(hashes, inExec) { var volumeid, mixedCmd; if (czipdl !== null) { if (fm.searchStatus.state > 1) { mixed = fm.searchStatus.mixed; } else if (fm.leafRoots[fm.cwd().hash]) { volumeid = fm.cwd().volumeid; jQuery.each(hashes, function(i, h) { if (h.indexOf(volumeid) !== 0) { mixed = true; return false; } }); } zipOn = (fm.isCommandEnabled('zipdl', hashes[0])); } if (mixed) { mixedCmd = czipdl? 'zipdl' : 'download'; hashes = jQuery.grep(hashes, function(h) { var f = fm.file(h), res = (! f || (! czipdl && f.mime === 'directory') || ! fm.isCommandEnabled(mixedCmd, h))? false : true; if (f && inExec && ! res) { fm.cwdHash2Elm(f.hash).trigger('unselect'); } return res; }); if (! hashes.length) { return []; } } else { if (!fm.isCommandEnabled('download', hashes[0])) { return []; } } return jQuery.grep(self.files(hashes), function(f) { var res = (! f.read || (! zipOn && f.mime == 'directory')) ? false : true; if (inExec && ! res) { fm.cwdHash2Elm(f.hash).trigger('unselect'); } return res; }); }; this.linkedCmds = ['zipdl']; this.shortcuts = [{ pattern : 'shift+enter' }]; this.getstate = function(select) { var sel = this.hashes(select), cnt = sel.length, maxReq = this.options.maxRequests || 10, mixed = false, croot = ''; if (cnt < 1) { return -1; } cnt = filter(sel).length; return (cnt && (zipOn || (cnt <= maxReq && ((!fm.UA.IE && !fm.UA.Mobile) || cnt == 1))) ? 0 : -1); }; fm.bind('contextmenu', function(e){ var fm = self.fm, helper = null, targets, file, link, getExtra = function(file) { var link = file.url || fm.url(file.hash); return { icon: 'link', node: jQuery('<a/>') .attr({href: link, target: '_blank', title: fm.i18n('link')}) .text(file.name) .on('mousedown click touchstart touchmove touchend contextmenu', function(e){ e.stopPropagation(); }) .on('dragstart', function(e) { var dt = e.dataTransfer || e.originalEvent.dataTransfer || null; helper = null; if (dt) { var icon = function(f) { var mime = f.mime, i, tmb = fm.tmb(f); i = '<div class="elfinder-cwd-icon '+fm.mime2class(mime)+' ui-corner-all"/>'; if (tmb) { i = jQuery(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML; } return i; }; dt.effectAllowed = 'copyLink'; if (dt.setDragImage) { helper = jQuery('<div class="elfinder-drag-helper html5-native">').append(icon(file)).appendTo(jQuery(document.body)); dt.setDragImage(helper.get(0), 50, 47); } if (!fm.UA.IE) { dt.setData('elfinderfrom', window.location.href + file.phash); dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), ''); } } }) .on('dragend', function(e) { helper && helper.remove(); }) }; }; self.extra = null; if (e.data) { targets = e.data.targets || []; if (targets.length === 1 && (file = fm.file(targets[0])) && file.mime !== 'directory') { if (file.url != '1') { self.extra = getExtra(file); } else { // Get URL ondemand var node; self.extra = { icon: 'link', node: jQuery('<a/>') .attr({href: '#', title: fm.i18n('getLink'), draggable: 'false'}) .text(file.name) .on('click touchstart', function(e){ if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) { return; } var parent = node.parent(); e.stopPropagation(); e.preventDefault(); parent.removeClass('ui-state-disabled').addClass('elfinder-button-icon-spinner'); fm.request({ data : {cmd : 'url', target : file.hash}, preventDefault : true }) .always(function(data) { parent.removeClass('elfinder-button-icon-spinner'); if (data.url) { var rfile = fm.file(file.hash); rfile.url = data.url; node.replaceWith(getExtra(file).node); } else { parent.addClass('ui-state-disabled'); } }); }) }; node = self.extra.node; node.ready(function(){ requestAnimationFrame(function(){ node.parent().addClass('ui-state-disabled').css('pointer-events', 'auto'); }); }); } } } }).one('open', function() { if (fm.api >= 2.1012) { czipdl = fm.getCommand('zipdl'); } dlntf = fm.api > 2.1038 && !fm.isCORS; }); this.exec = function(select) { var hashes = this.hashes(select), fm = this.fm, base = fm.options.url, files = filter(hashes, true), dfrd = jQuery.Deferred(), iframes = '', cdata = '', targets = {}, i, url, linkdl = false, getTask = function(hashes) { return function() { var dfd = jQuery.Deferred(), root = fm.file(fm.root(hashes[0])), single = (hashes.length === 1), volName = root? (root.i18 || root.name) : null, dir, dlName, phash; if (single) { if (dir = fm.file(hashes[0])) { dlName = (dir.i18 || dir.name); } } else { jQuery.each(hashes, function() { var d = fm.file(this); if (d && (!phash || phash === d.phash)) { phash = d.phash; } else { phash = null; return false; } }); if (phash && (dir = fm.file(phash))) { dlName = (dir.i18 || dir.name) + '-' + hashes.length; } } if (dlName) { volName = dlName; } volName && (volName = ' (' + volName + ')'); fm.request({ data : {cmd : 'zipdl', targets : hashes}, notify : {type : 'zipdl', cnt : 1, hideCnt : true, msg : fm.i18n('ntfzipdl') + volName}, cancel : true, eachCancel : true, preventDefault : true }).done(function(e) { var zipdl, dialog, btn = {}, dllink, form, iframe, m, uniq = 'dlw' + (+new Date()); if (e.error) { fm.error(e.error); dfd.resolve(); } else if (e.zipdl) { zipdl = e.zipdl; if (dlName) { m = fm.splitFileExtention(zipdl.name || ''); dlName += m[1]? ('.' + m[1]) : '.zip'; } else { dlName = zipdl.name; } if ((html5dl && (!fm.UA.Safari || fm.isSameOrigin(fm.options.url))) || linkdl) { url = fm.options.url + (fm.options.url.indexOf('?') === -1 ? '?' : '&') + 'cmd=zipdl&download=1'; jQuery.each([hashes[0], zipdl.file, dlName, zipdl.mime], function(key, val) { url += '&targets%5B%5D='+encodeURIComponent(val); }); jQuery.each(fm.customData, function(key, val) { url += '&'+encodeURIComponent(key)+'='+encodeURIComponent(val); }); url += '&'+encodeURIComponent(dlName); dllink = jQuery('<a/>') .attr('href', url) .attr('download', fm.escape(dlName)) .on('click', function() { dfd.resolve(); dialog && dialog.elfinderdialog('destroy'); }); if (linkdl) { dllink.attr('target', '_blank') .append('<span class="elfinder-button-icon elfinder-button-icon-download"></span>'+fm.escape(dlName)); btn[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('destroy'); }; dialog = self.fmDialog(dllink, { title: fm.i18n('link'), buttons: btn, width: '200px', destroyOnClose: true, close: function() { (dfd.state() !== 'resolved') && dfd.resolve(); } }); } else { click(dllink.hide().appendTo('body').get(0)); dllink.remove(); } } else { form = jQuery('<form action="'+fm.options.url+'" method="post" target="'+uniq+'" style="display:none"/>') .append('<input type="hidden" name="cmd" value="zipdl"/>') .append('<input type="hidden" name="download" value="1"/>'); jQuery.each([hashes[0], zipdl.file, dlName, zipdl.mime], function(key, val) { form.append('<input type="hidden" name="targets[]" value="'+fm.escape(val)+'"/>'); }); jQuery.each(fm.customData, function(key, val) { form.append('<input type="hidden" name="'+key+'" value="'+fm.escape(val)+'"/>'); }); form.attr('target', uniq).appendTo('body'); iframe = jQuery('<iframe style="display:none" name="'+uniq+'">') .appendTo('body') .ready(function() { form.submit().remove(); dfd.resolve(); setTimeout(function() { iframe.remove(); }, 20000); // give 20 sec file to be saved }); } } }).fail(function(error) { error && fm.error(error); dfd.resolve(); }); return dfd.promise(); }; }, // use MouseEvent to click element for Safari etc click = function(a) { var clickEv; if (typeof MouseEvent === 'function') { clickEv = new MouseEvent('click'); } else { clickEv = document.createEvent('MouseEvents'); clickEv.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); } a.dispatchEvent(clickEv); }, checkCookie = function(id) { var name = 'elfdl' + id, parts; parts = document.cookie.split(name + "="); if (parts.length === 2) { ntftm && clearTimeout(ntftm); document.cookie = name + '=; path=' + cpath + '; max-age=0'; closeNotify(); } else { setTimeout(function() { checkCookie(id); }, 200); } }, closeNotify = function() { if (fm.ui.notify.children('.elfinder-notify-download').length) { fm.notify({ type : 'download', cnt : -1 }); } }, reqids = [], link, html5dl, fileCnt, clickEv, cid, ntftm, reqid; if (!files.length) { return dfrd.reject(); } fileCnt = jQuery.grep(files, function(f) { return f.mime === 'directory'? false : true; }).length; link = jQuery('<a>').hide().appendTo('body'); html5dl = (typeof link.get(0).download === 'string'); if (zipOn && (fileCnt !== files.length || fileCnt >= (this.options.minFilesZipdl || 1))) { link.remove(); linkdl = (!html5dl && fm.UA.Mobile); if (mixed) { targets = {}; jQuery.each(files, function(i, f) { var p = f.hash.split('_', 2); if (! targets[p[0]]) { targets[p[0]] = [ f.hash ]; } else { targets[p[0]].push(f.hash); } }); if (!linkdl && fm.UA.Mobile && Object.keys(targets).length > 1) { linkdl = true; } } else { targets = [ jQuery.map(files, function(f) { return f.hash; }) ]; } dfrd = fm.sequence(jQuery.map(targets, function(t) { return getTask(t); })).always( function() { fm.trigger('download', {files : files}); } ); return dfrd; } else { reqids = []; for (i = 0; i < files.length; i++) { url = fm.openUrl(files[i].hash, true); if (dlntf && url.substr(0, fm.options.url.length) === fm.options.url) { reqid = fm.getRequestId(); reqids.push(reqid); url += '&cpath=' + cpath + '&reqid=' + reqid; ntftm = setTimeout(function() { fm.notify({ type : 'download', cnt : 1, cancel : (fm.UA.IE || fm.UA.Edge)? void(0) : function() { if (reqids.length) { jQuery.each(reqids, function() { fm.request({ data: { cmd: 'abort', id: this }, preventDefault: true }); }); } reqids = []; } }); }, fm.notifyDelay); checkCookie(reqid); } if (html5dl && (!fm.UA.Safari || fm.isSameOrigin(url))) { click(link.attr('href', url) .attr('download', fm.escape(files[i].name)) .get(0) ); } else { if (fm.UA.Mobile) { setTimeout(function(){ if (! window.open(url)) { fm.error('errPopup'); ntftm && cleaerTimeout(ntftm); closeNotify(); } }, 100); } else { iframes += '<iframe class="downloader" id="downloader-' + files[i].hash+'" style="display:none" src="'+url+'"/>'; } } } link.remove(); jQuery(iframes) .appendTo('body') .ready(function() { setTimeout(function() { jQuery(iframes).each(function() { jQuery('#' + jQuery(this).attr('id')).remove(); }); }, 20000 + (10000 * i)); // give 20 sec + 10 sec for each file to be saved }); fm.trigger('download', {files : files}); return dfrd.resolve(); } }; }; /* * File: /js/commands/duplicate.js */ /** * @class elFinder command "duplicate" * Create file/folder copy with suffix "copy Number" * * @type elFinder.command * @author Dmitry (dio) Levashov */ elFinder.prototype.commands.duplicate = function() { var fm = this.fm; this.getstate = function(select) { var sel = this.files(select), cnt = sel.length; return cnt && fm.cwd().write && jQuery.grep(sel, function(f) { return f.read && f.phash === fm.cwd().hash && ! fm.isRoot(f)? true : false; }).length == cnt ? 0 : -1; }; this.exec = function(hashes) { var fm = this.fm, files = this.files(hashes), cnt = files.length, dfrd = jQuery.Deferred() .fail(function(error) { error && fm.error(error); }), args = []; if (! cnt) { return dfrd.reject(); } jQuery.each(files, function(i, file) { if (!file.read || !fm.file(file.phash).write) { return !dfrd.reject(['errCopy', file.name, 'errPerm']); } }); if (dfrd.state() == 'rejected') { return dfrd; } return fm.request({ data : {cmd : 'duplicate', targets : this.hashes(hashes)}, notify : {type : 'copy', cnt : cnt}, navigate : { toast : { inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdduplicate')])} } } }); }; }; /* * File: /js/commands/edit.js */ /** * @class elFinder command "edit". * Edit text file in dialog window * * @author Dmitry (dio) Levashov, dio@std42.ru **/ elFinder.prototype.commands.edit = function() { var self = this, fm = this.fm, clsEditing = fm.res('class', 'editing'), mimesSingle = [], mimes = [], allowAll = false, rtrim = function(str){ return str.replace(/\s+$/, ''); }, getEncSelect = function(heads) { var sel = jQuery('<select class="ui-corner-all"/>'), hval; if (heads) { jQuery.each(heads, function(i, head) { hval = fm.escape(head.value); sel.append('<option value="'+hval+'">'+(head.caption? fm.escape(head.caption) : hval)+'</option>'); }); } jQuery.each(self.options.encodings, function(i, v) { sel.append('<option value="'+v+'">'+v+'</option>'); }); return sel; }, getDlgWidth = function() { var m, width; if (typeof self.options.dialogWidth === 'string' && (m = self.options.dialogWidth.match(/(\d+)%/))) { width = parseInt(fm.getUI().width() * (m[1] / 100)); } else { width = parseInt(self.options.dialogWidth || 650); } return Math.min(width, jQuery(window).width()); }, /** * Return files acceptable to edit * * @param Array files hashes * @return Array **/ filter = function(files) { var cnt = files.length, mime, ext, skip; if (cnt > 1) { mime = files[0].mime; ext = files[0].name.replace(/^.*(\.[^.]+)$/, '$1'); } return jQuery.grep(files, function(file) { var res; if (skip || file.mime === 'directory') { return false; } res = file.read && (allowAll || fm.mimeIsText(file.mime) || jQuery.inArray(file.mime, cnt === 1? mimesSingle : mimes) !== -1) && (!self.onlyMimes.length || jQuery.inArray(file.mime, self.onlyMimes) !== -1) && (cnt === 1 || (file.mime === mime && file.name.substr(ext.length * -1) === ext)) && (fm.uploadMimeCheck(file.mime, file.phash)? true : false) && setEditors(file, cnt) && Object.keys(editors).length; if (!res) { skip = true; } return res; }); }, fileSync = function(hash) { var old = fm.file(hash), f; fm.request({ cmd: 'info', targets: [hash], preventDefault: true }).done(function(data) { var changed; if (data && data.files && data.files.length) { f = data.files[0]; if (old.ts != f.ts || old.size != f.size) { changed = { changed: [ f ] }; fm.updateCache(changed); fm.change(changed); } } }); }, /** * Open dialog with textarea to edit file * * @param String id dialog id * @param Object file file object * @param String content file content * @return jQuery.Deferred **/ dialog = function(id, file, content, encoding, editor) { var dfrd = jQuery.Deferred(), _loaded = false, loaded = function() { if (!_loaded) { fm.toast({ mode: 'warning', msg: fm.i18n('nowLoading') }); return false; } return true; }, save = function() { var encord = selEncoding? selEncoding.val():void(0), saveDfd = jQuery.Deferred().fail(function(err) { dialogNode.show().find('button.elfinder-btncnt-0,button.elfinder-btncnt-1').hide(); }), conf, res; if (!loaded()) { return saveDfd.resolve(); } if (ta.editor) { ta.editor.save(ta[0], ta.editor.instance); conf = ta.editor.confObj; if (conf.info && (conf.info.schemeContent || conf.info.arrayBufferContent)) { encord = 'scheme'; } } res = getContent(); setOld(res); if (res.promise) { res.done(function(data) { dfrd.notifyWith(ta, [encord, ta.data('hash'), old, saveDfd]); }).fail(function(err) { saveDfd.reject(err); }); } else { dfrd.notifyWith(ta, [encord, ta.data('hash'), old, saveDfd]); } return saveDfd; }, saveon = function() { if (!loaded()) { return; } save().fail(function(err) { err && fm.error(err); }); }, cancel = function() { ta.elfinderdialog('close'); }, savecl = function() { if (!loaded()) { return; } save().done(function() { _loaded = false; dialogNode.show(); cancel(); }).fail(function(err) { dialogNode.show(); err && fm.error(err); }); dialogNode.hide(); }, saveAs = function() { if (!loaded()) { return; } var prevOld = old, phash = fm.file(file.phash)? file.phash : fm.cwd().hash, fail = function(err) { dialogs.addClass(clsEditing).fadeIn(function() { err && fm.error(err); }); old = prevOld; fm.disable(); }, make = function() { self.mime = saveAsFile.mime || file.mime; self.prefix = (saveAsFile.name || file.name).replace(/ \d+(\.[^.]+)?$/, '$1'); self.requestCmd = 'mkfile'; self.nextAction = {}; self.data = {target : phash}; jQuery.proxy(fm.res('mixin', 'make'), self)() .done(function(data) { if (data.added && data.added.length) { ta.data('hash', data.added[0].hash); save().done(function() { _loaded = false; dialogNode.show(); cancel(); dialogs.fadeIn(); }).fail(fail); } else { fail(); } }) .progress(function(err) { if (err && err === 'errUploadMime') { ta.trigger('saveAsFail'); } }) .fail(fail) .always(function() { delete self.mime; delete self.prefix; delete self.nextAction; delete self.data; }); fm.trigger('unselectfiles', { files: [ file.hash ] }); }, reqOpen = null, dialogs = fm.getUI().children('.' + self.dialogClass + ':visible'); if (dialogNode.is(':hidden')) { dialogs = dialogs.add(dialogNode); } dialogs.removeClass(clsEditing).fadeOut(); fm.enable(); if (fm.searchStatus.state < 2 && phash !== fm.cwd().hash) { reqOpen = fm.exec('open', [phash], {thash: phash}); } jQuery.when([reqOpen]).done(function() { reqOpen? fm.one('cwdrender', make) : make(); }).fail(fail); }, changed = function() { var dfd = jQuery.Deferred(), res, tm; if (!_loaded) { return dfd.resolve(false); } ta.editor && ta.editor.save(ta[0], ta.editor.instance); res = getContent(); if (res && res.promise) { tm = setTimeout(function() { fm.notify({ type : 'chkcontent', cnt : 1, hideCnt: true }); }, 100); res.always(function() { tm && clearTimeout(tm); fm.notify({ type : 'chkcontent', cnt: -1 }); }).done(function(d) { dfd.resolve(old !== d); }).fail(function(err) { dfd.resolve(err || true); }); } else { dfd.resolve(old !== res); } return dfd; }, opts = { title : fm.escape(file.name), width : getDlgWidth(), buttons : {}, cssClass : clsEditing, maxWidth : 'window', maxHeight : 'window', allowMinimize : true, allowMaximize : true, openMaximized : editorMaximized() || (editor && editor.info && editor.info.openMaximized), btnHoverFocus : false, closeOnEscape : false, propagationEvents : ['mousemove', 'mouseup', 'click'], minimize : function() { var conf; if (ta.editor && dialogNode.closest('.ui-dialog').is(':hidden')) { conf = ta.editor.confObj; if (conf.info && conf.info.syncInterval) { fileSync(file.hash); } } }, close : function() { var close = function() { var conf; dfrd.resolve(); if (ta.editor) { ta.editor.close(ta[0], ta.editor.instance); conf = ta.editor.confObj; if (conf.info && conf.info.syncInterval) { fileSync(file.hash); } } ta.elfinderdialog('destroy'); }, onlySaveAs = (typeof saveAsFile.name !== 'undefined'), accept = onlySaveAs? { label : 'btnSaveAs', callback : function() { requestAnimationFrame(saveAs); } } : { label : 'btnSaveClose', callback : function() { save().done(function() { close(); }); } }; changed().done(function(change) { var msgs = ['confirmNotSave']; if (change) { if (typeof change === 'string') { msgs.unshift(change); } fm.confirm({ title : self.title, text : msgs, accept : accept, cancel : { label : 'btnClose', callback : close }, buttons : onlySaveAs? null : [{ label : 'btnSaveAs', callback : function() { requestAnimationFrame(saveAs); } }] }); } else { close(); } }); }, open : function() { var loadRes, conf, interval; ta.initEditArea.call(ta, id, file, content, fm); if (ta.editor) { loadRes = ta.editor.load(ta[0]) || null; if (loadRes && loadRes.done) { loadRes.always(function() { _loaded = true; }).done(function(instance) { ta.editor.instance = instance; ta.editor.focus(ta[0], ta.editor.instance); setOld(getContent()); requestAnimationFrame(function() { dialogNode.trigger('resize'); }); }).fail(function(error) { error && fm.error(error); ta.elfinderdialog('destroy'); return; }); } else { _loaded = true; if (loadRes && (typeof loadRes === 'string' || Array.isArray(loadRes))) { fm.error(loadRes); ta.elfinderdialog('destroy'); return; } ta.editor.instance = loadRes; ta.editor.focus(ta[0], ta.editor.instance); setOld(getContent()); requestAnimationFrame(function() { dialogNode.trigger('resize'); }); } conf = ta.editor.confObj; if (conf.info && conf.info.syncInterval) { if (interval = parseInt(conf.info.syncInterval)) { setTimeout(function() { autoSync(interval); }, interval); } } } else { _loaded = true; setOld(getContent()); } }, resize : function(e, data) { ta.editor && ta.editor.resize(ta[0], ta.editor.instance, e, data || {}); } }, getContent = function() { return ta.getContent.call(ta, ta[0]); }, setOld = function(res) { if (res && res.promise) { res.done(function(d) { old = d; }); } else { old = res; } }, autoSync = function(interval) { if (dialogNode.is(':visible')) { fileSync(file.hash); setTimeout(function() { autoSync(interval); }, interval); } }, saveAsFile = {}, ta, old, dialogNode, selEncoding, extEditor, maxW, syncInterval; if (editor) { if (editor.html) { ta = jQuery(editor.html); } extEditor = { init : editor.init || null, load : editor.load, getContent : editor.getContent || null, save : editor.save, beforeclose : typeof editor.beforeclose == 'function' ? editor.beforeclose : void 0, close : typeof editor.close == 'function' ? editor.close : function() {}, focus : typeof editor.focus == 'function' ? editor.focus : function() {}, resize : typeof editor.resize == 'function' ? editor.resize : function() {}, instance : null, doSave : saveon, doCancel : cancel, doClose : savecl, file : file, fm : fm, confObj : editor, trigger : function(evName, data) { fm.trigger('editEditor' + evName, Object.assign({}, editor.info || {}, data)); } }; } if (!ta) { if (!fm.mimeIsText(file.mime)) { return dfrd.reject('errEditorNotFound'); } (function() { var stateChange = function() { if (selEncoding) { changed().done(function(change) { if (change) { selEncoding.attr('title', fm.i18n('saveAsEncoding')).addClass('elfinder-edit-changed'); } else { selEncoding.attr('title', fm.i18n('openAsEncoding')).removeClass('elfinder-edit-changed'); } }); } }; ta = jQuery('<textarea class="elfinder-file-edit" rows="20" id="'+id+'-ta"></textarea>') .on('input propertychange', stateChange); if (!ta.editor || !ta.editor.info || ta.editor.info.useTextAreaEvent) { ta.on('keydown', function(e) { var code = e.keyCode, value, start; e.stopPropagation(); if (code == jQuery.ui.keyCode.TAB) { e.preventDefault(); // insert tab on tab press if (this.setSelectionRange) { value = this.value; start = this.selectionStart; this.value = value.substr(0, start) + "\t" + value.substr(this.selectionEnd); start += 1; this.setSelectionRange(start, start); } } if (e.ctrlKey || e.metaKey) { // close on ctrl+w/q if (code == 'Q'.charCodeAt(0) || code == 'W'.charCodeAt(0)) { e.preventDefault(); cancel(); } if (code == 'S'.charCodeAt(0)) { e.preventDefault(); saveon(); } } }) .on('mouseenter', function(){this.focus();}); } ta.initEditArea = function(id, file, content) { var heads = (encoding && encoding !== 'unknown')? [{value: encoding}] : [], wfake = jQuery('<select/>').hide(), setSelW = function(init) { init && wfake.appendTo(selEncoding.parent()); wfake.empty().append(jQuery('<option/>').text(selEncoding.val())); selEncoding.width(wfake.width()); }; // ta.hide() for performance tune. Need ta.show() in `load()` if use textarea node. ta.hide().val(content); if (content === '' || ! encoding || encoding !== 'UTF-8') { heads.push({value: 'UTF-8'}); } selEncoding = getEncSelect(heads).on('touchstart', function(e) { // for touch punch event handler e.stopPropagation(); }).on('change', function() { // reload to change encoding if not edited changed().done(function(change) { if (! change && getContent() !== '') { cancel(); edit(file, selEncoding.val(), editor).fail(function(err) { err && fm.error(err); }); } }); setSelW(); }).on('mouseover', stateChange); ta.parent().next().prepend(jQuery('<div class="ui-dialog-buttonset elfinder-edit-extras"/>').append(selEncoding)); setSelW(true); }; })(); } ta.data('hash', file.hash); if (extEditor) { ta.editor = extEditor; if (typeof extEditor.beforeclose === 'function') { opts.beforeclose = function() { return extEditor.beforeclose(ta[0], extEditor.instance); }; } if (typeof extEditor.init === 'function') { ta.initEditArea = extEditor.init; } if (typeof extEditor.getContent === 'function') { ta.getContent = extEditor.getContent; } } if (! ta.initEditArea) { ta.initEditArea = function() {}; } if (! ta.getContent) { ta.getContent = function() { return rtrim(ta.val()); }; } if (!editor || !editor.info || !editor.info.preventGet) { opts.buttons[fm.i18n('btnSave')] = saveon; opts.buttons[fm.i18n('btnSaveClose')] = savecl; opts.buttons[fm.i18n('btnSaveAs')] = saveAs; opts.buttons[fm.i18n('btnCancel')] = cancel; } if (editor && typeof editor.prepare === 'function') { editor.prepare(ta, opts, file); } dialogNode = self.fmDialog(ta, opts) .attr('id', id) .on('keydown keyup keypress', function(e) { e.stopPropagation(); }) .css({ overflow: 'hidden', minHeight: '7em' }) .addClass('elfinder-edit-editor') .closest('.ui-dialog') .on('changeType', function(e, data) { if (data.extention && data.mime) { var ext = data.extention, mime = data.mime, btnSet = jQuery(this).children('.ui-dialog-buttonpane').children('.ui-dialog-buttonset'); btnSet.children('.elfinder-btncnt-0,.elfinder-btncnt-1').hide(); saveAsFile.name = fm.splitFileExtention(file.name)[0] + '.' + data.extention; saveAsFile.mime = data.mime; if (!data.keepEditor) { btnSet.children('.elfinder-btncnt-2').trigger('click'); } } }); // care to viewport scale change with mobile devices maxW = (fm.options.dialogContained? elfNode : jQuery(window)).width(); (dialogNode.width() > maxW) && dialogNode.width(maxW); return dfrd.promise(); }, /** * Get file content and * open dialog with textarea to edit file content * * @param String file hash * @return jQuery.Deferred **/ edit = function(file, convert, editor) { var hash = file.hash, opts = fm.options, dfrd = jQuery.Deferred(), id = 'edit-'+fm.namespace+'-'+file.hash, d = fm.getUI().find('#'+id), conv = !convert? 0 : convert, req, error, res; if (d.length) { d.elfinderdialog('toTop'); return dfrd.resolve(); } if (!file.read || (!file.write && (!editor.info || !editor.info.converter))) { error = ['errOpen', file.name, 'errPerm']; return dfrd.reject(error); } if (editor && editor.info) { if (typeof editor.info.edit === 'function') { res = editor.info.edit.call(fm, file, editor); if (res.promise) { res.done(function() { dfrd.resolve(); }).fail(function(error) { dfrd.reject(error); }); } else { res? dfrd.resolve() : dfrd.reject(); } return dfrd; } if (editor.info.urlAsContent || editor.info.preventGet || editor.info.noContent) { req = jQuery.Deferred(); if (editor.info.urlAsContent) { fm.url(hash, { async: true, onetime: true, temporary: true }).done(function(url) { req.resolve({content: url}); }); } else { req.resolve({}); } } else { req = fm.request({ data : {cmd : 'get', target : hash, conv : conv, _t : file.ts}, options : {type: 'get', cache : true}, notify : {type : 'file', cnt : 1}, preventDefault : true }); } req.done(function(data) { var selEncoding, reg, m, res; if (data.doconv) { fm.confirm({ title : self.title, text : data.doconv === 'unknown'? 'confirmNonUTF8' : 'confirmConvUTF8', accept : { label : 'btnConv', callback : function() { dfrd = edit(file, selEncoding.val(), editor); } }, cancel : { label : 'btnCancel', callback : function() { dfrd.reject(); } }, optionsCallback : function(options) { options.create = function() { var base = jQuery('<div class="elfinder-dialog-confirm-encoding"/>'), head = {value: data.doconv}, detected; if (data.doconv === 'unknown') { head.caption = '-'; } selEncoding = getEncSelect([head]); jQuery(this).next().find('.ui-dialog-buttonset') .prepend(base.append(jQuery('<label>'+fm.i18n('encoding')+' </label>').append(selEncoding))); }; } }); } else { if ((!editor || !editor.info || !editor.info.preventGet) && fm.mimeIsText(file.mime)) { reg = new RegExp('^(data:'+file.mime.replace(/([.+])/g, '\\$1')+';base64,)', 'i'); if (!editor.info.dataScheme) { if (window.atob && (m = data.content.match(reg))) { data.content = atob(data.content.substr(m[1].length)); } } else { if (window.btoa && !data.content.match(reg)) { data.content = 'data:'+file.mime+';base64,'+btoa(data.content); } } } dialog(id, file, data.content, data.encoding, editor) .done(function(data) { dfrd.resolve(data); }) .progress(function(encoding, newHash, data, saveDfd) { var ta = this; if (newHash) { hash = newHash; } fm.request({ options : {type : 'post'}, data : { cmd : 'put', target : hash, encoding : encoding || data.encoding, content : data }, notify : {type : 'save', cnt : 1}, syncOnFail : true, preventFail : true, navigate : { target : 'changed', toast : { inbuffer : {msg: fm.i18n(['complete', fm.i18n('btnSave')])} } } }) .fail(function(error) { dfrd.reject(error); saveDfd.reject(); }) .done(function(data) { requestAnimationFrame(function(){ ta.trigger('focus'); ta.editor && ta.editor.focus(ta[0], ta.editor.instance); }); saveDfd.resolve(); }); }) .fail(function(error) { dfrd.reject(error); }); } }) .fail(function(error) { var err = fm.parseError(error); err = Array.isArray(err)? err[0] : err; (err !== 'errConvUTF8') && fm.sync(); dfrd.reject(error); }); } return dfrd.promise(); }, /** * Current editors of selected files * * @type Object */ editors = {}, /** * Fallback editor (Simple text editor) * * @type Object */ fallbackEditor = { // Simple Text (basic textarea editor) info : { id : 'textarea', name : 'TextArea', useTextAreaEvent : true }, load : function(textarea) { // trigger event 'editEditorPrepare' this.trigger('Prepare', { node: textarea, editorObj: void(0), instance: void(0), opts: {} }); textarea.setSelectionRange && textarea.setSelectionRange(0, 0); jQuery(textarea).trigger('focus').show(); }, save : function(){} }, /** * Set current editors * * @param Object file object * @param Number cnt count of selected items * @return Void */ setEditors = function(file, cnt) { var mimeMatch = function(fileMime, editorMimes){ if (!editorMimes) { return fm.mimeIsText(fileMime); } else { if (editorMimes[0] === '*' || jQuery.inArray(fileMime, editorMimes) !== -1) { return true; } var i, l; l = editorMimes.length; for (i = 0; i < l; i++) { if (fileMime.indexOf(editorMimes[i]) === 0) { return true; } } return false; } }, extMatch = function(fileName, editorExts){ if (!editorExts || !editorExts.length) { return true; } var ext = fileName.replace(/^.+\.([^.]+)|(.+)$/, '$1$2').toLowerCase(), i, l; l = editorExts.length; for (i = 0; i < l; i++) { if (ext === editorExts[i].toLowerCase()) { return true; } } return false; }, optEditors = self.options.editors || [], cwdWrite = fm.cwd().write; stored = fm.storage('storedEditors') || {}; editors = {}; if (!optEditors.length) { optEditors = [fallbackEditor]; } jQuery.each(optEditors, function(i, editor) { var name; if ((cnt === 1 || !editor.info.single) && ((!editor.info || !editor.info.converter)? file.write : cwdWrite) && (file.size > 0 || (!editor.info.converter && (editor.info.canMakeEmpty || (editor.info.canMakeEmpty !== false && fm.mimeIsText(file.mime))))) && (!editor.info.maxSize || file.size <= editor.info.maxSize) && mimeMatch(file.mime, editor.mimes || null) && extMatch(file.name, editor.exts || null) && typeof editor.load == 'function' && typeof editor.save == 'function') { name = editor.info.name? editor.info.name : ('Code Editor'); editor.id = editor.info.id? editor.info.id : ('editor' + i), editor.name = name; editor.i18n = fm.i18n(name); editors[editor.id] = editor; } }); return Object.keys(editors).length? true : false; }, store = function(mime, editor) { if (mime && editor) { if (!jQuery.isPlainObject(stored)) { stored = {}; } stored[mime] = editor.id; fm.storage('storedEditors', stored); fm.trigger('selectfiles', {files : fm.selected()}); } }, useStoredEditor = function() { var d = fm.storage('useStoredEditor'); return d? (d > 0) : self.options.useStoredEditor; }, editorMaximized = function() { var d = fm.storage('editorMaximized'); return d? (d > 0) : self.options.editorMaximized; }, getSubMenuRaw = function(files, callback) { var subMenuRaw = []; jQuery.each(editors, function(id, ed) { subMenuRaw.push( { label : fm.escape(ed.i18n), icon : ed.info && ed.info.icon? ed.info.icon : 'edit', options : { iconImg: ed.info && ed.info.iconImg? fm.baseUrl + ed.info.iconImg : void(0) }, callback : function() { store(files[0].mime, ed); callback && callback.call(ed); } } ); }); return subMenuRaw; }, getStoreId = function(name) { // for compatibility to previous version return name.toLowerCase().replace(/ +/g, ''); }, getStoredEditor = function(mime) { var name = stored[mime]; return name && Object.keys(editors).length? editors[getStoreId(name)] : void(0); }, infoRequest = function() { }, stored; this.shortcuts = [{ pattern : 'ctrl+e' }]; this.init = function() { var self = this, fm = this.fm, opts = this.options, cmdChecks = [], ccData, dfd; this.onlyMimes = this.options.mimes || []; fm.one('open', function() { // editors setup if (opts.editors && Array.isArray(opts.editors)) { fm.trigger('canMakeEmptyFile', {mimes: Object.keys(fm.storage('mkfileTextMimes') || {}).concat(opts.makeTextMimes || ['text/plain'])}); jQuery.each(opts.editors, function(i, editor) { if (editor.info && editor.info.cmdCheck) { cmdChecks.push(editor.info.cmdCheck); } }); if (cmdChecks.length) { if (fm.api >= 2.1030) { dfd = fm.request({ data : { cmd: 'editor', name: cmdChecks, method: 'enabled' }, preventDefault : true }).done(function(d) { ccData = d; }).fail(function() { ccData = {}; }); } else { ccData = {}; dfd = jQuery.Deferred().resolve(); } } else { dfd = jQuery.Deferred().resolve(); } dfd.always(function() { if (ccData) { opts.editors = jQuery.grep(opts.editors, function(e) { if (e.info && e.info.cmdCheck) { return ccData[e.info.cmdCheck]? true : false; } else { return true; } }); } jQuery.each(opts.editors, function(i, editor) { if (editor.setup && typeof editor.setup === 'function') { editor.setup.call(editor, opts, fm); } if (!editor.disabled) { if (editor.mimes && Array.isArray(editor.mimes)) { mimesSingle = mimesSingle.concat(editor.mimes); if (!editor.info || !editor.info.single) { mimes = mimes.concat(editor.mimes); } } if (!allowAll && editor.mimes && editor.mimes[0] === '*') { allowAll = true; } if (!editor.info) { editor.info = {}; } if (editor.info.integrate) { fm.trigger('helpIntegration', Object.assign({cmd: 'edit'}, editor.info.integrate)); } if (editor.info.canMakeEmpty) { fm.trigger('canMakeEmptyFile', {mimes: editor.mimes}); } } }); mimesSingle = (jQuery.uniqueSort || jQuery.unique)(mimesSingle); mimes = (jQuery.uniqueSort || jQuery.unique)(mimes); opts.editors = jQuery.grep(opts.editors, function(e) { return e.disabled? false : true; }); }); } }) .bind('select', function() { editors = null; }) .bind('contextmenucreate', function(e) { var file, editor, single = function(editor) { var title = self.title; fm.one('contextmenucreatedone', function() { self.title = title; }); self.title = fm.escape(editor.i18n); if (editor.info && editor.info.iconImg) { self.contextmenuOpts = { iconImg: fm.baseUrl + editor.info.iconImg }; } delete self.variants; }; self.contextmenuOpts = void(0); if (e.data.type === 'files' && self.enabled()) { file = fm.file(e.data.targets[0]); if (setEditors(file, e.data.targets.length)) { if (Object.keys(editors).length > 1) { if (!useStoredEditor() || !(editor = getStoredEditor(file.mime))) { delete self.extra; self.variants = []; jQuery.each(editors, function(id, editor) { self.variants.push([{ editor: editor }, editor.i18n, editor.info && editor.info.iconImg? fm.baseUrl + editor.info.iconImg : 'edit']); }); } else { single(editor); self.extra = { icon: 'menu', node: jQuery('<span/>') .attr({title: fm.i18n('select')}) .on('click touchstart', function(e){ if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) { return; } var node = jQuery(this); e.stopPropagation(); e.preventDefault(); fm.trigger('contextmenu', { raw: getSubMenuRaw(fm.selectedFiles(), function() { var hashes = fm.selected(); fm.exec('edit', hashes, {editor: this}); fm.trigger('selectfiles', {files : hashes}); }), x: node.offset().left, y: node.offset().top }); }) }; } } else { single(editors[Object.keys(editors)[0]]); delete self.extra; } } } }) .bind('canMakeEmptyFile', function(e) { if (e.data && e.data.resetTexts) { var defs = fm.arrayFlip(self.options.makeTextMimes || ['text/plain']), hides = fm.storage('mkfileHides') || {}; jQuery.each((fm.storage('mkfileTextMimes') || {}), function(mime, type) { if (!defs[mime]) { delete fm.mimesCanMakeEmpty[mime]; delete hides[mime]; } }); fm.storage('mkfileTextMimes', null); if (Object.keys(hides).length) { fm.storage('mkfileHides', hides); } else { fm.storage('mkfileHides', null); } } }); }; this.getstate = function(select) { var sel = this.files(select), cnt = sel.length; return cnt && filter(sel).length == cnt ? 0 : -1; }; this.exec = function(select, opts) { var fm = this.fm, files = filter(this.files(select)), hashes = jQuery.map(files, function(f) { return f.hash; }), list = [], editor = opts && opts.editor? opts.editor : null, node = jQuery(opts && opts._currentNode? opts._currentNode : fm.cwdHash2Elm(hashes[0])), getEditor = function() { var dfd = jQuery.Deferred(), storedId; if (!editor && Object.keys(editors).length > 1) { if (useStoredEditor() && (editor = getStoredEditor(files[0].mime))) { return dfd.resolve(editor); } fm.trigger('contextmenu', { raw: getSubMenuRaw(files, function() { dfd.resolve(this); }), x: node.offset().left, y: node.offset().top + 22, opened: function() { fm.one('closecontextmenu',function() { requestAnimationFrame(function() { if (dfd.state() === 'pending') { dfd.reject(); } }); }); } }); fm.trigger('selectfiles', {files : hashes}); return dfd; } else { Object.keys(editors).length > 1 && editor && store(files[0].mime, editor); return dfd.resolve(editor? editor : (Object.keys(editors).length? editors[Object.keys(editors)[0]] : null)); } }, dfrd = jQuery.Deferred(), file; if (editors === null) { setEditors(files[0], hashes.length); } if (!node.length) { node = fm.getUI('cwd'); } getEditor().done(function(editor) { while ((file = files.shift())) { list.push(edit(file, void(0), editor).fail(function(error) { error && fm.error(error); })); } if (list.length) { jQuery.when.apply(null, list).done(function() { dfrd.resolve(); }).fail(function() { dfrd.reject(); }); } else { dfrd.reject(); } }).fail(function() { dfrd.reject(); }); return dfrd; }; }; /* * File: /js/commands/empty.js */ /** * @class elFinder command "empty". * Empty the folder * * @type elFinder.command * @author Naoki Sawada */ elFinder.prototype.commands.empty = function() { var self, fm, selFiles = function(select) { var sel = self.files(select); if (!sel.length) { sel = [ fm.cwd() ]; } return sel; }; this.linkedCmds = ['rm']; this.init = function() { // lazy assign to make possible to become superclass self = this; fm = this.fm; }; this.getstate = function(select) { var sel = selFiles(select), cnt; cnt = sel.length; return jQuery.grep(sel, function(f) { return f.read && f.write && f.mime === 'directory' ? true : false; }).length == cnt ? 0 : -1; }; this.exec = function(hashes) { var dirs = selFiles(hashes), cnt = dirs.length, dfrd = jQuery.Deferred() .done(function() { var data = {changed: {}}; fm.toast({msg: fm.i18n(['"'+success.join('", ')+'"', 'complete', fm.i18n('cmdempty')])}); jQuery.each(dirs, function(i, dir) { data.changed[dir.hash] = dir; }); fm.change(data); }) .always(function() { var cwd = fm.cwd().hash; fm.trigger('selectfiles', {files: jQuery.map(dirs, function(d) { return cwd === d.phash? d.hash : null; })}); }), success = [], done = function(res) { if (typeof res === 'number') { success.push(dirs[res].name); delete dirs[res].dirs; } else { res && fm.error(res); } (--cnt < 1) && dfrd[success.length? 'resolve' : 'reject'](); }; jQuery.each(dirs, function(i, dir) { var tm; if (!(dir.write && dir.mime === 'directory')) { done(['errEmpty', dir.name, 'errPerm']); return null; } if (!fm.isCommandEnabled('rm', dir.hash)) { done(['errCmdNoSupport', '"rm"']); return null; } tm = setTimeout(function() { fm.notify({type : 'search', cnt : 1, hideCnt : cnt > 1? false : true}); }, fm.notifyDelay); fm.request({ data : {cmd : 'open', target : dir.hash}, preventDefault : true, asNotOpen : true }).done(function(data) { var targets = []; tm && clearTimeout(tm); if (fm.ui.notify.children('.elfinder-notify-search').length) { fm.notify({type : 'search', cnt : -1, hideCnt : cnt > 1? false : true}); } if (data && data.files && data.files.length) { if (data.files.length > fm.maxTargets) { done(['errEmpty', dir.name, 'errMaxTargets', fm.maxTargets]); } else { fm.updateCache(data); jQuery.each(data.files, function(i, f) { if (!f.write || f.locked) { done(['errEmpty', dir.name, 'errRm', f.name, 'errPerm']); targets = []; return false; } targets.push(f.hash); }); if (targets.length) { fm.exec('rm', targets, { _userAction : true, addTexts : [ fm.i18n('folderToEmpty', dir.name) ] }) .fail(function(error) { fm.trigger('unselectfiles', {files: fm.selected()}); done(fm.parseError(error) || ''); }) .done(function() { done(i); }); } } } else { fm.toast({ mode: 'warning', msg: fm.i18n('filderIsEmpty', dir.name)}); done(''); } }).fail(function(error) { done(fm.parseError(error) || ''); }); }); return dfrd; }; }; /* * File: /js/commands/extract.js */ /** * @class elFinder command "extract" * Extract files from archive * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.extract = function() { var self = this, fm = self.fm, mimes = [], filter = function(files) { return jQuery.grep(files, function(file) { return file.read && jQuery.inArray(file.mime, mimes) !== -1 ? true : false; }); }; this.variants = []; this.disableOnSearch = true; // Update mimes list on open/reload fm.bind('open reload', function() { mimes = fm.option('archivers')['extract'] || []; if (fm.api > 2) { self.variants = [[{makedir: true}, fm.i18n('cmdmkdir')], [{}, fm.i18n('btnCwd')]]; } else { self.variants = [[{}, fm.i18n('btnCwd')]]; } self.change(); }); this.getstate = function(select) { var sel = this.files(select), cnt = sel.length; return cnt && this.fm.cwd().write && filter(sel).length == cnt ? 0 : -1; }; this.exec = function(hashes, opts) { var files = this.files(hashes), dfrd = jQuery.Deferred(), cnt = files.length, makedir = opts && opts.makedir ? 1 : 0, i, error, decision; var overwriteAll = false; var omitAll = false; var mkdirAll = 0; var names = jQuery.map(fm.files(hashes), function(file) { return file.name; }); var map = {}; jQuery.grep(fm.files(hashes), function(file) { map[file.name] = file; return false; }); var decide = function(decision) { switch (decision) { case 'overwrite_all' : overwriteAll = true; break; case 'omit_all': omitAll = true; break; } }; var unpack = function(file) { if (!(file.read && fm.file(file.phash).write)) { error = ['errExtract', file.name, 'errPerm']; fm.error(error); dfrd.reject(error); } else if (jQuery.inArray(file.mime, mimes) === -1) { error = ['errExtract', file.name, 'errNoArchive']; fm.error(error); dfrd.reject(error); } else { fm.request({ data:{cmd:'extract', target:file.hash, makedir:makedir}, notify:{type:'extract', cnt:1}, syncOnFail:true, navigate:{ toast : makedir? { incwd : {msg: fm.i18n(['complete', fm.i18n('cmdextract')]), action: {cmd: 'open', msg: 'cmdopen'}}, inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdextract')]), action: {cmd: 'open', msg: 'cmdopen'}} } : { inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdextract')])} } } }) .fail(function (error) { if (dfrd.state() != 'rejected') { dfrd.reject(error); } }) .done(function () { }); } }; var confirm = function(files, index) { var file = files[index], name = fm.splitFileExtention(file.name)[0], existed = (jQuery.inArray(name, names) >= 0), next = function(){ if((index+1) < cnt) { confirm(files, index+1); } else { dfrd.resolve(); } }; if (!makedir && existed && map[name].mime != 'directory') { fm.confirm( { title : fm.i18n('ntfextract'), text : ['errExists', name, 'confirmRepl'], accept:{ label : 'btnYes', callback:function (all) { decision = all ? 'overwrite_all' : 'overwrite'; decide(decision); if(!overwriteAll && !omitAll) { if('overwrite' == decision) { unpack(file); } if((index+1) < cnt) { confirm(files, index+1); } else { dfrd.resolve(); } } else if(overwriteAll) { for (i = index; i < cnt; i++) { unpack(files[i]); } dfrd.resolve(); } } }, reject : { label : 'btnNo', callback:function (all) { decision = all ? 'omit_all' : 'omit'; decide(decision); if(!overwriteAll && !omitAll && (index+1) < cnt) { confirm(files, index+1); } else if (omitAll) { dfrd.resolve(); } } }, cancel : { label : 'btnCancel', callback:function () { dfrd.resolve(); } }, all : ((index+1) < cnt) } ); } else if (!makedir) { if (mkdirAll == 0) { fm.confirm({ title : fm.i18n('cmdextract'), text : [fm.i18n('cmdextract')+' "'+file.name+'"', 'confirmRepl'], accept:{ label : 'btnYes', callback:function (all) { all && (mkdirAll = 1); unpack(file); next(); } }, reject : { label : 'btnNo', callback:function (all) { all && (mkdirAll = -1); next(); } }, cancel : { label : 'btnCancel', callback:function () { dfrd.resolve(); } }, all : ((index+1) < cnt) }); } else { (mkdirAll > 0) && unpack(file); next(); } } else { unpack(file); next(); } }; if (!(this.enabled() && cnt && mimes.length)) { return dfrd.reject(); } if(cnt > 0) { confirm(files, 0); } return dfrd; }; }; /* * File: /js/commands/forward.js */ /** * @class elFinder command "forward" * Open next visited folder * * @author Dmitry (dio) Levashov **/ (elFinder.prototype.commands.forward = function() { this.alwaysEnabled = true; this.updateOnSelect = true; this.shortcuts = [{ pattern : 'ctrl+right' }]; this.getstate = function() { return this.fm.history.canForward() ? 0 : -1; }; this.exec = function() { return this.fm.history.forward(); }; }).prototype = { forceLoad : true }; // this is required command /* * File: /js/commands/fullscreen.js */ /** * @class elFinder command "fullscreen" * elFinder node to full scrren mode * * @author Naoki Sawada **/ elFinder.prototype.commands.fullscreen = function() { var self = this, fm = this.fm, update = function(e, data) { e.preventDefault(); e.stopPropagation(); if (data && data.fullscreen) { self.update(void(0), (data.fullscreen === 'on')); } }; this.alwaysEnabled = true; this.updateOnSelect = false; this.syncTitleOnChange = true; this.value = false; this.options = { ui : 'fullscreenbutton' }; this.getstate = function() { return 0; }; this.exec = function() { var node = fm.getUI().get(0), full = (node === fm.toggleFullscreen(node)); self.title = fm.i18n(full ? 'reinstate' : 'cmdfullscreen'); self.update(void(0), full); return jQuery.Deferred().resolve(); }; fm.bind('init', function() { fm.getUI().off('resize.' + fm.namespace, update).on('resize.' + fm.namespace, update); }); }; /* * File: /js/commands/getfile.js */ /** * @class elFinder command "getfile". * Return selected files info into outer callback. * For use elFinder with wysiwyg editors etc. * * @author Dmitry (dio) Levashov, dio@std42.ru **/ (elFinder.prototype.commands.getfile = function() { var self = this, fm = this.fm, filter = function(files) { var o = self.options; files = jQuery.grep(files, function(file) { return (file.mime != 'directory' || o.folders) && file.read ? true : false; }); return o.multiple || files.length == 1 ? files : []; }; this.alwaysEnabled = true; this.callback = fm.options.getFileCallback; this._disabled = typeof(this.callback) == 'function'; this.getstate = function(select) { var sel = this.files(select), cnt = sel.length; return this.callback && cnt && filter(sel).length == cnt ? 0 : -1; }; this.exec = function(hashes) { var fm = this.fm, opts = this.options, files = this.files(hashes), cnt = files.length, url = fm.option('url'), tmb = fm.option('tmbUrl'), dfrd = jQuery.Deferred() .done(function(data) { var res, done = function() { if (opts.oncomplete == 'close') { fm.hide(); } else if (opts.oncomplete == 'destroy') { fm.destroy(); } }, fail = function(error) { if (opts.onerror == 'close') { fm.hide(); } else if (opts.onerror == 'destroy') { fm.destroy(); } else { error && fm.error(error); } }; fm.trigger('getfile', {files : data}); try { res = self.callback(data, fm); } catch(e) { fail(['Error in `getFileCallback`.', e.message]); return; } if (typeof res === 'object' && typeof res.done === 'function') { res.done(done).fail(fail); } else { done(); } }), result = function(file) { return opts.onlyURL ? opts.multiple ? jQuery.map(files, function(f) { return f.url; }) : files[0].url : opts.multiple ? files : files[0]; }, req = [], i, file, dim; for (i = 0; i < cnt; i++) { file = files[i]; if (file.mime == 'directory' && !opts.folders) { return dfrd.reject(); } file.baseUrl = url; if (file.url == '1') { req.push(fm.request({ data : {cmd : 'url', target : file.hash}, notify : {type : 'url', cnt : 1, hideCnt : true}, preventDefault : true }) .done(function(data) { if (data.url) { var rfile = fm.file(this.hash); rfile.url = this.url = data.url; } }.bind(file))); } else { file.url = fm.url(file.hash); } if (! opts.onlyURL) { if (opts.getPath) { file.path = fm.path(file.hash); if (file.path === '' && file.phash) { // get parents (function() { var dfd = jQuery.Deferred(); req.push(dfd); fm.path(file.hash, false, {}) .done(function(path) { file.path = path; }) .fail(function() { file.path = ''; }) .always(function() { dfd.resolve(); }); })(); } } if (file.tmb && file.tmb != 1) { file.tmb = tmb + file.tmb; } if (!file.width && !file.height) { if (file.dim) { dim = file.dim.split('x'); file.width = dim[0]; file.height = dim[1]; } else if (opts.getImgSize && file.mime.indexOf('image') !== -1) { req.push(fm.request({ data : {cmd : 'dim', target : file.hash}, notify : {type : 'dim', cnt : 1, hideCnt : true}, preventDefault : true }) .done(function(data) { if (data.dim) { var dim = data.dim.split('x'); var rfile = fm.file(this.hash); rfile.width = this.width = dim[0]; rfile.height = this.height = dim[1]; } }.bind(file))); } } } } if (req.length) { jQuery.when.apply(null, req).always(function() { dfrd.resolve(result(files)); }); return dfrd; } return dfrd.resolve(result(files)); }; }).prototype = { forceLoad : true }; // this is required command /* * File: /js/commands/help.js */ /** * @class elFinder command "help" * "About" dialog * * @author Dmitry (dio) Levashov **/ (elFinder.prototype.commands.help = function() { var fm = this.fm, self = this, linktpl = '<div class="elfinder-help-link"> <a href="{url}">{link}</a></div>', linktpltgt = '<div class="elfinder-help-link"> <a href="{url}" target="_blank">{link}</a></div>', atpl = '<div class="elfinder-help-team"><div>{author}</div>{work}</div>', url = /\{url\}/, link = /\{link\}/, author = /\{author\}/, work = /\{work\}/, r = 'replace', prim = 'ui-priority-primary', sec = 'ui-priority-secondary', lic = 'elfinder-help-license', tab = '<li class="' + fm.res('class', 'tabstab') + ' elfinder-help-tab-{id}"><a href="#'+fm.namespace+'-help-{id}" class="ui-tabs-anchor">{title}</a></li>', html = ['<div class="ui-tabs ui-widget ui-widget-content ui-corner-all elfinder-help">', '<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-top">'], stpl = '<div class="elfinder-help-shortcut"><div class="elfinder-help-shortcut-pattern">{pattern}</div> {descrip}</div>', sep = '<div class="elfinder-help-separator"/>', selfUrl = jQuery('base').length? document.location.href.replace(/#.*$/, '') : '', clTabActive = fm.res('class', 'tabsactive'), getTheme = function() { var src; if (fm.theme && fm.theme.author) { src = atpl[r]('elfinder-help-team', 'elfinder-help-team elfinder-help-term-theme')[r](author, fm.i18n(fm.theme.author) + (fm.theme.email? ' <'+fm.theme.email+'>' : ''))[r](work, fm.i18n('theme') + ' ('+fm.i18n(fm.theme.name)+')'); } else { src = '<div class="elfinder-help-team elfinder-help-term-theme" style="display:none"></div>'; } return src; }, about = function() { html.push('<div id="'+fm.namespace+'-help-about" class="ui-tabs-panel ui-widget-content ui-corner-bottom"><div class="elfinder-help-logo"/>'); html.push('<h3>elFinder</h3>'); html.push('<div class="'+prim+'">'+fm.i18n('webfm')+'</div>'); html.push('<div class="'+sec+'">'+fm.i18n('ver')+': '+fm.version+'</div>'); html.push('<div class="'+sec+'">'+fm.i18n('protocolver')+': <span class="apiver"></span></div>'); html.push('<div class="'+sec+'">jQuery/jQuery UI: '+jQuery().jquery+'/'+jQuery.ui.version+'</div>'); html.push(sep); html.push(linktpltgt[r](url, 'https://studio-42.github.io/elFinder/')[r](link, fm.i18n('homepage'))); html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder/wiki')[r](link, fm.i18n('docs'))); html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder')[r](link, fm.i18n('github'))); //html.push(linktpltgt[r](url, 'http://twitter.com/elrte_elfinder')[r](link, fm.i18n('twitter'))); html.push(sep); html.push('<div class="'+prim+'">'+fm.i18n('team')+'</div>'); html.push(atpl[r](author, 'Dmitry "dio" Levashov <dio@std42.ru>')[r](work, fm.i18n('chiefdev'))); html.push(atpl[r](author, 'Naoki Sawada <hypweb+elfinder@gmail.com>')[r](work, fm.i18n('developer'))); html.push(atpl[r](author, 'Troex Nevelin <troex@fury.scancode.ru>')[r](work, fm.i18n('maintainer'))); html.push(atpl[r](author, 'Alexey Sukhotin <strogg@yandex.ru>')[r](work, fm.i18n('contributor'))); if (fm.i18[fm.lang].translator) { jQuery.each(fm.i18[fm.lang].translator.split(', '), function() { html.push(atpl[r](author, jQuery.trim(this))[r](work, fm.i18n('translator')+' ('+fm.i18[fm.lang].language+')')); }); } html.push(getTheme()); html.push(sep); html.push('<div class="'+lic+'">'+fm.i18n('icons')+': Pixelmixer, <a href="http://p.yusukekamiyamane.com" target="_blank">Fugue</a>, <a href="https://icons8.com" target="_blank">Icons8</a></div>'); html.push(sep); html.push('<div class="'+lic+'">Licence: 3-clauses BSD Licence</div>'); html.push('<div class="'+lic+'">Copyright © 2009-2019, Studio 42</div>'); html.push('<div class="'+lic+'">„ …'+fm.i18n('dontforget')+' ”</div>'); html.push('</div>'); }, shortcuts = function() { var sh = fm.shortcuts(); // shortcuts tab html.push('<div id="'+fm.namespace+'-help-shortcuts" class="ui-tabs-panel ui-widget-content ui-corner-bottom">'); if (sh.length) { html.push('<div class="ui-widget-content elfinder-help-shortcuts">'); jQuery.each(sh, function(i, s) { html.push(stpl.replace(/\{pattern\}/, s[0]).replace(/\{descrip\}/, s[1])); }); html.push('</div>'); } else { html.push('<div class="elfinder-help-disabled">'+fm.i18n('shortcutsof')+'</div>'); } html.push('</div>'); }, help = function() { // help tab html.push('<div id="'+fm.namespace+'-help-help" class="ui-tabs-panel ui-widget-content ui-corner-bottom">'); html.push('<a href="https://github.com/Studio-42/elFinder/wiki" target="_blank" class="elfinder-dont-panic"><span>DON\'T PANIC</span></a>'); html.push('</div>'); // end help }, useInteg = false, integrations = function() { useInteg = true; html.push('<div id="'+fm.namespace+'-help-integrations" class="ui-tabs-panel ui-widget-content ui-corner-bottom"/>'); }, useDebug = false, debug = function() { useDebug = true; // debug tab html.push('<div id="'+fm.namespace+'-help-debug" class="ui-tabs-panel ui-widget-content ui-corner-bottom">'); html.push('<div class="ui-widget-content elfinder-help-debug"><ul></ul></div>'); html.push('</div>'); // end debug }, debugRender = function() { var render = function(elm, obj) { jQuery.each(obj, function(k, v) { elm.append(jQuery('<dt/>').text(k)); if (typeof v === 'undefined') { elm.append(jQuery('<dd/>').append(jQuery('<span/>').text('undfined'))); } else if (typeof v === 'object' && !v) { elm.append(jQuery('<dd/>').append(jQuery('<span/>').text('null'))); } else if (typeof v === 'object' && (jQuery.isPlainObject(v) || v.length)) { elm.append( jQuery('<dd/>').append(render(jQuery('<dl/>'), v))); } else { elm.append(jQuery('<dd/>').append(jQuery('<span/>').text((v && typeof v === 'object')? '[]' : (v? v : '""')))); } }); return elm; }, cnt = debugUL.children('li').length, targetL, target, tabId, info, lastUL, lastDIV; if (self.debug.options || self.debug.debug) { if (cnt >= 5) { lastUL = debugUL.children('li:last'); lastDIV = debugDIV.children('div:last'); if (lastDIV.is(':hidden')) { lastUL.remove(); lastDIV.remove(); } else { lastUL.prev().remove(); lastDIV.prev().remove(); } } tabId = fm.namespace + '-help-debug-' + (+new Date()); targetL = jQuery('<li/>').html('<a href="'+selfUrl+'#'+tabId+'">'+self.debug.debug.cmd+'</a>').prependTo(debugUL); target = jQuery('<div id="'+tabId+'"/>').data('debug', self.debug); targetL.on('click.debugrender', function() { var debug = target.data('debug'); target.removeData('debug'); if (debug) { target.hide(); if (debug.debug) { info = jQuery('<fieldset>').append(jQuery('<legend/>').text('debug'), render(jQuery('<dl/>'), debug.debug)); target.append(info); } if (debug.options) { info = jQuery('<fieldset>').append(jQuery('<legend/>').text('options'), render(jQuery('<dl/>'), debug.options)); target.append(info); } target.show(); } targetL.off('click.debugrender'); }); debugUL.after(target); opened && debugDIV.tabs('refresh'); } }, content = '', opened, tabInteg, integDIV, tabDebug, debugDIV, debugUL; this.alwaysEnabled = true; this.updateOnSelect = false; this.state = -1; this.shortcuts = [{ pattern : 'f1', description : this.title }]; fm.bind('load', function() { var parts = self.options.view || ['about', 'shortcuts', 'help', 'integrations', 'debug'], i, helpSource, tabBase, tabNav, tabs, delta; // remove 'preference' tab, it moved to command 'preference' if ((i = jQuery.inArray('preference', parts)) !== -1) { parts.splice(i, 1); } // debug tab require jQueryUI Tabs Widget if (! jQuery.fn.tabs) { if ((i = jQuery.inArray(parts, 'debug')) !== -1) { parts.splice(i, 1); } } jQuery.each(parts, function(i, title) { html.push(tab[r](/\{id\}/g, title)[r](/\{title\}/, fm.i18n(title))); }); html.push('</ul>'); jQuery.inArray('about', parts) !== -1 && about(); jQuery.inArray('shortcuts', parts) !== -1 && shortcuts(); if (jQuery.inArray('help', parts) !== -1) { helpSource = fm.i18nBaseUrl + 'help/%s.html.js'; help(); } jQuery.inArray('integrations', parts) !== -1 && integrations(); jQuery.inArray('debug', parts) !== -1 && debug(); html.push('</div>'); content = jQuery(html.join('')); content.find('.ui-tabs-nav li') .on('mouseenter mouseleave', function(e) { jQuery(this).toggleClass('ui-state-hover', e.type === 'mouseenter'); }) .on('focus blur', 'a', function(e) { jQuery(e.delegateTarget).toggleClass('ui-state-focus', e.type === 'focusin'); }) .children() .on('click', function(e) { var link = jQuery(this); e.preventDefault(); e.stopPropagation(); link.parent().addClass(clTabActive).siblings().removeClass(clTabActive); content.children('.ui-tabs-panel').hide().filter(link.attr('href')).show(); }) .filter(':first').trigger('click'); if (useInteg) { tabInteg = content.find('.elfinder-help-tab-integrations').hide(); integDIV = content.find('#'+fm.namespace+'-help-integrations').hide().append(jQuery('<div class="elfinder-help-integrations-desc"/>').html(fm.i18n('integrationWith'))); fm.bind('helpIntegration', function(e) { var ul = integDIV.children('ul:first'), data, elm, cmdUL, cmdCls; if (e.data) { if (jQuery.isPlainObject(e.data)) { data = Object.assign({ link: '', title: '', banner: '' }, e.data); if (data.title || data.link) { if (!data.title) { data.title = data.link; } if (data.link) { elm = jQuery('<a/>').attr('href', data.link).attr('target', '_blank').text(data.title); } else { elm = jQuery('<span/>').text(data.title); } if (data.banner) { elm = jQuery('<span/>').append(jQuery('<img/>').attr(data.banner), elm); } } } else { elm = jQuery(e.data); elm.filter('a').each(function() { var tgt = jQuery(this); if (!tgt.attr('target')) { tgt.attr('target', '_blank');; } }); } if (elm) { tabInteg.show(); if (!ul.length) { ul = jQuery('<ul class="elfinder-help-integrations"/>').appendTo(integDIV); } if (data && data.cmd) { cmdCls = 'elfinder-help-integration-' + data.cmd; cmdUL = ul.find('ul.' + cmdCls); if (!cmdUL.length) { cmdUL = jQuery('<ul class="'+cmdCls+'"/>'); ul.append(jQuery('<li/>').append(jQuery('<span/>').html(fm.i18n('cmd'+data.cmd))).append(cmdUL)); } elm = cmdUL.append(jQuery('<li/>').append(elm)); } else { ul.append(jQuery('<li/>').append(elm)); } } } }).bind('themechange', function() { content.find('div.elfinder-help-term-theme').replaceWith(getTheme()); }); } // debug if (useDebug) { tabDebug = content.find('.elfinder-help-tab-debug').hide(); debugDIV = content.find('#'+fm.namespace+'-help-debug').children('div:first'); debugUL = debugDIV.children('ul:first').on('click', function(e) { e.preventDefault(); e.stopPropagation(); }); self.debug = {}; fm.bind('backenddebug', function(e) { // CAUTION: DO NOT TOUCH `e.data` if (useDebug && e.data && e.data.debug) { self.debug = { options : e.data.options, debug : Object.assign({ cmd : fm.currentReqCmd }, e.data.debug) }; if (self.dialog) { debugRender(); } } }); } content.find('#'+fm.namespace+'-help-about').find('.apiver').text(fm.api); self.dialog = self.fmDialog(content, { title : self.title, width : 530, maxWidth: 'window', maxHeight: 'window', autoOpen : false, destroyOnClose : false, close : function() { if (useDebug) { tabDebug.hide(); debugDIV.tabs('destroy'); } opened = false; } }) .on('click', function(e) { e.stopPropagation(); }) .css({ overflow: 'hidden' }); tabBase = self.dialog.children('.ui-tabs'); tabNav = tabBase.children('.ui-tabs-nav:first'); tabs = tabBase.children('.ui-tabs-panel'); delta = self.dialog.outerHeight(true) - self.dialog.height(); self.dialog.closest('.ui-dialog').on('resize', function() { tabs.height(self.dialog.height() - delta - tabNav.outerHeight(true) - 20); }); if (helpSource) { self.dialog.one('initContents', function() { jQuery.ajax({ url: self.options.helpSource? self.options.helpSource : helpSource.replace('%s', fm.lang), dataType: 'html' }).done(function(source) { jQuery('#'+fm.namespace+'-help-help').html(source); }).fail(function() { jQuery.ajax({ url: helpSource.replace('%s', 'en'), dataType: 'html' }).done(function(source) { jQuery('#'+fm.namespace+'-help-help').html(source); }); }); }); } self.state = 0; fm.trigger('helpBuilded', self.dialog); }).one('open', function() { var debug = false; fm.one('backenddebug', function() { debug =true; }).one('opendone', function() { requestAnimationFrame(function() { if (! debug && useDebug) { useDebug = false; tabDebug.hide(); debugDIV.hide(); debugUL.hide(); } }); }); }); this.getstate = function() { return 0; }; this.exec = function(sel, opts) { var tab = opts? opts.tab : void(0), debugShow = function() { if (useDebug) { debugDIV.tabs(); debugUL.find('a:first').trigger('click'); tabDebug.show(); opened = true; } }; debugShow(); this.dialog.trigger('initContents').elfinderdialog('open').find((tab? '.elfinder-help-tab-'+tab : '.ui-tabs-nav li') + ' a:first').trigger('click'); return jQuery.Deferred().resolve(); }; }).prototype = { forceLoad : true }; // this is required command /* * File: /js/commands/hidden.js */ /** * @class elFinder command "hidden" * Always hidden command for uiCmdMap * * @author Naoki Sawada **/ elFinder.prototype.commands.hidden = function() { this.hidden = true; this.updateOnSelect = false; this.getstate = function() { return -1; }; }; /* * File: /js/commands/hide.js */ /** * @class elFinder command "hide". * folders/files to hide as personal setting. * * @type elFinder.command * @author Naoki Sawada */ elFinder.prototype.commands.hide = function() { var self = this, nameCache = {}, hideData, hideCnt, cMenuType, sOrigin; this.syncTitleOnChange = true; this.shortcuts = [{ pattern : 'ctrl+shift+dot', description : this.fm.i18n('toggleHidden') }]; this.init = function() { var fm = this.fm; hideData = fm.storage('hide') || {items: {}}; hideCnt = Object.keys(hideData.items).length; this.title = fm.i18n(hideData.show? 'hideHidden' : 'showHidden'); self.update(void(0), self.title); }; this.fm.bind('select contextmenucreate closecontextmenu', function(e, fm) { var sel = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected(); if (e.type === 'select' && e.data) { sOrigin = e.data.origin; } else if (e.type === 'contextmenucreate') { cMenuType = e.data.type; } if (!sel.length || (((e.type !== 'contextmenucreate' && sOrigin !== 'navbar') || cMenuType === 'cwd') && sel[0] === fm.cwd().hash)) { self.title = fm.i18n(hideData.show? 'hideHidden' : 'showHidden'); } else { self.title = fm.i18n('cmdhide'); } if (e.type !== 'closecontextmenu') { self.update(cMenuType === 'cwd'? (hideCnt? 0 : -1) : void(0), self.title); } else { cMenuType = ''; requestAnimationFrame(function() { self.update(void(0), self.title); }); } }); this.getstate = function(sel) { return (cMenuType !== 'cwd' && (sel || this.fm.selected()).length) || hideCnt? 0 : -1; }; this.exec = function(hashes, opts) { var fm = this.fm, dfrd = jQuery.Deferred() .done(function() { fm.trigger('hide', {items: items, opts: opts}); }) .fail(function(error) { fm.error(error); }), o = opts || {}, items = o.targets? o.targets : (hashes || fm.selected()), added = [], removed = [], notifyto, files, res; hideData = fm.storage('hide') || {}; if (!jQuery.isPlainObject(hideData)) { hideData = {}; } if (!jQuery.isPlainObject(hideData.items)) { hideData.items = {}; } if (opts._currentType === 'shortcut' || !items.length || (opts._currentType !== 'navbar' && sOrigin !=='navbar' && items[0] === fm.cwd().hash)) { if (hideData.show) { o.hide = true; } else if (Object.keys(hideData.items).length) { o.show = true; } } if (o.reset) { o.show = true; hideCnt = 0; } if (o.show || o.hide) { if (o.show) { hideData.show = true; } else { delete hideData.show; } if (o.show) { fm.storage('hide', o.reset? null : hideData); self.title = fm.i18n('hideHidden'); self.update(o.reset? -1 : void(0), self.title); jQuery.each(hideData.items, function(h) { var f = fm.file(h, true); if (f && (fm.searchStatus.state || !f.phash || fm.file(f.phash))) { added.push(f); } }); if (added.length) { fm.updateCache({added: added}); fm.add({added: added}); } if (o.reset) { hideData = {items: {}}; } return dfrd.resolve(); } items = Object.keys(hideData.items); } if (items.length) { jQuery.each(items, function(i, h) { var f; if (!hideData.items[h]) { f = fm.file(h); if (f) { nameCache[h] = f.i18 || f.name; } hideData.items[h] = nameCache[h]? nameCache[h] : h; } }); hideCnt = Object.keys(hideData.items).length; files = this.files(items); fm.storage('hide', hideData); fm.remove({removed: items}); if (hideData.show) { this.exec(void(0), {hide: true}); } if (!o.hide) { res = {}; res.undo = { cmd : 'hide', callback : function() { var nData = fm.storage('hide'); if (nData) { jQuery.each(items, function(i, h) { delete nData.items[h]; }); hideCnt = Object.keys(nData.items).length; fm.storage('hide', nData); fm.trigger('hide', {items: items, opts: {}}); self.update(hideCnt? 0 : -1); } fm.updateCache({added: files}); fm.add({added: files}); } }; res.redo = { cmd : 'hide', callback : function() { return fm.exec('hide', void(0), {targets: items}); } }; } } return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(res); }; }; /* * File: /js/commands/home.js */ (elFinder.prototype.commands.home = function() { this.title = 'Home'; this.alwaysEnabled = true; this.updateOnSelect = false; this.shortcuts = [{ pattern : 'ctrl+home ctrl+shift+up', description : 'Home' }]; this.getstate = function() { var root = this.fm.root(), cwd = this.fm.cwd().hash; return root && cwd && root != cwd ? 0: -1; }; this.exec = function() { return this.fm.exec('open', this.fm.root()); }; }).prototype = { forceLoad : true }; // this is required command /* * File: /js/commands/info.js */ /** * @class elFinder command "info". * Display dialog with file properties. * * @author Dmitry (dio) Levashov, dio@std42.ru **/ (elFinder.prototype.commands.info = function() { var m = 'msg', fm = this.fm, spclass = 'elfinder-spinner', btnclass = 'elfinder-info-button', msg = { calc : fm.i18n('calc'), size : fm.i18n('size'), unknown : fm.i18n('unknown'), path : fm.i18n('path'), aliasfor : fm.i18n('aliasfor'), modify : fm.i18n('modify'), perms : fm.i18n('perms'), locked : fm.i18n('locked'), dim : fm.i18n('dim'), kind : fm.i18n('kind'), files : fm.i18n('files'), folders : fm.i18n('folders'), roots : fm.i18n('volumeRoots'), items : fm.i18n('items'), yes : fm.i18n('yes'), no : fm.i18n('no'), link : fm.i18n('link'), owner : fm.i18n('owner'), group : fm.i18n('group'), perm : fm.i18n('perm'), getlink : fm.i18n('getLink') }, applyZWSP = function(str, remove) { if (remove) { return str.replace(/\u200B/g, ''); } else { return str.replace(/(\/|\\)/g, "$1\u200B"); } }; this.items = ['size', 'aliasfor', 'path', 'link', 'dim', 'modify', 'perms', 'locked', 'owner', 'group', 'perm']; if (this.options.custom && Object.keys(this.options.custom).length) { jQuery.each(this.options.custom, function(name, details) { details.label && this.items.push(details.label); }); } this.tpl = { main : '<div class="ui-helper-clearfix elfinder-info-title {dirclass}"><span class="elfinder-cwd-icon {class} ui-corner-all"{style}/>{title}</div><table class="elfinder-info-tb">{content}</table>', itemTitle : '<strong>{name}</strong><span class="elfinder-info-kind">{kind}</span>', groupTitle : '<strong>{items}: {num}</strong>', row : '<tr><td class="elfinder-info-label">{label} : </td><td class="{class}">{value}</td></tr>', spinner : '<span>{text}</span> <span class="'+spclass+' '+spclass+'-{name}"/>' }; this.alwaysEnabled = true; this.updateOnSelect = false; this.shortcuts = [{ pattern : 'ctrl+i' }]; this.init = function() { jQuery.each(msg, function(k, v) { msg[k] = fm.i18n(v); }); }; this.getstate = function() { return 0; }; this.exec = function(hashes) { var files = this.files(hashes); if (! files.length) { files = this.files([ this.fm.cwd().hash ]); } var self = this, fm = this.fm, o = this.options, tpl = this.tpl, row = tpl.row, cnt = files.length, content = [], view = tpl.main, l = '{label}', v = '{value}', reqs = [], reqDfrd = null, opts = { title : fm.i18n('selectionInfo'), width : 'auto', close : function() { jQuery(this).elfinderdialog('destroy'); if (reqDfrd && reqDfrd.state() === 'pending') { reqDfrd.reject(); } jQuery.grep(reqs, function(r) { r && r.state() === 'pending' && r.reject(); }); } }, count = [], replSpinner = function(msg, name, className) { dialog.find('.'+spclass+'-'+name).parent().html(msg).addClass(className || ''); }, id = fm.namespace+'-info-'+jQuery.map(files, function(f) { return f.hash; }).join('-'), dialog = fm.getUI().find('#'+id), customActions = [], style = '', hashClass = 'elfinder-font-mono elfinder-info-hash', size, tmb, file, title, dcnt, rdcnt, path, getHashAlgorisms, hideItems; if (!cnt) { return jQuery.Deferred().reject(); } if (dialog.length) { dialog.elfinderdialog('toTop'); return jQuery.Deferred().resolve(); } hideItems = fm.storage('infohides') || fm.arrayFlip(o.hideItems, true); if (cnt === 1) { file = files[0]; if (file.icon) { style = ' '+fm.getIconStyle(file); } view = view.replace('{dirclass}', file.csscls? fm.escape(file.csscls) : '').replace('{class}', fm.mime2class(file.mime)).replace('{style}', style); title = tpl.itemTitle.replace('{name}', fm.escape(file.i18 || file.name)).replace('{kind}', '<span title="'+fm.escape(file.mime)+'">'+fm.mime2kind(file)+'</span>'); tmb = fm.tmb(file); if (!file.read) { size = msg.unknown; } else if (file.mime != 'directory' || file.alias) { size = fm.formatSize(file.size); } else { size = tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size'); count.push(file.hash); } !hideItems.size && content.push(row.replace(l, msg.size).replace(v, size)); !hideItems.aleasfor && file.alias && content.push(row.replace(l, msg.aliasfor).replace(v, file.alias)); if (!hideItems.path) { if (path = fm.path(file.hash, true)) { content.push(row.replace(l, msg.path).replace(v, applyZWSP(fm.escape(path))).replace('{class}', 'elfinder-info-path')); } else { content.push(row.replace(l, msg.path).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'path')).replace('{class}', 'elfinder-info-path')); reqs.push(fm.path(file.hash, true, {notify: null}) .fail(function() { replSpinner(msg.unknown, 'path'); }) .done(function(path) { replSpinner(applyZWSP(path), 'path'); })); } } if (!hideItems.link && file.read) { var href, name_esc = fm.escape(file.name); if (file.url == '1') { content.push(row.replace(l, msg.link).replace(v, '<button class="'+btnclass+' '+spclass+'-url">'+msg.getlink+'</button>')); } else { if (file.url) { href = file.url; } else if (file.mime === 'directory') { if (o.nullUrlDirLinkSelf && file.url === null) { var loc = window.location; href = loc.pathname + loc.search + '#elf_' + file.hash; } else if (file.url !== '' && fm.option('url', (!fm.isRoot(file) && file.phash) || file.hash)) { href = fm.url(file.hash); } } else { href = fm.url(file.hash); } /*href && content.push(row.replace(l, msg.link).replace(v, '<a href="'+href+'" target="_blank">'+name_esc+'</a>'));*/ href && content.push(row.replace(l, msg.link).replace(v, '<a href="'+href+'" target="_blank">'+name_esc+'</a> <a href="mailto:?Subject=WP File Manager Share '+name_esc+'&Body='+href+'" class="mk_elfinder_share_button" title="Share"><button class="button button-primary">Share</button></a>')); } } if (!hideItems.dim) { if (file.dim) { // old api content.push(row.replace(l, msg.dim).replace(v, file.dim)); } else if (file.mime.indexOf('image') !== -1) { if (file.width && file.height) { content.push(row.replace(l, msg.dim).replace(v, file.width+'x'+file.height)); } else { content.push(row.replace(l, msg.dim).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'dim'))); reqs.push(fm.request({ data : {cmd : 'dim', target : file.hash}, preventDefault : true }) .fail(function() { replSpinner(msg.unknown, 'dim'); }) .done(function(data) { replSpinner(data.dim || msg.unknown, 'dim'); if (data.dim) { var dim = data.dim.split('x'); var rfile = fm.file(file.hash); rfile.width = dim[0]; rfile.height = dim[1]; } })); } } } !hideItems.modify && content.push(row.replace(l, msg.modify).replace(v, fm.formatDate(file))); !hideItems.perms && content.push(row.replace(l, msg.perms).replace(v, fm.formatPermissions(file))); !hideItems.locked && content.push(row.replace(l, msg.locked).replace(v, file.locked ? msg.yes : msg.no)); !hideItems.owner && file.owner && content.push(row.replace(l, msg.owner).replace(v, file.owner)); !hideItems.group && file.group && content.push(row.replace(l, msg.group).replace(v, file.group)); !hideItems.perm && file.perm && content.push(row.replace(l, msg.perm).replace(v, fm.formatFileMode(file.perm))); // Get MD5 hash if (window.ArrayBuffer && (fm.options.cdns.sparkmd5 || fm.options.cdns.jssha) && file.mime !== 'directory' && file.size > 0 && (!o.showHashMaxsize || file.size <= o.showHashMaxsize)) { getHashAlgorisms = []; jQuery.each(fm.storage('hashchekcer') || o.showHashAlgorisms, function(i, n) { if (!file[n]) { content.push(row.replace(l, fm.i18n(n)).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', n))); getHashAlgorisms.push(n); } else { content.push(row.replace(l, fm.i18n(n)).replace(v, file[n]).replace('{class}', hashClass)); } }); reqs.push( fm.getContentsHashes(file.hash, getHashAlgorisms).progress(function(hashes) { jQuery.each(getHashAlgorisms, function(i, n) { if (hashes[n]) { replSpinner(hashes[n], n, hashClass); } }); }).always(function() { jQuery.each(getHashAlgorisms, function(i, n) { replSpinner(msg.unknown, n); }); }) ); } // Add custom info fields if (o.custom) { jQuery.each(o.custom, function(name, details) { if ( !hideItems[details.label] && (!details.mimes || jQuery.grep(details.mimes, function(m){return (file.mime === m || file.mime.indexOf(m+'/') === 0)? true : false;}).length) && (!details.hashRegex || file.hash.match(details.hashRegex)) ) { // Add to the content content.push(row.replace(l, fm.i18n(details.label)).replace(v , details.tpl.replace('{id}', id))); // Register the action if (details.action && (typeof details.action == 'function')) { customActions.push(details.action); } } }); } } else { view = view.replace('{class}', 'elfinder-cwd-icon-group'); title = tpl.groupTitle.replace('{items}', msg.items).replace('{num}', cnt); dcnt = jQuery.grep(files, function(f) { return f.mime == 'directory' ? true : false ; }).length; if (!dcnt) { size = 0; jQuery.each(files, function(h, f) { var s = parseInt(f.size); if (s >= 0 && size >= 0) { size += s; } else { size = 'unknown'; } }); content.push(row.replace(l, msg.kind).replace(v, msg.files)); !hideItems.size && content.push(row.replace(l, msg.size).replace(v, fm.formatSize(size))); } else { rdcnt = jQuery.grep(files, function(f) { return f.mime === 'directory' && (! f.phash || f.isroot)? true : false ; }).length; dcnt -= rdcnt; content.push(row.replace(l, msg.kind).replace(v, (rdcnt === cnt || dcnt === cnt)? msg[rdcnt? 'roots' : 'folders'] : jQuery.map({roots: rdcnt, folders: dcnt, files: cnt - rdcnt - dcnt}, function(c, t) { return c? msg[t]+' '+c : null; }).join(', '))); !hideItems.size && content.push(row.replace(l, msg.size).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size'))); count = jQuery.map(files, function(f) { return f.hash; }); } } view = view.replace('{title}', title).replace('{content}', content.join('').replace(/{class}/g, '')); dialog = self.fmDialog(view, opts); dialog.attr('id', id).one('mousedown', '.elfinder-info-path', function() { jQuery(this).html(applyZWSP(jQuery(this).html(), true)); }); if (fm.UA.Mobile && jQuery.fn.tooltip) { dialog.children('.ui-dialog-content .elfinder-info-title').tooltip({ classes: { 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow' }, tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow', track: true }); } if (file && file.url == '1') { dialog.on('click', '.'+spclass+'-url', function(){ jQuery(this).parent().html(tpl.spinner.replace('{text}', fm.i18n('ntfurl')).replace('{name}', 'url')); fm.request({ data : {cmd : 'url', target : file.hash}, preventDefault : true }) .fail(function() { replSpinner(name_esc, 'url'); }) .done(function(data) { if (data.url) { replSpinner('<a href="'+data.url+'" target="_blank">'+name_esc+'</a>' || name_esc, 'url'); var rfile = fm.file(file.hash); rfile.url = data.url; } else { replSpinner(name_esc, 'url'); } }); }); } // load thumbnail if (tmb) { jQuery('<img/>') .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); }) .attr('src', tmb.url); } // send request to count total size if (count.length) { reqDfrd = fm.getSize(count).done(function(data) { replSpinner(data.formated, 'size'); }).fail(function() { replSpinner(msg.unknown, 'size'); }); } // call custom actions if (customActions.length) { jQuery.each(customActions, function(i, action) { try { action(file, fm, dialog); } catch(e) { fm.debug('error', e); } }); } return jQuery.Deferred().resolve(); }; }).prototype = { forceLoad : true }; // this is required command /* * File: /js/commands/mkdir.js */ /** * @class elFinder command "mkdir" * Create new folder * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.mkdir = function() { var fm = this.fm, self = this, curOrg; this.value = ''; this.disableOnSearch = true; this.updateOnSelect = false; this.syncTitleOnChange = true; this.mime = 'directory'; this.prefix = 'untitled folder'; this.exec = function(select, cOpts) { var onCwd; if (select && select.length && cOpts && cOpts._currentType && cOpts._currentType === 'navbar') { this.origin = cOpts._currentType; this.data = { target: select[0] }; } else { onCwd = fm.cwd().hash === select[0]; this.origin = curOrg && !onCwd? curOrg : 'cwd'; delete this.data; } if (! select && ! this.options.intoNewFolderToolbtn) { fm.getUI('cwd').trigger('unselectall'); } //this.move = (!onCwd && curOrg !== 'navbar' && fm.selected().length)? true : false; this.move = this.value === fm.i18n('cmdmkdirin'); return jQuery.proxy(fm.res('mixin', 'make'), self)(); }; this.shortcuts = [{ pattern : 'ctrl+shift+n' }]; this.init = function() { if (this.options.intoNewFolderToolbtn) { this.syncTitleOnChange = true; } }; fm.bind('select contextmenucreate closecontextmenu', function(e) { var sel = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected(); self.className = 'mkdir'; curOrg = e.data && sel.length? (e.data.origin || e.data.type || '') : ''; if (!self.options.intoNewFolderToolbtn && curOrg === '') { curOrg = 'cwd'; } if (sel.length && curOrg !== 'navbar' && curOrg !== 'cwd' && fm.cwd().hash !== sel[0]) { self.title = fm.i18n('cmdmkdirin'); self.className += ' elfinder-button-icon-mkdirin'; } else { self.title = fm.i18n('cmdmkdir'); } if (e.type !== 'closecontextmenu') { self.update(void(0), self.title); } else { requestAnimationFrame(function() { self.update(void(0), self.title); }); } }); this.getstate = function(select) { var cwd = fm.cwd(), sel = (curOrg === 'navbar' || (select && select[0] !== cwd.hash))? this.files(select || fm.selected()) : [], cnt = sel.length; if (curOrg === 'navbar') { return cnt && sel[0].write && sel[0].read? 0 : -1; } else { return cwd.write && (!cnt || jQuery.grep(sel, function(f) { return f.read && ! f.locked? true : false; }).length == cnt)? 0 : -1; } }; }; /* * File: /js/commands/mkfile.js */ /** * @class elFinder command "mkfile" * Create new empty file * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.mkfile = function() { var self = this; this.disableOnSearch = true; this.updateOnSelect = false; this.mime = 'text/plain'; this.prefix = 'untitled file.txt'; this.variants = []; this.getTypeName = function(mime, type) { var fm = self.fm, name; if (name = fm.messages['kind' + fm.kinds[mime]]) { name = fm.i18n(['extentiontype', type.toUpperCase(), name]); } else { name = fm.i18n(['extentionfile', type.toUpperCase()]); } return name; }; this.fm.bind('open reload canMakeEmptyFile', function() { var fm = self.fm, hides = fm.storage('mkfileHides') || {}; self.variants = []; if (fm.mimesCanMakeEmpty) { jQuery.each(fm.mimesCanMakeEmpty, function(mime, type) { type && !hides[mime] && fm.uploadMimeCheck(mime) && self.variants.push([mime, self.getTypeName(mime, type)]); }); } self.change(); }); this.getstate = function() { return this.fm.cwd().write ? 0 : -1; }; this.exec = function(_dum, mime) { var fm = self.fm, type, err; if (type = fm.mimesCanMakeEmpty[mime]) { if (fm.uploadMimeCheck(mime)) { this.mime = mime; this.prefix = fm.i18n(['untitled file', type]); return jQuery.proxy(fm.res('mixin', 'make'), self)(); } err = ['errMkfile', self.getTypeName(mime, type)]; } return jQuery.Deferred().reject(err); }; }; /* * File: /js/commands/netmount.js */ /** * @class elFinder command "netmount" * Mount network volume with user credentials. * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.netmount = function() { var self = this, hasMenus = false, content; this.alwaysEnabled = true; this.updateOnSelect = false; this.drivers = []; this.handlers = { load : function() { var fm = self.fm; self.drivers = fm.netDrivers; if (self.drivers.length) { requestAnimationFrame(function() { jQuery.each(self.drivers, function() { var d = self.options[this]; if (d) { hasMenus = true; if (d.integrateInfo) { fm.trigger('helpIntegration', Object.assign({cmd: 'netmount'}, d.integrateInfo)); } } }); }); } } }; this.getstate = function() { return hasMenus ? 0 : -1; }; this.exec = function() { var fm = self.fm, dfrd = jQuery.Deferred(), o = self.options, create = function() { var winFocus = function() { inputs.protocol.trigger('change', 'winfocus'); }, inputs = { protocol : jQuery('<select/>') .on('change', function(e, data){ var protocol = this.value; content.find('.elfinder-netmount-tr').hide(); content.find('.elfinder-netmount-tr-'+protocol).show(); dialogNode && dialogNode.children('.ui-dialog-buttonpane:first').find('button').show(); if (typeof o[protocol].select == 'function') { o[protocol].select(fm, e, data); } requestAnimationFrame(function() { content.find('input:text.elfinder-tabstop:visible:first').trigger('focus'); }); }) .addClass('ui-corner-all') }, opts = { title : fm.i18n('netMountDialogTitle'), resizable : false, modal : true, destroyOnClose : false, open : function() { jQuery(window).on('focus.'+fm.namespace, winFocus); inputs.protocol.trigger('change'); }, close : function() { dfrd.state() == 'pending' && dfrd.reject(); jQuery(window).off('focus.'+fm.namespace, winFocus); }, buttons : {} }, doMount = function() { var protocol = inputs.protocol.val(), data = {cmd : 'netmount', protocol: protocol}, cur = o[protocol]; jQuery.each(content.find('input.elfinder-netmount-inputs-'+protocol), function(name, input) { var val, elm; elm = jQuery(input); if (elm.is(':radio,:checkbox')) { if (elm.is(':checked')) { val = jQuery.trim(elm.val()); } } else { val = jQuery.trim(elm.val()); } if (val) { data[input.name] = val; } }); if (!data.host) { return fm.trigger('error', {error : 'errNetMountHostReq', opts : {modal: true}}); } fm.request({data : data, notify : {type : 'netmount', cnt : 1, hideCnt : true}}) .done(function(data) { var pdir; if (data.added && data.added.length) { if (data.added[0].phash) { if (pdir = fm.file(data.added[0].phash)) { if (! pdir.dirs) { pdir.dirs = 1; fm.change({ changed: [ pdir ] }); } } } fm.one('netmountdone', function() { fm.exec('open', data.added[0].hash); }); } dfrd.resolve(); }) .fail(function(error) { if (cur.fail && typeof cur.fail == 'function') { cur.fail(fm, fm.parseError(error)); } dfrd.reject(error); }); self.dialog.elfinderdialog('close'); }, form = jQuery('<form autocomplete="off"/>').on('keydown', 'input', function(e) { var comp = true, next; if (e.keyCode === jQuery.ui.keyCode.ENTER) { jQuery.each(form.find('input:visible:not(.elfinder-input-optional)'), function() { if (jQuery(this).val() === '') { comp = false; next = jQuery(this); return false; } }); if (comp) { doMount(); } else { next.trigger('focus'); } } }), hidden = jQuery('<div/>'), dialog; content = jQuery('<table class="elfinder-info-tb elfinder-netmount-tb"/>') .append(jQuery('<tr/>').append(jQuery('<td>'+fm.i18n('protocol')+'</td>')).append(jQuery('<td/>').append(inputs.protocol))); jQuery.each(self.drivers, function(i, protocol) { if (o[protocol]) { inputs.protocol.append('<option value="'+protocol+'">'+fm.i18n(o[protocol].name || protocol)+'</option>'); jQuery.each(o[protocol].inputs, function(name, input) { input.attr('name', name); if (input.attr('type') != 'hidden') { input.addClass('ui-corner-all elfinder-netmount-inputs-'+protocol); content.append(jQuery('<tr/>').addClass('elfinder-netmount-tr elfinder-netmount-tr-'+protocol).append(jQuery('<td>'+fm.i18n(name)+'</td>')).append(jQuery('<td/>').append(input))); } else { input.addClass('elfinder-netmount-inputs-'+protocol); hidden.append(input); } }); o[protocol].protocol = inputs.protocol; } }); content.append(hidden); content.find('.elfinder-netmount-tr').hide(); opts.buttons[fm.i18n('btnMount')] = doMount; opts.buttons[fm.i18n('btnCancel')] = function() { self.dialog.elfinderdialog('close'); }; content.find('select,input').addClass('elfinder-tabstop'); dialog = self.fmDialog(form.append(content), opts); dialogNode = dialog.closest('.ui-dialog'); dialog.ready(function(){ inputs.protocol.trigger('change'); dialog.elfinderdialog('posInit'); }); return dialog; }, dialogNode; if (!self.dialog) { self.dialog = create(); } else { self.dialog.elfinderdialog('open'); } return dfrd.promise(); }; self.fm.bind('netmount', function(e) { var d = e.data || null, o = self.options; if (d && d.protocol) { if (o[d.protocol] && typeof o[d.protocol].done == 'function') { o[d.protocol].done(self.fm, d); content.find('select,input').addClass('elfinder-tabstop'); self.dialog.elfinderdialog('tabstopsInit'); } } }); }; elFinder.prototype.commands.netunmount = function() { var self = this; this.alwaysEnabled = true; this.updateOnSelect = false; this.drivers = []; this.handlers = { load : function() { this.drivers = this.fm.netDrivers; } }; this.getstate = function(sel) { var fm = this.fm, file; return !!sel && this.drivers.length && !this._disabled && (file = fm.file(sel[0])) && file.netkey ? 0 : -1; }; this.exec = function(hashes) { var self = this, fm = this.fm, dfrd = jQuery.Deferred() .fail(function(error) { error && fm.error(error); }), drive = fm.file(hashes[0]), childrenRoots = function(hash) { var roots = [], work; if (fm.leafRoots) { work = []; jQuery.each(fm.leafRoots, function(phash, hashes) { var parents = fm.parents(phash), idx, deep; if ((idx = jQuery.inArray(hash, parents)) !== -1) { idx = parents.length - idx; jQuery.each(hashes, function(i, h) { work.push({i: idx, hash: h}); }); } }); if (work.length) { work.sort(function(a, b) { return a.i < b.i; }); jQuery.each(work, function(i, o) { roots.push(o.hash); }); } } return roots; }; if (this._disabled) { return dfrd.reject(); } if (dfrd.state() == 'pending') { fm.confirm({ title : self.title, text : fm.i18n('confirmUnmount', drive.name), accept : { label : 'btnUnmount', callback : function() { var target = drive.hash, roots = childrenRoots(target), requests = [], removed = [], doUmount = function() { jQuery.when(requests).done(function() { fm.request({ data : {cmd : 'netmount', protocol : 'netunmount', host: drive.netkey, user : target, pass : 'dum'}, notify : {type : 'netunmount', cnt : 1, hideCnt : true}, preventFail : true }) .fail(function(error) { dfrd.reject(error); }) .done(function(data) { drive.volumeid && delete fm.volumeExpires[drive.volumeid]; dfrd.resolve(); }); }).fail(function(error) { if (removed.length) { fm.remove({ removed: removed }); } dfrd.reject(error); }); }; if (roots.length) { fm.confirm({ title : self.title, text : (function() { var msgs = ['unmountChildren']; jQuery.each(roots, function(i, hash) { msgs.push([fm.file(hash).name]); }); return msgs; })(), accept : { label : 'btnUnmount', callback : function() { jQuery.each(roots, function(i, hash) { var d = fm.file(hash); if (d.netkey) { requests.push(fm.request({ data : {cmd : 'netmount', protocol : 'netunmount', host: d.netkey, user : d.hash, pass : 'dum'}, notify : {type : 'netunmount', cnt : 1, hideCnt : true}, preventDefault : true }).done(function(data) { if (data.removed) { d.volumeid && delete fm.volumeExpires[d.volumeid]; removed = removed.concat(data.removed); } })); } }); doUmount(); } }, cancel : { label : 'btnCancel', callback : function() { dfrd.reject(); } } }); } else { requests = null; doUmount(); } } }, cancel : { label : 'btnCancel', callback : function() { dfrd.reject(); } } }); } return dfrd; }; }; /* * File: /js/commands/open.js */ /** * @class elFinder command "open" * Enter folder or open files in new windows * * @author Dmitry (dio) Levashov **/ (elFinder.prototype.commands.open = function() { var fm = this.fm; this.alwaysEnabled = true; this.noChangeDirOnRemovedCwd = true; this._handlers = { dblclick : function(e) { e.preventDefault(); fm.exec('open', e.data && e.data.file? [ e.data.file ]: void(0)); }, 'select enable disable reload' : function(e) { this.update(e.type == 'disable' ? -1 : void(0)); } }; this.shortcuts = [{ pattern : 'ctrl+down numpad_enter'+(fm.OS != 'mac' && ' enter') }]; this.getstate = function(select) { var sel = this.files(select), cnt = sel.length; return cnt == 1 ? (sel[0].read? 0 : -1) : (cnt && !fm.UA.Mobile) ? (jQuery.grep(sel, function(file) { return file.mime == 'directory' || ! file.read ? false : true;}).length == cnt ? 0 : -1) : -1; }; this.exec = function(hashes, cOpts) { var dfrd = jQuery.Deferred().fail(function(error) { error && fm.error(error); }), files = this.files(hashes), cnt = files.length, thash = (typeof cOpts == 'object')? cOpts.thash : false, opts = this.options, into = opts.into || 'window', file, url, s, w, imgW, imgH, winW, winH, reg, link, html5dl, inline, selAct, cmd; if (!cnt && !thash) { { return dfrd.reject(); } } // open folder if (thash || (cnt == 1 && (file = files[0]) && file.mime == 'directory')) { if (!thash && file && !file.read) { return dfrd.reject(['errOpen', file.name, 'errPerm']); } else { if (fm.keyState.ctrlKey && (fm.keyState.shiftKey || typeof fm.options.getFileCallback !== 'function')) { if (fm.getCommand('opennew')) { return fm.exec('opennew', [thash? thash : file.hash]); } } return fm.request({ data : {cmd : 'open', target : thash || file.hash}, notify : {type : 'open', cnt : 1, hideCnt : true}, syncOnFail : true, lazy : false }); } } files = jQuery.grep(files, function(file) { return file.mime != 'directory' ? true : false; }); // nothing to open or files and folders selected - do nothing if (cnt != files.length) { return dfrd.reject(); } var doOpen = function() { var wnd, target, getOnly; try { reg = new RegExp(fm.option('dispInlineRegex'), 'i'); } catch(e) { reg = false; } // open files link = jQuery('<a>').hide().appendTo(jQuery('body')), html5dl = (typeof link.get(0).download === 'string'); cnt = files.length; while (cnt--) { target = 'elf_open_window'; file = files[cnt]; if (!file.read) { return dfrd.reject(['errOpen', file.name, 'errPerm']); } inline = (reg && file.mime.match(reg)); url = fm.openUrl(file.hash, !inline); if (fm.UA.Mobile || !inline) { if (html5dl) { if (!inline) { link.attr('download', file.name); } else { link.attr('target', '_blank'); } link.attr('href', url).get(0).click(); } else { wnd = window.open(url); if (!wnd) { return dfrd.reject('errPopup'); } } } else { getOnly = (typeof opts.method === 'string' && opts.method.toLowerCase() === 'get'); if (!getOnly && url.indexOf(fm.options.url) === 0 && fm.customData && Object.keys(fm.customData).length // Since playback by POST request can not be done in Chrome, media allows GET request && !file.mime.match(/^(?:video|audio)/) ) { // Send request as 'POST' method to hide custom data at location bar url = ''; } if (into === 'window') { // set window size for image if set imgW = winW = Math.round(2 * screen.availWidth / 3); imgH = winH = Math.round(2 * screen.availHeight / 3); if (parseInt(file.width) && parseInt(file.height)) { imgW = parseInt(file.width); imgH = parseInt(file.height); } else if (file.dim) { s = file.dim.split('x'); imgW = parseInt(s[0]); imgH = parseInt(s[1]); } if (winW >= imgW && winH >= imgH) { winW = imgW; winH = imgH; } else { if ((imgW - winW) > (imgH - winH)) { winH = Math.round(imgH * (winW / imgW)); } else { winW = Math.round(imgW * (winH / imgH)); } } w = 'width='+winW+',height='+winH; wnd = window.open(url, target, w + ',top=50,left=50,scrollbars=yes,resizable=yes,titlebar=no'); } else { if (into === 'tabs') { target = file.hash; } wnd = window.open('about:blank', target); } if (!wnd) { return dfrd.reject('errPopup'); } if (url === '') { var form = document.createElement("form"); form.action = fm.options.url; form.method = 'POST'; form.target = target; form.style.display = 'none'; var params = Object.assign({}, fm.customData, { cmd: 'file', target: file.hash, _t: file.ts || parseInt(+new Date()/1000) }); jQuery.each(params, function(key, val) { var input = document.createElement("input"); input.name = key; input.value = val; form.appendChild(input); }); document.body.appendChild(form); form.submit(); } else if (into !== 'window') { wnd.location = url; } jQuery(wnd).trigger('focus'); } } link.remove(); return dfrd.resolve(hashes); }; if (cnt > 1) { fm.confirm({ title: 'openMulti', text : ['openMultiConfirm', cnt + ''], accept : { label : 'cmdopen', callback : function() { doOpen(); } }, cancel : { label : 'btnCancel', callback : function() { dfrd.reject(); } }, buttons : (fm.getCommand('zipdl') && fm.isCommandEnabled('zipdl', fm.cwd().hash))? [ { label : 'cmddownload', callback : function() { fm.exec('download', hashes); dfrd.reject(); } } ] : [] }); } else { selAct = fm.storage('selectAction') || opts.selectAction; if (selAct) { jQuery.each(selAct.split('/'), function() { var cmdName = this.valueOf(); if (cmdName !== 'open' && (cmd = fm.getCommand(cmdName)) && cmd.enabled()) { return false; } cmd = null; }); if (cmd) { return fm.exec(cmd.name); } } doOpen(); } return dfrd; }; }).prototype = { forceLoad : true }; // this is required command /* * File: /js/commands/opendir.js */ /** * @class elFinder command "opendir" * Enter parent folder * * @author Naoki Sawada **/ elFinder.prototype.commands.opendir = function() { this.alwaysEnabled = true; this.getstate = function() { var sel = this.fm.selected(), cnt = sel.length, wz; if (cnt !== 1) { return -1; } wz = this.fm.getUI('workzone'); return wz.hasClass('elfinder-search-result')? 0 : -1; }; this.exec = function(hashes) { var fm = this.fm, dfrd = jQuery.Deferred(), files = this.files(hashes), cnt = files.length, hash, pcheck = null; if (!cnt || !files[0].phash) { return dfrd.reject(); } hash = files[0].phash; fm.trigger('searchend', { noupdate: true }); fm.request({ data : {cmd : 'open', target : hash}, notify : {type : 'open', cnt : 1, hideCnt : true}, syncOnFail : false }); return dfrd; }; }; /* * File: /js/commands/opennew.js */ /** * @class elFinder command "opennew" * Open folder in new window * * @author Naoki Sawada **/ elFinder.prototype.commands.opennew = function() { var fm = this.fm; this.shortcuts = [{ pattern : (typeof(fm.options.getFileCallback) === 'function'? 'shift+' : '') + 'ctrl+enter' }]; this.getstate = function(select) { var sel = this.files(select), cnt = sel.length; return cnt === 1 ? (sel[0].mime === 'directory' && sel[0].read? 0 : -1) : -1; }; this.exec = function(hashes) { var dfrd = jQuery.Deferred(), files = this.files(hashes), cnt = files.length, opts = this.options, file, loc, url, win; // open folder to new tab (window) if (cnt === 1 && (file = files[0]) && file.mime === 'directory') { loc = window.location; if (opts.url) { url = opts.url; } else { url = loc.pathname; } if (opts.useOriginQuery) { if (!url.match(/\?/)) { url += loc.search; } else if (loc.search) { url += '&' + loc.search.substr(1); } } url += '#elf_' + file.hash; win = window.open(url, '_blank'); setTimeout(function() { win.focus(); }, 1000); return dfrd.resolve(); } else { return dfrd.reject(); } }; }; /* * File: /js/commands/paste.js */ /** * @class elFinder command "paste" * Paste filesfrom clipboard into directory. * If files pasted in its parent directory - files duplicates will created * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.paste = function() { this.updateOnSelect = false; this.handlers = { changeclipboard : function() { this.update(); } }; this.shortcuts = [{ pattern : 'ctrl+v shift+insert' }]; this.getstate = function(dst) { if (this._disabled) { return -1; } if (dst) { if (Array.isArray(dst)) { if (dst.length != 1) { return -1; } dst = this.fm.file(dst[0]); } } else { dst = this.fm.cwd(); } return this.fm.clipboard().length && dst.mime == 'directory' && dst.write ? 0 : -1; }; this.exec = function(select, cOpts) { var self = this, fm = self.fm, opts = cOpts || {}, dst = select ? this.files(select)[0] : fm.cwd(), files = fm.clipboard(), cnt = files.length, cut = cnt ? files[0].cut : false, cmd = opts._cmd? opts._cmd : (cut? 'move' : 'copy'), error = 'err' + cmd.charAt(0).toUpperCase() + cmd.substr(1), fpaste = [], fcopy = [], dfrd = jQuery.Deferred() .fail(function(error) { error && fm.error(error); }) .always(function() { fm.unlockfiles({files : jQuery.map(files, function(f) { return f.hash; })}); }), copy = function(files) { return files.length && fm._commands.duplicate ? fm.exec('duplicate', files) : jQuery.Deferred().resolve(); }, paste = function(files) { var dfrd = jQuery.Deferred(), existed = [], hashes = {}, intersect = function(files, names) { var ret = [], i = files.length; while (i--) { jQuery.inArray(files[i].name, names) !== -1 && ret.unshift(i); } return ret; }, confirm = function(ndx) { var i = existed[ndx], file = files[i], last = ndx == existed.length-1; if (!file) { return; } fm.confirm({ title : fm.i18n(cmd + 'Files'), text : ['errExists', file.name, cmd === 'restore'? 'confirmRest' : 'confirmRepl'], all : !last, accept : { label : 'btnYes', callback : function(all) { !last && !all ? confirm(++ndx) : paste(files); } }, reject : { label : 'btnNo', callback : function(all) { var i; if (all) { i = existed.length; while (ndx < i--) { files[existed[i]].remove = true; } } else { files[existed[ndx]].remove = true; } !last && !all ? confirm(++ndx) : paste(files); } }, cancel : { label : 'btnCancel', callback : function() { dfrd.resolve(); } }, buttons : [ { label : 'btnBackup', callback : function(all) { var i; if (all) { i = existed.length; while (ndx < i--) { files[existed[i]].rename = true; } } else { files[existed[ndx]].rename = true; } !last && !all ? confirm(++ndx) : paste(files); } } ] }); }, valid = function(names) { var exists = {}, existedArr; if (names) { if (Array.isArray(names)) { if (names.length) { if (typeof names[0] == 'string') { // elFinder <= 2.1.6 command `is` results existed = intersect(files, names); } else { jQuery.each(names, function(i, v) { exists[v.name] = v.hash; }); existed = intersect(files, jQuery.map(exists, function(h, n) { return n; })); jQuery.each(files, function(i, file) { if (exists[file.name]) { hashes[exists[file.name]] = file.name; } }); } } } else { existedArr = []; existed = jQuery.map(names, function(n) { if (typeof n === 'string') { return n; } else { // support to >=2.1.11 plugin Normalizer, Sanitizer existedArr = existedArr.concat(n); return false; } }); if (existedArr.length) { existed = existed.concat(existedArr); } existed = intersect(files, existed); hashes = names; } } existed.length ? confirm(0) : paste(files); }, paste = function(selFiles) { var renames = [], files = jQuery.grep(selFiles, function(file) { if (file.rename) { renames.push(file.name); } return !file.remove ? true : false; }), cnt = files.length, groups = {}, args = [], targets, reqData; if (!cnt) { return dfrd.resolve(); } targets = jQuery.map(files, function(f) { return f.hash; }); reqData = {cmd : 'paste', dst : dst.hash, targets : targets, cut : cut ? 1 : 0, renames : renames, hashes : hashes, suffix : fm.options.backupSuffix}; if (fm.api < 2.1) { reqData.src = files[0].phash; } fm.request({ data : reqData, notify : {type : cmd, cnt : cnt}, navigate : { toast : opts.noToast? {} : { inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd' + cmd)]), action: { cmd: 'open', msg: 'cmdopendir', data: [dst.hash], done: 'select', cwdNot: dst.hash }} } } }) .done(function(data) { var dsts = {}, added = data.added && data.added.length? data.added : null; if (cut && added) { // undo/redo jQuery.each(files, function(i, f) { var phash = f.phash, srcHash = function(name) { var hash; jQuery.each(added, function(i, f) { if (f.name === name) { hash = f.hash; return false; } }); return hash; }, shash = srcHash(f.name); if (shash) { if (dsts[phash]) { dsts[phash].push(shash); } else { dsts[phash] = [ shash ]; } } }); if (Object.keys(dsts).length) { data.undo = { cmd : 'move', callback : function() { var reqs = []; jQuery.each(dsts, function(dst, targets) { reqs.push(fm.request({ data : {cmd : 'paste', dst : dst, targets : targets, cut : 1}, notify : {type : 'undo', cnt : targets.length} })); }); return jQuery.when.apply(null, reqs); } }; data.redo = { cmd : 'move', callback : function() { return fm.request({ data : reqData, notify : {type : 'redo', cnt : cnt} }); } }; } } dfrd.resolve(data); }) .fail(function() { dfrd.reject(); }) .always(function() { fm.unlockfiles({files : files}); }); }, internames; if (!fm.isCommandEnabled(self.name, dst.hash) || !files.length) { return dfrd.resolve(); } if (fm.oldAPI) { paste(files); } else { if (!fm.option('copyOverwrite', dst.hash)) { paste(files); } else { internames = jQuery.map(files, function(f) { return f.name; }); dst.hash == fm.cwd().hash ? valid(jQuery.map(fm.files(), function(file) { return file.phash == dst.hash ? {hash: file.hash, name: file.name} : null; })) : fm.request({ data : {cmd : 'ls', target : dst.hash, intersect : internames}, notify : {type : 'prepare', cnt : 1, hideCnt : true}, preventFail : true }) .always(function(data) { valid(data.list); }); } } return dfrd; }, parents, fparents; if (!cnt || !dst || dst.mime != 'directory') { return dfrd.reject(); } if (!dst.write) { return dfrd.reject([error, files[0].name, 'errPerm']); } parents = fm.parents(dst.hash); jQuery.each(files, function(i, file) { if (!file.read) { return !dfrd.reject([error, file.name, 'errPerm']); } if (cut && file.locked) { return !dfrd.reject(['errLocked', file.name]); } if (jQuery.inArray(file.hash, parents) !== -1) { return !dfrd.reject(['errCopyInItself', file.name]); } if (file.mime && file.mime !== 'directory' && ! fm.uploadMimeCheck(file.mime, dst.hash)) { return !dfrd.reject([error, file.name, 'errUploadMime']); } fparents = fm.parents(file.hash); fparents.pop(); if (jQuery.inArray(dst.hash, fparents) !== -1) { if (jQuery.grep(fparents, function(h) { var d = fm.file(h); return d.phash == dst.hash && d.name == file.name ? true : false; }).length) { return !dfrd.reject(['errReplByChild', file.name]); } } if (file.phash == dst.hash) { fcopy.push(file.hash); } else { fpaste.push({ hash : file.hash, phash : file.phash, name : file.name }); } }); if (dfrd.state() == 'rejected') { return dfrd; } jQuery.when( copy(fcopy), paste(fpaste) ) .done(function(cr, pr) { dfrd.resolve(pr && pr.undo? pr : void(0)); }) .fail(function() { dfrd.reject(); }) .always(function() { cut && fm.clipboard([]); }); return dfrd; }; }; /* * File: /js/commands/places.js */ /** * @class elFinder command "places" * Regist to Places * * @author Naoki Sawada **/ elFinder.prototype.commands.places = function() { var self = this, fm = this.fm, filter = function(hashes) { return jQuery.grep(self.files(hashes), function(f) { return f.mime == 'directory' ? true : false; }); }, places = null; this.getstate = function(select) { var sel = this.hashes(select), cnt = sel.length; return places && cnt && cnt == filter(sel).length ? 0 : -1; }; this.exec = function(hashes) { var files = this.files(hashes); places.trigger('regist', [ files ]); return jQuery.Deferred().resolve(); }; fm.one('load', function(){ places = fm.ui.places; }); }; /* * File: /js/commands/preference.js */ /** * @class elFinder command "preference" * "Preference" dialog * * @author Naoki Sawada **/ elFinder.prototype.commands.preference = function() { var self = this, fm = this.fm, r = 'replace', tab = '<li class="' + fm.res('class', 'tabstab') + ' elfinder-preference-tab-{id}"><a href="#'+fm.namespace+'-preference-{id}" id="'+fm.namespace+'-preference-tab-{id}" class="ui-tabs-anchor {class}">{title}</a></li>', base = jQuery('<div class="ui-tabs ui-widget ui-widget-content ui-corner-all elfinder-preference">'), ul = jQuery('<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-top">'), tabs = jQuery('<div class="elfinder-preference-tabs ui-tabs-panel ui-widget-content ui-corner-bottom"/>'), sep = '<div class="elfinder-preference-separator"/>', selfUrl = jQuery('base').length? document.location.href.replace(/#.*$/, '') : '', selectTab = function(tab) { jQuery('#'+fm.namespace+'-preference-tab-'+tab).trigger('mouseover').trigger('click'); openTab = tab; }, clTabActive = fm.res('class', 'tabsactive'), build = function() { var cats = self.options.categories || { 'language' : ['language'], 'theme' : ['theme'], 'toolbar' : ['toolbarPref'], 'workspace' : ['iconSize','columnPref', 'selectAction', 'makefileTypes', 'useStoredEditor', 'editorMaximized', 'showHidden'], 'dialog' : ['autoFocusDialog'], 'selectionInfo' : ['infoItems', 'hashChecker'], 'reset' : ['clearBrowserData'], 'all' : true }, forms = self.options.prefs || ['language', 'theme', 'toolbarPref', 'iconSize', 'columnPref', 'selectAction', 'makefileTypes', 'useStoredEditor', 'editorMaximized', 'showHidden', 'infoItems', 'hashChecker', 'autoFocusDialog', 'clearBrowserData']; forms = fm.arrayFlip(forms, true); if (fm.options.getFileCallback) { delete forms.selectAction; } forms.language && (forms.language = (function() { var langSel = jQuery('<select/>').on('change', function() { var lang = jQuery(this).val(); fm.storage('lang', lang); jQuery('#'+fm.id).elfinder('reload'); }), optTags = [], langs = self.options.langs || { ar: 'اللغة العربية', bg: 'Български', ca: 'Català', cs: 'Čeština', da: 'Dansk', de: 'Deutsch', el: 'Ελληνικά', en: 'English', es: 'Español', fa: 'فارسی', fo: 'Føroyskt', fr: 'Français', he: 'עברית', hr: 'Hrvatski', hu: 'Magyar', id: 'Bahasa Indonesia', it: 'Italiano', ja: '日本語', ko: '한국어', nl: 'Nederlands', no: 'Norsk', pl: 'Polski', pt_BR: 'Português', ro: 'Română', ru: 'Pусский', si: 'සිංහල', sk: 'Slovenčina', sl: 'Slovenščina', sr: 'Srpski', sv: 'Svenska', tr: 'Türkçe', ug_CN: 'ئۇيغۇرچە', uk: 'Український', vi: 'Tiếng Việt', zh_CN: '简体中文', zh_TW: '正體中文' }; jQuery.each(langs, function(lang, name) { optTags.push('<option value="'+lang+'">'+name+'</option>'); }); return langSel.append(optTags.join('')).val(fm.lang); })()); forms.theme && (forms.theme = (function() { var cnt = fm.options.themes? Object.keys(fm.options.themes).length : 0; if (cnt === 0 || (cnt === 1 && fm.options.themes.default)) { return null; } var themeSel = jQuery('<select/>').on('change', function() { var theme = jQuery(this).val(); fm.changeTheme(theme).storage('theme', theme); }), optTags = [], tpl = { image: '<img class="elfinder-preference-theme elfinder-preference-theme-image" src="$2" />', link: '<a href="$1" target="_blank" title="$3">$2</a>', data: '<dt>$1</dt><dd><span class="elfinder-preference-theme elfinder-preference-theme-$0">$2</span></dd>' }, items = ['image', 'description', 'author', 'email', 'license'], render = function(key, data) { }, defBtn = jQuery('<button class="ui-button ui-corner-all ui-widget elfinder-preference-theme-default"/>').text(fm.i18n('default')).on('click', function(e) { themeSel.val('default').trigger('change'); }), list = jQuery('<div class="elfinder-reference-hide-taball"/>').on('click', 'button', function() { var val = jQuery(this).data('themeid'); themeSel.val(val).trigger('change'); }); if (!fm.options.themes.default) { themeSel.append('<option value="default">'+fm.i18n('default')+'</option>'); } jQuery.each(fm.options.themes, function(id, val) { var opt = jQuery('<option class="elfinder-theme-option-'+id+'" value="'+id+'">'+fm.i18n(id)+'</option>'), dsc = jQuery('<fieldset class="ui-widget ui-widget-content ui-corner-all elfinder-theme-list-'+id+'"><legend>'+fm.i18n(id)+'</legend><div><span class="elfinder-spinner"/></div></fieldset>'), tm; themeSel.append(opt); list.append(dsc); tm = setTimeout(function() { dsc.find('span.elfinder-spinner').replaceWith(fm.i18n(['errRead', id])); }, 10000); fm.getTheme(id).always(function() { tm && clearTimeout(tm); }).done(function(data) { var link, val = jQuery(), dl = jQuery('<dl/>'); link = data.link? tpl.link.replace(/\$1/g, data.link).replace(/\$3/g, fm.i18n('website')) : '$2'; if (data.name) { opt.html(fm.i18n(data.name)); } dsc.children('legend').html(link.replace(/\$2/g, fm.i18n(data.name) || id)); jQuery.each(items, function(i, key) { var t = tpl[key] || tpl.data, elm; if (data[key]) { elm = t.replace(/\$0/g, fm.escape(key)).replace(/\$1/g, fm.i18n(key)).replace(/\$2/g, fm.i18n(data[key])); if (key === 'image' && data.link) { elm = jQuery(elm).on('click', function() { themeSel.val(id).trigger('change'); }).attr('title', fm.i18n('select')); } dl.append(elm); } }); val = val.add(dl); val = val.add(jQuery('<div class="elfinder-preference-theme-btn"/>').append(jQuery('<button class="ui-button ui-corner-all ui-widget"/>').data('themeid', id).html(fm.i18n('select')))); dsc.find('span.elfinder-spinner').replaceWith(val); }).fail(function() { dsc.find('span.elfinder-spinner').replaceWith(fm.i18n(['errRead', id])); }); }); return jQuery('<div/>').append(themeSel.val(fm.theme && fm.theme.id? fm.theme.id : 'default'), defBtn, list); })()); forms.toolbarPref && (forms.toolbarPref = (function() { var pnls = jQuery.map(fm.options.uiOptions.toolbar, function(v) { return jQuery.isArray(v)? v : null; }), tags = [], hides = fm.storage('toolbarhides') || {}; jQuery.each(pnls, function() { var cmd = this, name = fm.i18n('cmd'+cmd); if (name === 'cmd'+cmd) { name = fm.i18n(cmd); } tags.push('<span class="elfinder-preference-toolbar-item"><label><input type="checkbox" value="'+cmd+'" '+(hides[cmd]? '' : 'checked')+'/>'+name+'</label></span>'); }); return jQuery(tags.join(' ')).on('change', 'input', function() { var v = jQuery(this).val(), o = jQuery(this).is(':checked'); if (!o && !hides[v]) { hides[v] = true; } else if (o && hides[v]) { delete hides[v]; } fm.storage('toolbarhides', hides); fm.trigger('toolbarpref'); }); })()); forms.iconSize && (forms.iconSize = (function() { var max = fm.options.uiOptions.cwd.iconsView.sizeMax || 3, size = fm.storage('iconsize') || 0, sld = jQuery('<div class="touch-punch"/>').slider({ classes: { 'ui-slider-handle': 'elfinder-tabstop', }, value: size, max: max, slide: function(e, ui) { fm.getUI('cwd').trigger('iconpref', {size: ui.value}); }, change: function(e, ui) { fm.storage('iconsize', ui.value); } }); fm.getUI('cwd').on('iconpref', function(e, data) { sld.slider('option', 'value', data.size); }); return sld; })()); forms.columnPref && (forms.columnPref = (function() { var cols = fm.options.uiOptions.cwd.listView.columns, tags = [], hides = fm.storage('columnhides') || {}; jQuery.each(cols, function() { var key = this, name = fm.getColumnName(key); tags.push('<span class="elfinder-preference-column-item"><label><input type="checkbox" value="'+key+'" '+(hides[key]? '' : 'checked')+'/>'+name+'</label></span>'); }); return jQuery(tags.join(' ')).on('change', 'input', function() { var v = jQuery(this).val(), o = jQuery(this).is(':checked'); if (!o && !hides[v]) { hides[v] = true; } else if (o && hides[v]) { delete hides[v]; } fm.storage('columnhides', hides); fm.trigger('columnpref', { repaint: true }); }); })()); forms.selectAction && (forms.selectAction = (function() { var actSel = jQuery('<select/>').on('change', function() { var act = jQuery(this).val(); fm.storage('selectAction', act === 'default'? null : act); }), optTags = [], acts = self.options.selectActions, defAct = fm.getCommand('open').options.selectAction || 'open'; if (jQuery.inArray(defAct, acts) === -1) { acts.unshift(defAct); } jQuery.each(acts, function(i, act) { var names = jQuery.map(act.split('/'), function(cmd) { var name = fm.i18n('cmd'+cmd); if (name === 'cmd'+cmd) { name = fm.i18n(cmd); } return name; }); optTags.push('<option value="'+act+'">'+names.join('/')+'</option>'); }); return actSel.append(optTags.join('')).val(fm.storage('selectAction') || defAct); })()); forms.makefileTypes && (forms.makefileTypes = (function() { var hides = fm.storage('mkfileHides') || {}, getTag = function() { var tags = []; // re-assign hides hides = fm.storage('mkfileHides') || {}; jQuery.each(fm.mimesCanMakeEmpty, function(mime, type) { var name = fm.getCommand('mkfile').getTypeName(mime, type); tags.push('<span class="elfinder-preference-column-item" title="'+fm.escape(name)+'"><label><input type="checkbox" value="'+mime+'" '+(hides[mime]? '' : 'checked')+'/>'+type+'</label></span>'); }); return tags.join(' '); }, elm = jQuery('<div/>').on('change', 'input', function() { var v = jQuery(this).val(), o = jQuery(this).is(':checked'); if (!o && !hides[v]) { hides[v] = true; } else if (o && hides[v]) { delete hides[v]; } fm.storage('mkfileHides', hides); fm.trigger('canMakeEmptyFile'); }).append(getTag()), add = jQuery('<div/>').append( jQuery('<input type="text" placeholder="'+fm.i18n('typeOfTextfile')+'"/>').on('keydown', function(e) { (e.keyCode === jQuery.ui.keyCode.ENTER) && jQuery(this).next().trigger('click'); }), jQuery('<button class="ui-button"/>').html(fm.i18n('add')).on('click', function() { var input = jQuery(this).prev(), val = input.val(), uiToast = fm.getUI('toast'), err = function() { uiToast.appendTo(input.closest('.ui-dialog')); fm.toast({ msg: fm.i18n('errUsupportType'), mode: 'warning', onHidden: function() { uiToast.children().length === 1 && uiToast.appendTo(fm.getUI()); } }); input.trigger('focus'); return false; }, tmpMimes; if (!val.match(/\//)) { val = fm.arrayFlip(fm.mimeTypes)[val]; if (!val) { return err(); } input.val(val); } if (!fm.mimeIsText(val) || !fm.mimeTypes[val]) { return err(); } fm.trigger('canMakeEmptyFile', {mimes: [val], unshift: true}); tmpMimes = {}; tmpMimes[val] = fm.mimeTypes[val]; fm.storage('mkfileTextMimes', Object.assign(tmpMimes, fm.storage('mkfileTextMimes') || {})); input.val(''); uiToast.appendTo(input.closest('.ui-dialog')); fm.toast({ msg: fm.i18n(['complete', val + ' (' + tmpMimes[val] + ')']), onHidden: function() { uiToast.children().length === 1 && uiToast.appendTo(fm.getUI()); } }); }), jQuery('<button class="ui-button"/>').html(fm.i18n('reset')).on('click', function() { fm.one('canMakeEmptyFile', {done: function() { elm.empty().append(getTag()); }}); fm.trigger('canMakeEmptyFile', {resetTexts: true}); }) ), tm; fm.bind('canMakeEmptyFile', {done: function(e) { if (e.data && e.data.mimes && e.data.mimes.length) { elm.empty().append(getTag()); } }}); return jQuery('<div/>').append(elm, add); })()); forms.useStoredEditor && (forms.useStoredEditor = jQuery('<input type="checkbox"/>').prop('checked', (function() { var s = fm.storage('useStoredEditor'); return s? (s > 0) : fm.options.commandsOptions.edit.useStoredEditor; })()).on('change', function(e) { fm.storage('useStoredEditor', jQuery(this).is(':checked')? 1 : -1); })); forms.editorMaximized && (forms.editorMaximized = jQuery('<input type="checkbox"/>').prop('checked', (function() { var s = fm.storage('editorMaximized'); return s? (s > 0) : fm.options.commandsOptions.edit.editorMaximized; })()).on('change', function(e) { fm.storage('editorMaximized', jQuery(this).is(':checked')? 1 : -1); })); if (forms.showHidden) { (function() { var setTitle = function() { var s = fm.storage('hide'), t = [], v; if (s && s.items) { jQuery.each(s.items, function(h, n) { t.push(fm.escape(n)); }); } elms.prop('disabled', !t.length)[t.length? 'removeClass' : 'addClass']('ui-state-disabled'); v = t.length? t.join('\n') : ''; forms.showHidden.attr('title',v); useTooltip && forms.showHidden.tooltip('option', 'content', v.replace(/\n/g, '<br>')).tooltip('close'); }, chk = jQuery('<input type="checkbox"/>').prop('checked', (function() { var s = fm.storage('hide'); return s && s.show; })()).on('change', function(e) { var o = {}; o[jQuery(this).is(':checked')? 'show' : 'hide'] = true; fm.exec('hide', void(0), o); }), btn = jQuery('<button class="ui-button ui-corner-all ui-widget"/>').append(fm.i18n('reset')).on('click', function() { fm.exec('hide', void(0), {reset: true}); jQuery(this).parent().find('input:first').prop('checked', false); setTitle(); }), elms = jQuery().add(chk).add(btn), useTooltip; forms.showHidden = jQuery('<div/>').append(chk, btn); fm.bind('hide', function(e) { var d = e.data; if (!d.opts || (!d.opts.show && !d.opts.hide)) { setTitle(); } }); if (fm.UA.Mobile && jQuery.fn.tooltip) { useTooltip = true; forms.showHidden.tooltip({ classes: { 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow' }, tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow', track: true }).css('user-select', 'none'); btn.css('user-select', 'none'); } setTitle(); })(); } forms.infoItems && (forms.infoItems = (function() { var items = fm.getCommand('info').items, tags = [], hides = fm.storage('infohides') || fm.arrayFlip(fm.options.commandsOptions.info.hideItems, true); jQuery.each(items, function() { var key = this, name = fm.i18n(key); tags.push('<span class="elfinder-preference-info-item"><label><input type="checkbox" value="'+key+'" '+(hides[key]? '' : 'checked')+'/>'+name+'</label></span>'); }); return jQuery(tags.join(' ')).on('change', 'input', function() { var v = jQuery(this).val(), o = jQuery(this).is(':checked'); if (!o && !hides[v]) { hides[v] = true; } else if (o && hides[v]) { delete hides[v]; } fm.storage('infohides', hides); fm.trigger('infopref', { repaint: true }); }); })()); forms.hashChecker && fm.hashCheckers.length && (forms.hashChecker = (function() { var tags = [], enabled = fm.arrayFlip(fm.storage('hashchekcer') || fm.options.commandsOptions.info.showHashAlgorisms, true); jQuery.each(fm.hashCheckers, function() { var cmd = this, name = fm.i18n(cmd); tags.push('<span class="elfinder-preference-hashchecker-item"><label><input type="checkbox" value="'+cmd+'" '+(enabled[cmd]? 'checked' : '')+'/>'+name+'</label></span>'); }); return jQuery(tags.join(' ')).on('change', 'input', function() { var v = jQuery(this).val(), o = jQuery(this).is(':checked'); if (o) { enabled[v] = true; } else if (enabled[v]) { delete enabled[v]; } fm.storage('hashchekcer', jQuery.grep(fm.hashCheckers, function(v) { return enabled[v]; })); }); })()); forms.autoFocusDialog && (forms.autoFocusDialog = jQuery('<input type="checkbox"/>').prop('checked', (function() { var s = fm.storage('autoFocusDialog'); return s? (s > 0) : fm.options.uiOptions.dialog.focusOnMouseOver; })()).on('change', function(e) { fm.storage('autoFocusDialog', jQuery(this).is(':checked')? 1 : -1); })); forms.clearBrowserData && (forms.clearBrowserData = jQuery('<button/>').text(fm.i18n('reset')).button().on('click', function(e) { e.preventDefault(); fm.storage(); jQuery('#'+fm.id).elfinder('reload'); })); jQuery.each(cats, function(id, prefs) { var dls, found; if (prefs === true) { found = 1; } else if (prefs) { dls = jQuery(); jQuery.each(prefs, function(i, n) { var f, title, chks = '', cbox; if (f = forms[n]) { found = 2; title = fm.i18n(n); cbox = jQuery(f).filter('input[type="checkbox"]'); if (!cbox.length) { cbox = jQuery(f).find('input[type="checkbox"]'); } if (cbox.length === 1) { if (!cbox.attr('id')) { cbox.attr('id', 'elfinder-preference-'+n+'-checkbox'); } title = '<label for="'+cbox.attr('id')+'">'+title+'</label>'; } else if (cbox.length > 1) { chks = ' elfinder-preference-checkboxes'; } dls = dls.add(jQuery('<dt class="elfinder-preference-'+n+chks+'">'+title+'</dt>')).add(jQuery('<dd class="elfinder-preference-'+n+chks+'"/>').append(f)); } }); } if (found) { ul.append(tab[r](/\{id\}/g, id)[r](/\{title\}/, fm.i18n(id))[r](/\{class\}/, openTab === id? 'elfinder-focus' : '')); if (found === 2) { tabs.append( jQuery('<div id="'+fm.namespace+'-preference-'+id+'" class="elfinder-preference-content"/>') .hide() .append(jQuery('<dl/>').append(dls)) ); } } }); ul.on('click', 'a', function(e) { var t = jQuery(e.target), h = t.attr('href'); e.preventDefault(); e.stopPropagation(); ul.children().removeClass(clTabActive); t.removeClass('ui-state-hover').parent().addClass(clTabActive); if (h.match(/all$/)) { tabs.addClass('elfinder-preference-taball').children().show(); } else { tabs.removeClass('elfinder-preference-taball').children().hide(); jQuery(h).show(); } }).on('focus blur', 'a', function(e) { jQuery(this).parent().toggleClass('ui-state-focus', e.type === 'focusin'); }).on('mouseenter mouseleave', 'li', function(e) { jQuery(this).toggleClass('ui-state-hover', e.type === 'mouseenter'); }); tabs.find('a,input,select,button').addClass('elfinder-tabstop'); base.append(ul, tabs); dialog = self.fmDialog(base, { title : self.title, width : self.options.width || 600, height: self.options.height || 400, maxWidth: 'window', maxHeight: 'window', autoOpen : false, destroyOnClose : false, allowMinimize : false, open : function() { openTab && selectTab(openTab); openTab = null; }, resize : function() { tabs.height(dialog.height() - ul.outerHeight(true) - (tabs.outerHeight(true) - tabs.height()) - 5); } }) .on('click', function(e) { e.stopPropagation(); }) .css({ overflow: 'hidden' }); dialog.closest('.ui-dialog') .css({ overflow: 'hidden' }) .addClass('elfinder-bg-translucent'); openTab = 'all'; }, dialog, openTab; this.shortcuts = [{ pattern : 'ctrl+comma', description : this.title }]; this.alwaysEnabled = true; this.getstate = function() { return 0; }; this.exec = function(sel, cOpts) { !dialog && build(); if (cOpts) { if (cOpts.tab) { selectTab(cOpts.tab); } else if (cOpts._currentType === 'cwd') { selectTab('workspace'); } } dialog.elfinderdialog('open'); return jQuery.Deferred().resolve(); }; }; /* * File: /js/commands/quicklook.js */ /** * @class elFinder command "quicklook" * Fast preview for some files types * * @author Dmitry (dio) Levashov **/ (elFinder.prototype.commands.quicklook = function() { var self = this, fm = self.fm, /** * window closed state * * @type Number **/ closed = 0, /** * window animated state * * @type Number **/ animated = 1, /** * window opened state * * @type Number **/ opened = 2, /** * window docked state * * @type Number **/ docked = 3, /** * window docked and hidden state * * @type Number **/ dockedhidden = 4, /** * window state * * @type Number **/ state = closed, /** * Event name of update * for fix conflicts with Prototype.JS * * `@see https://github.com/Studio-42/elFinder/pull/2346 * @type String **/ evUpdate = Element.update? 'quicklookupdate' : 'update', /** * navbar icon class * * @type String **/ navicon = 'elfinder-quicklook-navbar-icon', /** * navbar "fullscreen" icon class * * @type String **/ fullscreen = 'elfinder-quicklook-fullscreen', /** * info wrapper class * * @type String */ infocls = 'elfinder-quicklook-info-wrapper', /** * Triger keydown/keypress event with left/right arrow key code * * @param Number left/right arrow key code * @return void **/ navtrigger = function(code) { jQuery(document).trigger(jQuery.Event('keydown', { keyCode: code, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false })); }, /** * Return css for closed window * * @param jQuery file node in cwd * @return void **/ closedCss = function(node) { var elf = fm.getUI().offset(), base = (function() { var target = node.find('.elfinder-cwd-file-wrapper'); return target.length? target : node; })(), baseOffset = base.offset() || { top: 0, left: 0 }; return { opacity : 0, width : base.width(), height : base.height() - 30, top : baseOffset.top - elf.top, left : baseOffset.left - elf.left }; }, /** * Return css for opened window * * @return void **/ openedCss = function() { var contain = self.options.contain, win = contain? fm.getUI() : jQuery(window), elf = fm.getUI().offset(), w = Math.min(width, win.width()-10), h = Math.min(height, win.height()-80); return { opacity : 1, width : w, height : h, top : parseInt((win.height() - h - 60) / 2 + (contain? 0 : win.scrollTop() - elf.top)), left : parseInt((win.width() - w) / 2 + (contain? 0 : win.scrollLeft() - elf.left)) }; }, mediaNode = {}, support = function(codec, name) { var node = name || codec.substr(0, codec.indexOf('/')), media = mediaNode[node]? mediaNode[node] : (mediaNode[node] = document.createElement(node)), value = false; try { value = media.canPlayType && media.canPlayType(codec); } catch(e) {} return (value && value !== '' && value != 'no')? true : false; }, platformWin = (window.navigator.platform.indexOf('Win') != -1), /** * Opened window width (from config) * * @type Number **/ width, /** * Opened window height (from config) * * @type Number **/ height, /** * Previous style before docked * * @type String **/ prevStyle, /** * elFinder node * * @type jQuery **/ parent, /** * elFinder current directory node * * @type jQuery **/ cwd, /** * Current directory hash * * @type String **/ cwdHash, dockEnabled = false, navdrag = false, navmove = false, navtm = null, leftKey = jQuery.ui.keyCode.LEFT, rightKey = jQuery.ui.keyCode.RIGHT, coverEv = 'mousemove touchstart ' + ('onwheel' in document? 'wheel' : 'onmousewheel' in document? 'mousewheel' : 'DOMMouseScroll'), title = jQuery('<span class="elfinder-dialog-title elfinder-quicklook-title"/>'), icon = jQuery('<div/>'), info = jQuery('<div class="elfinder-quicklook-info"/>'),//.hide(), cover = jQuery('<div class="ui-front elfinder-quicklook-cover"/>'), fsicon = jQuery('<div class="'+navicon+' '+navicon+'-fullscreen"/>') .on('click touchstart', function(e) { if (navmove) { return; } var win = self.window, full = win.hasClass(fullscreen), $window = jQuery(window), resize = function() { self.preview.trigger('changesize'); }; e.stopPropagation(); e.preventDefault(); if (full) { navStyle = ''; navShow(); win.toggleClass(fullscreen) .css(win.data('position')); $window.trigger(self.resize).off(self.resize, resize); navbar.off('mouseenter mouseleave'); cover.off(coverEv); } else { win.toggleClass(fullscreen) .data('position', { left : win.css('left'), top : win.css('top'), width : win.width(), height : win.height(), display: 'block' }) .removeAttr('style'); jQuery(window).on(self.resize, resize) .trigger(self.resize); cover.on(coverEv, function(e) { if (! navdrag) { if (e.type === 'mousemove' || e.type === 'touchstart') { navShow(); navtm = setTimeout(function() { if (fm.UA.Mobile || navbar.parent().find('.elfinder-quicklook-navbar:hover').length < 1) { navbar.fadeOut('slow', function() { cover.show(); }); } }, 3000); } if (cover.is(':visible')) { coverHide(); cover.data('tm', setTimeout(function() { cover.show(); }, 3000)); } } }).show().trigger('mousemove'); navbar.on('mouseenter mouseleave', function(e) { if (! navdrag) { if (e.type === 'mouseenter') { navShow(); } else { cover.trigger('mousemove'); } } }); } if (fm.zIndex) { win.css('z-index', fm.zIndex + 1); } if (fm.UA.Mobile) { navbar.attr('style', navStyle); } else { navbar.attr('style', navStyle).draggable(full ? 'destroy' : { start: function() { navdrag = true; navmove = true; cover.show(); navShow(); }, stop: function() { navdrag = false; navStyle = self.navbar.attr('style'); requestAnimationFrame(function() { navmove = false; }); } }); } jQuery(this).toggleClass(navicon+'-fullscreen-off'); var collection = win; if (parent.is('.ui-resizable')) { collection = collection.add(parent); } collection.resizable(full ? 'enable' : 'disable').removeClass('ui-state-disabled'); win.trigger('viewchange'); } ), updateOnSel = function() { self.update(void(0), (function() { var fm = self.fm, files = fm.selectedFiles(), cnt = files.length, inDock = self.docked(), getInfo = function() { var ts = 0; jQuery.each(files, function(i, f) { var t = parseInt(f.ts); if (ts >= 0) { if (t > ts) { ts = t; } } else { ts = 'unknown'; } }); return { hash : files[0].hash + '/' + (+new Date()), name : fm.i18n('items') + ': ' + cnt, mime : 'group', size : spinner, ts : ts, files : jQuery.map(files, function(f) { return f.hash; }), getSize : true }; }; if (! cnt) { cnt = 1; files = [fm.cwd()]; } return (cnt === 1)? files[0] : getInfo(); })()); }, navShow = function() { if (self.window.hasClass(fullscreen)) { navtm && clearTimeout(navtm); navtm = null; // if use `show()` it make infinite loop with old jQuery (jQuery/jQuery UI: 1.8.0/1.9.0) // see #1478 https://github.com/Studio-42/elFinder/issues/1478 navbar.stop(true, true).css('display', 'block'); coverHide(); } }, coverHide = function() { cover.data('tm') && clearTimeout(cover.data('tm')); cover.removeData('tm'); cover.hide(); }, prev = jQuery('<div class="'+navicon+' '+navicon+'-prev"/>').on('click touchstart', function(e) { ! navmove && navtrigger(leftKey); return false; }), next = jQuery('<div class="'+navicon+' '+navicon+'-next"/>').on('click touchstart', function(e) { ! navmove && navtrigger(rightKey); return false; }), navbar = jQuery('<div class="elfinder-quicklook-navbar"/>') .append(prev) .append(fsicon) .append(next) .append('<div class="elfinder-quicklook-navbar-separator"/>') .append(jQuery('<div class="'+navicon+' '+navicon+'-close"/>').on('click touchstart', function(e) { ! navmove && self.window.trigger('close'); return false; })) , titleClose = jQuery('<span class="ui-front ui-icon elfinder-icon-close ui-icon-closethick"/>').on('mousedown', function(e) { e.stopPropagation(); self.window.trigger('close'); }), titleDock = jQuery('<span class="ui-front ui-icon elfinder-icon-minimize ui-icon-minusthick"/>').on('mousedown', function(e) { e.stopPropagation(); if (! self.docked()) { self.window.trigger('navdockin'); } else { self.window.trigger('navdockout'); } }), spinner = '<span class="elfinder-spinner-text">' + fm.i18n('calc') + '</span>' + '<span class="elfinder-spinner"/>', navStyle = '', init = true, dockHeight, getSize, tm4cwd, dockedNode, selectTm; this.cover = cover; this.evUpdate = evUpdate; (this.navbar = navbar)._show = navShow; this.resize = 'resize.'+fm.namespace; this.info = jQuery('<div/>').addClass(infocls) .append(icon) .append(info); this.autoPlay = function() { if (self.opened()) { return !! self.options[self.docked()? 'dockAutoplay' : 'autoplay']; } return false; }; this.preview = jQuery('<div class="elfinder-quicklook-preview ui-helper-clearfix"/>') // clean info/icon .on('change', function() { navShow(); navbar.attr('style', navStyle); self.docked() && navbar.hide(); self.preview.attr('style', '').removeClass('elfinder-overflow-auto'); self.info.attr('style', '').hide(); self.cover.removeClass('elfinder-quicklook-coverbg'); icon.removeAttr('class').attr('style', ''); info.html(''); }) // update info/icon .on(evUpdate, function(e) { var preview = self.preview, file = e.file, tpl = '<div class="elfinder-quicklook-info-data">{value}</div>', update = function() { var win = self.window.css('overflow', 'hidden'); name = fm.escape(file.i18 || file.name); !file.read && e.stopImmediatePropagation(); self.window.data('hash', file.hash); self.preview.off('changesize').trigger('change').children().remove(); title.html(name); prev.css('visibility', ''); next.css('visibility', ''); if (file.hash === fm.cwdId2Hash(cwd.find('[id]:not(.elfinder-cwd-parent):first').attr('id'))) { prev.css('visibility', 'hidden'); } if (file.hash === fm.cwdId2Hash(cwd.find('[id]:last').attr('id'))) { next.css('visibility', 'hidden'); } if (file.mime === 'directory') { getSizeHashes = [ file.hash ]; } else if (file.mime === 'group' && file.getSize) { getSizeHashes = file.files; } info.html( tpl.replace(/\{value\}/, name) + tpl.replace(/\{value\}/, fm.mime2kind(file)) + tpl.replace(/\{value\}/, getSizeHashes.length ? spinner : fm.formatSize(file.size)) + tpl.replace(/\{value\}/, fm.i18n('modify')+': '+ fm.formatDate(file)) ); if (getSizeHashes.length) { getSize = fm.getSize(getSizeHashes).done(function(data) { info.find('span.elfinder-spinner').parent().html(data.formated); }).fail(function() { info.find('span.elfinder-spinner').parent().html(fm.i18n('unknown')); }).always(function() { getSize = null; }); getSize._hash = file.hash; } icon.addClass('elfinder-cwd-icon ui-corner-all '+fm.mime2class(file.mime)); if (file.icon) { icon.css(fm.getIconStyle(file, true)); } self.info.attr('class', infocls); if (file.csscls) { self.info.addClass(file.csscls); } if (file.read && (tmb = fm.tmb(file))) { jQuery('<img/>') .hide() .appendTo(self.preview) .on('load', function() { icon.addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); jQuery(this).remove(); }) .attr('src', tmb.url); } self.info.delay(100).fadeIn(10); if (self.window.hasClass(fullscreen)) { cover.trigger('mousemove'); } win.css('overflow', ''); }, tmb, name, getSizeHashes = []; if (file && ! Object.keys(file).length) { file = fm.cwd(); } if (file && getSize && getSize.state() === 'pending' && getSize._hash !== file.hash) { getSize.reject(); } if (file && (e.forceUpdate || self.window.data('hash') !== file.hash)) { update(); } else { e.stopImmediatePropagation(); } }); this.window = jQuery('<div class="ui-front ui-helper-reset ui-widget elfinder-quicklook touch-punch" style="position:absolute"/>') .hide() .addClass(fm.UA.Touch? 'elfinder-touch' : '') .on('click', function(e) { var win = this; e.stopPropagation(); if (state === opened) { requestAnimationFrame(function() { state === opened && fm.toFront(win); }); } }) .append( jQuery('<div class="ui-dialog-titlebar ui-widget-header ui-corner-top ui-helper-clearfix elfinder-quicklook-titlebar"/>') .append( jQuery('<span class="ui-widget-header ui-dialog-titlebar-close ui-corner-all elfinder-titlebar-button elfinder-quicklook-titlebar-icon'+(platformWin? ' elfinder-titlebar-button-right' : '')+'"/>').append( titleClose, titleDock ), title ), this.preview, self.info.hide(), cover.hide(), navbar ) .draggable({handle : 'div.elfinder-quicklook-titlebar'}) .on('open', function(e, clcss) { var win = self.window, file = self.value, node = fm.getUI('cwd'), open = function(status) { state = status; self.update(1, self.value); self.change(); win.trigger('resize.' + fm.namespace); }; if (!init && state === closed) { if (file && file.hash !== cwdHash) { node = fm.cwdHash2Elm(file.hash.split('/', 2)[0]); } navStyle = ''; navbar.attr('style', ''); state = animated; node.trigger('scrolltoview'); coverHide(); win.css(clcss || closedCss(node)) .show() .animate(openedCss(), 550, function() { open(opened); navShow(); }); fm.toFront(win); } else if (state === dockedhidden) { fm.getUI('navdock').data('addNode')(dockedNode); open(docked); self.preview.trigger('changesize'); fm.storage('previewDocked', '1'); if (fm.getUI('navdock').width() === 0) { win.trigger('navdockout'); } } }) .on('close', function(e, dfd) { var win = self.window, preview = self.preview.trigger('change'), file = self.value, hash = (win.data('hash') || '').split('/', 2)[0], close = function(status, winhide) { state = status; winhide && fm.toHide(win); preview.children().remove(); self.update(0, self.value); win.data('hash', ''); dfd && dfd.resolve(); }, node; if (self.opened()) { getSize && getSize.state() === 'pending' && getSize.reject(); if (! self.docked()) { state = animated; win.hasClass(fullscreen) && fsicon.click(); (hash && (node = cwd.find('#'+hash)).length) ? win.animate(closedCss(node), 500, function() { close(closed, true); }) : close(closed, true); } else { dockedNode = fm.getUI('navdock').data('removeNode')(self.window.attr('id'), 'detach'); close(dockedhidden); fm.storage('previewDocked', '2'); } } }) .on('navdockin', function(e, data) { var w = self.window, box = fm.getUI('navdock'), height = dockHeight || box.width(), opts = data || {}; if (init) { opts.init = true; } state = docked; prevStyle = w.attr('style'); w.toggleClass('ui-front').removeClass('ui-widget').draggable('disable').resizable('disable').removeAttr('style').css({ width: '100%', height: height, boxSizing: 'border-box', paddingBottom: 0, zIndex: 'unset' }); navbar.hide(); titleDock.toggleClass('ui-icon-plusthick ui-icon-minusthick elfinder-icon-full elfinder-icon-minimize'); fm.toHide(w, true); box.data('addNode')(w, opts); self.preview.trigger('changesize'); fm.storage('previewDocked', '1'); }) .on('navdockout', function(e) { var w = self.window, box = fm.getUI('navdock'), dfd = jQuery.Deferred(), clcss = closedCss(self.preview); dockHeight = w.outerHeight(); box.data('removeNode')(w.attr('id'), fm.getUI()); w.toggleClass('ui-front').addClass('ui-widget').draggable('enable').resizable('enable').attr('style', prevStyle); titleDock.toggleClass('ui-icon-plusthick ui-icon-minusthick elfinder-icon-full elfinder-icon-minimize'); state = closed; w.trigger('open', clcss); fm.storage('previewDocked', '0'); }) .on('resize.' + fm.namespace, function() { self.preview.trigger('changesize'); }); /** * This command cannot be disable by backend * * @type Boolean **/ this.alwaysEnabled = true; /** * Selected file * * @type Object **/ this.value = null; this.handlers = { // save selected file select : function(e, d) { selectTm && cancelAnimationFrame(selectTm); if (! e.data || ! e.data.selected || ! e.data.selected.length) { selectTm = requestAnimationFrame(function() { self.opened() && updateOnSel(); }); } else { self.opened() && updateOnSel(); } }, error : function() { self.window.is(':visible') && self.window.trigger('close'); }, 'searchshow searchhide' : function() { this.opened() && this.window.trigger('close'); }, navbarshow : function() { requestAnimationFrame(function() { self.docked() && self.preview.trigger('changesize'); }); }, destroy : function() { self.window.remove(); } }; this.shortcuts = [{ pattern : 'space' }]; this.support = { audio : { ogg : support('audio/ogg;'), webm: support('audio/webm;'), mp3 : support('audio/mpeg;'), wav : support('audio/wav;'), m4a : support('audio/mp4;') || support('audio/x-m4a;') || support('audio/aac;'), flac: support('audio/flac;'), amr : support('audio/amr;') }, video : { ogg : support('video/ogg;'), webm : support('video/webm;'), mp4 : support('video/mp4;'), mkv : support('video/x-matroska;') || support('video/webm;'), '3gp': support('video/3gpp;') || support('video/mp4;'), // try as mp4 m3u8 : support('application/x-mpegURL', 'video') || support('application/vnd.apple.mpegURL', 'video'), mpd : support('application/dash+xml', 'video') } }; // for GC mediaNode = {}; /** * Return true if quickLoock window is hiddenReturn true if quickLoock window is visible and not animated * * @return Boolean **/ this.closed = function() { return (state == closed || state == dockedhidden); }; /** * Return true if quickLoock window is visible and not animated * * @return Boolean **/ this.opened = function() { return state == opened || state == docked; }; /** * Return true if quickLoock window is in NavDock * * @return Boolean **/ this.docked = function() { return state == docked; }; /** * Adds an integration into help dialog. * * @param Object opts options */ this.addIntegration = function(opts) { requestAnimationFrame(function() { fm.trigger('helpIntegration', Object.assign({cmd: 'quicklook'}, opts)); }); }; /** * Init command. * Add default plugins and init other plugins * * @return Object **/ this.init = function() { var o = this.options, win = this.window, preview = this.preview, i, p, cwdDispInlineRegex; width = o.width > 0 ? parseInt(o.width) : 450; height = o.height > 0 ? parseInt(o.height) : 300; if (o.dockHeight !== 'auto') { dockHeight = parseInt(o.dockHeight); if (! dockHeight) { dockHeight = void(0); } } fm.one('load', function() { dockEnabled = fm.getUI('navdock').data('dockEnabled'); ! dockEnabled && titleDock.hide(); parent = fm.getUI(); cwd = fm.getUI('cwd'); if (fm.zIndex) { win.css('z-index', fm.zIndex + 1); } win.appendTo(parent); // close window on escape jQuery(document).on('keydown.'+fm.namespace, function(e) { e.keyCode == jQuery.ui.keyCode.ESCAPE && self.opened() && ! self.docked() && win.hasClass('elfinder-frontmost') && win.trigger('close'); }); win.resizable({ handles : 'se', minWidth : 350, minHeight : 120, resize : function() { // use another event to avoid recursion in fullscreen mode // may be there is clever solution, but i cant find it :( preview.trigger('changesize'); } }); self.change(function() { if (self.opened()) { if (self.value) { if (self.value.tmb && self.value.tmb == 1) { // try re-get file object self.value = Object.assign({}, fm.file(self.value.hash)); } preview.trigger(jQuery.Event(evUpdate, {file : self.value})); } } }); preview.on(evUpdate, function(e) { var file, hash, serach; if (file = e.file) { hash = file.hash; serach = (fm.searchStatus.mixed && fm.searchStatus.state > 1); if (file.mime !== 'directory') { if (parseInt(file.size) || file.mime.match(o.mimeRegexNotEmptyCheck)) { // set current dispInlineRegex self.dispInlineRegex = cwdDispInlineRegex; if (serach || fm.optionsByHashes[hash]) { try { self.dispInlineRegex = new RegExp(fm.option('dispInlineRegex', hash), 'i'); } catch(e) { try { self.dispInlineRegex = new RegExp(!fm.isRoot(file)? fm.option('dispInlineRegex', file.phash) : fm.options.dispInlineRegex, 'i'); } catch(e) { self.dispInlineRegex = /^$/; } } } } else { // do not preview of file that size = 0 e.stopImmediatePropagation(); } } else { self.dispInlineRegex = /^$/; } self.info.show(); } else { e.stopImmediatePropagation(); } }); jQuery.each(fm.commands.quicklook.plugins || [], function(i, plugin) { if (typeof(plugin) == 'function') { new plugin(self); } }); }).one('open', function() { var dock = Number(fm.storage('previewDocked') || o.docked), win; if (dockEnabled && dock >= 1) { win = self.window; self.exec(); win.trigger('navdockin', { init : true }); if (dock === 2) { win.trigger('close'); } else { self.update(void(0), fm.cwd()); self.change(); } } init = false; }).bind('open', function() { cwdHash = fm.cwd().hash; self.value = fm.cwd(); // set current volume dispInlineRegex try { cwdDispInlineRegex = new RegExp(fm.option('dispInlineRegex'), 'i'); } catch(e) { cwdDispInlineRegex = /^$/; } }).bind('change', function(e) { if (e.data && e.data.changed && self.opened()) { jQuery.each(e.data.changed, function() { if (self.window.data('hash') === this.hash) { self.window.data('hash', null); self.preview.trigger(evUpdate); return false; } }); } }).bind('navdockresizestart navdockresizestop', function(e) { cover[e.type === 'navdockresizestart'? 'show' : 'hide'](); }); }; this.getstate = function() { return self.opened()? 1 : 0; }; this.exec = function() { self.closed() && updateOnSel(); self.enabled() && self.window.trigger(self.opened() ? 'close' : 'open'); return jQuery.Deferred().resolve(); }; this.hideinfo = function() { this.info.stop(true, true).hide(); }; }).prototype = { forceLoad : true }; // this is required command /* * File: /js/commands/quicklook.plugins.js */ elFinder.prototype.commands.quicklook.plugins = [ /** * Images preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var mimes = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/x-ms-bmp'], preview = ql.preview, WebP, flipMime; // webp support WebP = new Image(); WebP.onload = WebP.onerror = function() { if (WebP.height == 2) { mimes.push('image/webp'); } }; WebP.src='data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA'; // what kind of images we can display jQuery.each(navigator.mimeTypes, function(i, o) { var mime = o.type; if (mime.indexOf('image/') === 0 && jQuery.inArray(mime, mimes)) { mimes.push(mime); } }); preview.on(ql.evUpdate, function(e) { var fm = ql.fm, file = e.file, showed = false, dimreq = null, setdim = function(dim) { var rfile = fm.file(file.hash); rfile.width = dim[0]; rfile.height = dim[1]; }, show = function() { var elm, varelm, memSize, width, height, prop; dimreq && dimreq.state && dimreq.state() === 'pending' && dimreq.reject(); if (showed) { return; } showed = true; elm = img.get(0); memSize = file.width && file.height? {w: file.width, h: file.height} : (elm.naturalWidth? null : {w: img.width(), h: img.height()}); memSize && img.removeAttr('width').removeAttr('height'); width = file.width || elm.naturalWidth || elm.width || img.width(); height = file.height || elm.naturalHeight || elm.height || img.height(); if (!file.width || !file.height) { setdim([width, height]); } memSize && img.width(memSize.w).height(memSize.h); prop = (width/height).toFixed(2); preview.on('changesize', function() { var pw = parseInt(preview.width()), ph = parseInt(preview.height()), w, h; if (prop < (pw/ph).toFixed(2)) { h = ph; w = Math.floor(h * prop); } else { w = pw; h = Math.floor(w/prop); } img.width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0); }) .trigger('changesize'); //show image img.fadeIn(100); }, hideInfo = function() { loading.remove(); // hide info/icon ql.hideinfo(); }, url, img, loading, m; if (!flipMime) { flipMime = fm.arrayFlip(mimes); } if (flipMime[file.mime] && ql.dispInlineRegex.test(file.mime)) { // this is our file - stop event propagation e.stopImmediatePropagation(); loading = jQuery('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info')); url = fm.openUrl(file.hash); img = jQuery('<img/>') .hide() .appendTo(preview) .on('load', function() { hideInfo(); show(); }) .on('error', function() { loading.remove(); }) .attr('src', url); if (file.width && file.height) { show(); } else if (file.size > (ql.options.getDimThreshold || 0)) { dimreq = fm.request({ data : {cmd : 'dim', target : file.hash}, preventDefault : true }) .done(function(data) { if (data.dim) { var dim = data.dim.split('x'); file.width = dim[0]; file.height = dim[1]; setdim(dim); show(); } }); } } }); }, /** * PSD(Adobe Photoshop data) preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, mimes = fm.arrayFlip(['image/vnd.adobe.photoshop', 'image/x-photoshop']), preview = ql.preview, load = function(url, img, loading) { try { fm.replaceXhrSend(); PSD.fromURL(url).then(function(psd) { var prop; img.attr('src', psd.image.toBase64()); requestAnimationFrame(function() { prop = (img.width()/img.height()).toFixed(2); preview.on('changesize', function() { var pw = parseInt(preview.width()), ph = parseInt(preview.height()), w, h; if (prop < (pw/ph).toFixed(2)) { h = ph; w = Math.floor(h * prop); } else { w = pw; h = Math.floor(w/prop); } img.width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0); }).trigger('changesize'); loading.remove(); // hide info/icon ql.hideinfo(); //show image img.fadeIn(100); }); }, function() { loading.remove(); img.remove(); }); fm.restoreXhrSend(); } catch(e) { fm.restoreXhrSend(); loading.remove(); img.remove(); } }, PSD; preview.on(ql.evUpdate, function(e) { var file = e.file, url, img, loading, m, _define, _require; if (mimes[file.mime] && fm.options.cdns.psd && ! fm.UA.ltIE10 && ql.dispInlineRegex.test(file.mime)) { // this is our file - stop event propagation e.stopImmediatePropagation(); loading = jQuery('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info')); url = fm.openUrl(file.hash); if (!fm.isSameOrigin(url)) { url = fm.openUrl(file.hash, true); } img = jQuery('<img/>').hide().appendTo(preview); if (PSD) { load(url, img, loading); } else { _define = window.define; _require = window.require; window.require = null; window.define = null; fm.loadScript( [ fm.options.cdns.psd ], function() { PSD = require('psd'); _define? (window.define = _define) : (delete window.define); _require? (window.require = _require) : (delete window.require); load(url, img, loading); } ); } } }); }, /** * HTML preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, mimes = fm.arrayFlip(['text/html', 'application/xhtml+xml']), preview = ql.preview; preview.on(ql.evUpdate, function(e) { var file = e.file, jqxhr, loading; if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) { e.stopImmediatePropagation(); loading = jQuery('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info')); // stop loading on change file if not loaded yet preview.one('change', function() { jqxhr.state() == 'pending' && jqxhr.reject(); }).addClass('elfinder-overflow-auto'); jqxhr = fm.request({ data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts}, options : {type: 'get', cache : true}, preventDefault : true }) .done(function(data) { ql.hideinfo(); var doc = jQuery('<iframe class="elfinder-quicklook-preview-html"/>').appendTo(preview)[0].contentWindow.document; doc.open(); doc.write(data.content); doc.close(); }) .always(function() { loading.remove(); }); } }); }, /** * MarkDown preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, mimes = fm.arrayFlip(['text/x-markdown']), preview = ql.preview, marked = null, show = function(data, loading) { ql.hideinfo(); var doc = jQuery('<iframe class="elfinder-quicklook-preview-html"/>').appendTo(preview)[0].contentWindow.document; doc.open(); doc.write(marked(data.content)); doc.close(); loading.remove(); }, error = function(loading) { marked = false; loading.remove(); }; preview.on(ql.evUpdate, function(e) { var file = e.file, jqxhr, loading; if (mimes[file.mime] && fm.options.cdns.marked && marked !== false && ql.dispInlineRegex.test(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) { e.stopImmediatePropagation(); loading = jQuery('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info')); // stop loading on change file if not loaded yet preview.one('change', function() { jqxhr.state() == 'pending' && jqxhr.reject(); }).addClass('elfinder-overflow-auto'); jqxhr = fm.request({ data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts}, options : {type: 'get', cache : true}, preventDefault : true }) .done(function(data) { if (marked || window.marked) { if (!marked) { marked = window.marked; } show(data, loading); } else { fm.loadScript([fm.options.cdns.marked], function(res) { marked = res || window.marked || false; delete window.marked; if (marked) { show(data, loading); } else { error(loading); } }, { tryRequire: true, error: function() { error(loading); } } ); } }) .fail(function() { error(loading); }); } }); }, /** * PDF/ODT/ODS/ODP preview with ViewerJS * * @param elFinder.commands.quicklook */ function(ql) { if (ql.options.viewerjs) { var fm = ql.fm, preview = ql.preview, opts = ql.options.viewerjs, mimes = opts.url? fm.arrayFlip(opts.mimes || []) : []; if (opts.url) { preview.on('update', function(e) { var win = ql.window, file = e.file, node, loading; if (mimes[file.mime]) { var url = fm.openUrl(file.hash); if (url && fm.isSameOrigin(url)) { e.stopImmediatePropagation(); loading = jQuery('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info')); node = jQuery('<iframe class="elfinder-quicklook-preview-iframe"/>') .css('background-color', 'transparent') .on('load', function() { ql.hideinfo(); loading.remove(); node.css('background-color', '#fff'); }) .on('error', function() { loading.remove(); node.remove(); }) .appendTo(preview) .attr('src', opts.url + '#' + url); preview.one('change', function() { loading.remove(); node.off('load').remove(); }); } } }); } } }, /** * PDF preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, mime = 'application/pdf', preview = ql.preview, active = false, urlhash = '', firefox, toolbar; if ((fm.UA.Safari && fm.OS === 'mac' && !fm.UA.iOS) || fm.UA.IE || fm.UA.Firefox) { active = true; } else { jQuery.each(navigator.plugins, function(i, plugins) { jQuery.each(plugins, function(i, plugin) { if (plugin.type === mime) { return !(active = true); } }); }); } if (active) { if (typeof ql.options.pdfToolbar !== 'undefined' && !ql.options.pdfToolbar) { urlhash = '#toolbar=0'; } preview.on(ql.evUpdate, function(e) { var file = e.file; if (active && file.mime === mime && ql.dispInlineRegex.test(file.mime)) { e.stopImmediatePropagation(); ql.hideinfo(); ql.cover.addClass('elfinder-quicklook-coverbg'); jQuery('<object class="elfinder-quicklook-preview-pdf" data="'+fm.openUrl(file.hash)+urlhash+'" type="application/pdf" />') .on('error', function(e) { active = false; ql.update(void(0), fm.cwd()); ql.update(void(0), file); }) .appendTo(preview); } }); } }, /** * Flash preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, mime = 'application/x-shockwave-flash', preview = ql.preview, active = false; jQuery.each(navigator.plugins, function(i, plugins) { jQuery.each(plugins, function(i, plugin) { if (plugin.type === mime) { return !(active = true); } }); }); active && preview.on(ql.evUpdate, function(e) { var file = e.file, node; if (file.mime === mime && ql.dispInlineRegex.test(file.mime)) { e.stopImmediatePropagation(); ql.hideinfo(); node = jQuery('<embed class="elfinder-quicklook-preview-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" src="'+fm.openUrl(file.hash)+'" quality="high" type="application/x-shockwave-flash" wmode="transparent" />') .appendTo(preview); } }); }, /** * HTML5 audio preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, preview = ql.preview, mimes = { 'audio/mpeg' : 'mp3', 'audio/mpeg3' : 'mp3', 'audio/mp3' : 'mp3', 'audio/x-mpeg3' : 'mp3', 'audio/x-mp3' : 'mp3', 'audio/x-wav' : 'wav', 'audio/wav' : 'wav', 'audio/x-m4a' : 'm4a', 'audio/aac' : 'm4a', 'audio/mp4' : 'm4a', 'audio/x-mp4' : 'm4a', 'audio/ogg' : 'ogg', 'audio/webm' : 'webm', 'audio/flac' : 'flac', 'audio/x-flac' : 'flac', 'audio/amr' : 'amr' }, node, curHash, win = ql.window, navi = ql.navbar, AMR, autoplay, controlsList = typeof ql.options.mediaControlsList === 'string' && ql.options.mediaControlsList? ' controlsList="' + fm.escape(ql.options.mediaControlsList) + '"' : '', setNavi = function() { navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : ''); }, getNode = function(src, hash) { return jQuery('<audio class="elfinder-quicklook-preview-audio ui-front" controls' + controlsList + ' preload="auto" autobuffer><source src="'+src+'" /></audio>') .on('change', function(e) { // Firefox fire change event on seek or volume change e.stopPropagation(); }) .on('error', function(e) { node && node.data('hash') === hash && reset(); }) .data('hash', hash) .appendTo(preview); }, amrToWavUrl = function(hash) { var dfd = jQuery.Deferred(), loader = jQuery.Deferred().done(function() { fm.getContents(hash).done(function(data) { try { var buffer = AMR.toWAV(new Uint8Array(data)); if (buffer) { dfd.resolve(URL.createObjectURL(new Blob([buffer], { type: 'audio/x-wav' }))); } else { dfd.reject(); } } catch(e) { dfd.reject(); } }).fail(function() { dfd.reject(); }); }).fail(function() { AMR = false; dfd.reject(); }), _AMR; if (window.TextEncoder && window.URL && URL.createObjectURL && typeof AMR === 'undefined') { // previous window.AMR _AMR = window.AMR; delete window.AMR; fm.loadScript( [ fm.options.cdns.amr ], function() { AMR = window.AMR? window.AMR : false; // restore previous window.AMR window.AMR = _AMR; loader[AMR? 'resolve':'reject'](); }, { error: function() { loader.reject(); } } ); } else { loader[AMR? 'resolve':'reject'](); } return dfd; }, play = function(player) { var hash = node.data('hash'), playPromise; autoplay && (playPromise = player.play()); // uses "playPromise['catch']" instead "playPromise.catch" to support Old IE if (playPromise && playPromise['catch']) { playPromise['catch'](function(e) { if (!player.paused) { node && node.data('hash') === hash && reset(); } }); } }, reset = function() { if (node && node.parent().length) { var elm = node[0], url = node.children('source').attr('src'); win.off('viewchange.audio'); try { elm.pause(); node.empty(); if (url.match(/^blob:/)) { URL.revokeObjectURL(url); } elm.src = ''; elm.load(); } catch(e) {} node.remove(); node = null; } }; preview.on(ql.evUpdate, function(e) { var file = e.file, type = mimes[file.mime], html5, srcUrl; if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime) && ((html5 = ql.support.audio[type]) || (type === 'amr'))) { autoplay = ql.autoPlay(); curHash = file.hash; srcUrl = html5? fm.openUrl(curHash) : ''; if (!html5) { if (fm.options.cdns.amr && type === 'amr' && AMR !== false) { e.stopImmediatePropagation(); node = getNode(srcUrl, curHash); amrToWavUrl(file.hash).done(function(url) { if (curHash === file.hash) { var elm = node[0]; try { node.children('source').attr('src', url); elm.pause(); elm.load(); play(elm); win.on('viewchange.audio', setNavi); setNavi(); } catch(e) { URL.revokeObjectURL(url); node.remove(); } } else { URL.revokeObjectURL(url); } }).fail(function() { node.remove(); }); } } else { e.stopImmediatePropagation(); node = getNode(srcUrl, curHash); play(node[0]); win.on('viewchange.audio', setNavi); setNavi(); } } }).on('change', reset); }, /** * HTML5 video preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, preview = ql.preview, mimes = { 'video/mp4' : 'mp4', 'video/x-m4v' : 'mp4', 'video/quicktime' : 'mp4', 'video/ogg' : 'ogg', 'application/ogg' : 'ogg', 'video/webm' : 'webm', 'video/x-matroska': 'mkv', 'video/3gpp' : '3gp', 'application/vnd.apple.mpegurl' : 'm3u8', 'application/x-mpegurl' : 'm3u8', 'application/dash+xml' : 'mpd', 'video/x-flv' : 'flv' }, node, win = ql.window, navi = ql.navbar, cHls, cDash, pDash, cFlv, autoplay, tm, controlsList = typeof ql.options.mediaControlsList === 'string' && ql.options.mediaControlsList? ' controlsList="' + fm.escape(ql.options.mediaControlsList) + '"' : '', setNavi = function() { if (fm.UA.iOS) { if (win.hasClass('elfinder-quicklook-fullscreen')) { preview.css('height', '-webkit-calc(100% - 50px)'); navi._show(); } else { preview.css('height', ''); } } else { navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : ''); } }, render = function(file, opts) { var errTm = function(e) { if (err > 1) { tm && clearTimeout(tm); tm = setTimeout(function() { !canPlay && reset(true); }, 800); } }, err = 0, canPlay; //reset(); pDash = null; opts = opts || {}; ql.hideinfo(); node = jQuery('<video class="elfinder-quicklook-preview-video" controls' + controlsList + ' preload="auto" autobuffer playsinline>' +'</video>') .on('change', function(e) { // Firefox fire change event on seek or volume change e.stopPropagation(); }) .on('timeupdate progress', errTm) .on('canplay', function() { canPlay = true; }) .data('hash', file.hash); // can not handling error event with jQuery `on` event handler node[0].addEventListener('error', function(e) { if (opts.src && fm.convAbsUrl(opts.src) === fm.convAbsUrl(e.target.src)) { ++err; errTm(); } }, true); if (opts.src) { node.append('<source src="'+opts.src+'" type="'+file.mime+'"/><source src="'+opts.src+'"/>'); } node.appendTo(preview); win.on('viewchange.video', setNavi); setNavi(); }, loadHls = function(file) { var hls; render(file); hls = new cHls(); hls.loadSource(fm.openUrl(file.hash)); hls.attachMedia(node[0]); if (autoplay) { hls.on(cHls.Events.MANIFEST_PARSED, function() { play(node[0]); }); } }, loadDash = function(file) { render(file); pDash = window.dashjs.MediaPlayer().create(); pDash.getDebug().setLogToBrowserConsole(false); pDash.initialize(node[0], fm.openUrl(file.hash), autoplay); pDash.on('error', function(e) { reset(true); }); }, loadFlv = function(file) { if (!cFlv.isSupported()) { cFlv = false; return; } var player = cFlv.createPlayer({ type: 'flv', url: fm.openUrl(file.hash) }); render(file); player.on(cFlv.Events.ERROR, function() { player.destroy(); reset(true); }); player.attachMediaElement(node[0]); player.load(); play(player); }, play = function(player) { var hash = node.data('hash'), playPromise; autoplay && (playPromise = player.play()); // uses "playPromise['catch']" instead "playPromise.catch" to support Old IE if (playPromise && playPromise['catch']) { playPromise['catch'](function(e) { if (!player.paused) { node && node.data('hash') === hash && reset(true); } }); } }, reset = function(showInfo) { tm && clearTimeout(tm); if (node && node.parent().length) { var elm = node[0]; win.off('viewchange.video'); pDash && pDash.reset(); try { elm.pause(); node.empty(); elm.src = ''; elm.load(); } catch(e) {} node.remove(); node = null; } showInfo && ql.info.show(); }; preview.on(ql.evUpdate, function(e) { var file = e.file, mime = file.mime.toLowerCase(), type = mimes[mime], stock, playPromise; if (mimes[mime] && ql.dispInlineRegex.test(file.mime) && (((type === 'm3u8' || (type === 'mpd' && !fm.UA.iOS) || type === 'flv') && !fm.UA.ltIE10) || ql.support.video[type])) { autoplay = ql.autoPlay(); if (ql.support.video[type] && (type !== 'm3u8' || fm.UA.Safari)) { e.stopImmediatePropagation(); render(file, { src: fm.openUrl(file.hash) }); play(node[0]); } else { if (cHls !== false && fm.options.cdns.hls && type === 'm3u8') { e.stopImmediatePropagation(); if (cHls) { loadHls(file); } else { stock = window.Hls; delete window.Hls; fm.loadScript( [ fm.options.cdns.hls ], function(res) { cHls = res || window.Hls || false; window.Hls = stock; cHls && loadHls(file); }, { tryRequire: true, error : function() { cHls = false; } } ); } } else if (cDash !== false && fm.options.cdns.dash && type === 'mpd') { e.stopImmediatePropagation(); if (cDash) { loadDash(file); } else { fm.loadScript( [ fm.options.cdns.dash ], function() { // dashjs require window.dashjs in global scope cDash = window.dashjs? true : false; cDash && loadDash(file); }, { tryRequire: true, error : function() { cDash = false; } } ); } } else if (cFlv !== false && fm.options.cdns.flv && type === 'flv') { e.stopImmediatePropagation(); if (cFlv) { loadFlv(file); } else { stock = window.flvjs; delete window.flvjs; fm.loadScript( [ fm.options.cdns.flv ], function(res) { cFlv = res || window.flvjs || false; window.flvjs = stock; cFlv && loadFlv(file); }, { tryRequire: true, error : function() { cFlv = false; } } ); } } } } }).on('change', reset); }, /** * Audio/video preview plugin using browser plugins * * @param elFinder.commands.quicklook **/ function(ql) { var preview = ql.preview, mimes = [], node, win = ql.window, navi = ql.navbar; jQuery.each(navigator.plugins, function(i, plugins) { jQuery.each(plugins, function(i, plugin) { (plugin.type.indexOf('audio/') === 0 || plugin.type.indexOf('video/') === 0) && mimes.push(plugin.type); }); }); mimes = ql.fm.arrayFlip(mimes); preview.on(ql.evUpdate, function(e) { var file = e.file, mime = file.mime, video, setNavi = function() { navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : ''); }; if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime)) { e.stopImmediatePropagation(); (video = mime.indexOf('video/') === 0) && ql.hideinfo(); node = jQuery('<embed src="'+ql.fm.openUrl(file.hash)+'" type="'+mime+'" class="elfinder-quicklook-preview-'+(video ? 'video' : 'audio')+'"/>') .appendTo(preview); win.on('viewchange.embed', setNavi); setNavi(); } }).on('change', function() { if (node && node.parent().length) { win.off('viewchange.embed'); node.remove(); node= null; } }); }, /** * Archive(zip|gzip|tar) preview plugin using https://github.com/imaya/zlib.js * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, mimes = fm.arrayFlip(['application/zip', 'application/x-gzip', 'application/x-tar']), preview = ql.preview, unzipFiles = function() { /** @type {Array.<string>} */ var filenameList = []; /** @type {number} */ var i; /** @type {number} */ var il; /** @type {Array.<Zlib.Unzip.FileHeader>} */ var fileHeaderList; // need check this.Y when update cdns.zlibUnzip this.Y(); fileHeaderList = this.i; for (i = 0, il = fileHeaderList.length; i < il; ++i) { // need check fileHeaderList[i].J when update cdns.zlibUnzip filenameList[i] = fileHeaderList[i].filename + (fileHeaderList[i].J? ' (' + fm.formatSize(fileHeaderList[i].J) + ')' : ''); } return filenameList; }, tarFiles = function(tar) { var filenames = [], tarlen = tar.length, offset = 0, toStr = function(arr) { return String.fromCharCode.apply(null, arr).replace(/\0+$/, ''); }, h, name, prefix, size, dbs; while (offset < tarlen && tar[offset] !== 0) { h = tar.subarray(offset, offset + 512); name = toStr(h.subarray(0, 100)); if (prefix = toStr(h.subarray(345, 500))) { name = prefix + name; } size = parseInt(toStr(h.subarray(124, 136)), 8); dbs = Math.ceil(size / 512) * 512; if (name === '././@LongLink') { name = toStr(tar.subarray(offset + 512, offset + 512 + dbs)); } (name !== 'pax_global_header') && filenames.push(name + (size? ' (' + fm.formatSize(size) + ')': '')); offset = offset + 512 + dbs; } return filenames; }, Zlib; if (window.Uint8Array && window.DataView && fm.options.cdns.zlibUnzip && fm.options.cdns.zlibGunzip) { preview.on(ql.evUpdate, function(e) { var file = e.file, isTar = (file.mime === 'application/x-tar'); if (mimes[file.mime] && ( isTar || ((typeof Zlib === 'undefined' || Zlib) && (file.mime === 'application/zip' || file.mime === 'application/x-gzip')) )) { var jqxhr, loading, url, req = function() { url = fm.openUrl(file.hash); if (!fm.isSameOrigin(url)) { url = fm.openUrl(file.hash, true); } jqxhr = fm.request({ data : {cmd : 'get'}, options : { url: url, type: 'get', cache : true, dataType : 'binary', responseType :'arraybuffer', processData: false } }) .fail(function() { loading.remove(); }) .done(function(data) { var unzip, filenames; try { if (file.mime === 'application/zip') { unzip = new Zlib.Unzip(new Uint8Array(data)); //filenames = unzip.getFilenames(); filenames = unzipFiles.call(unzip); } else if (file.mime === 'application/x-gzip') { unzip = new Zlib.Gunzip(new Uint8Array(data)); filenames = tarFiles(unzip.decompress()); } else if (file.mime === 'application/x-tar') { filenames = tarFiles(new Uint8Array(data)); } makeList(filenames); } catch (e) { loading.remove(); fm.debug('error', e); } }); }, makeList = function(filenames) { var header, doc; if (filenames && filenames.length) { filenames = jQuery.map(filenames, function(str) { return fm.decodeRawString(str); }); filenames.sort(); loading.remove(); header = '<strong>'+fm.escape(file.mime)+'</strong> ('+fm.formatSize(file.size)+')'+'<hr/>'; doc = jQuery('<div class="elfinder-quicklook-preview-archive-wrapper">'+header+'<pre class="elfinder-quicklook-preview-text">'+fm.escape(filenames.join("\n"))+'</pre></div>') .on('touchstart', function(e) { if (jQuery(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) { e.originalEvent._preventSwipeX = true; } }) .appendTo(preview); ql.hideinfo(); } }, _Zlib; // this is our file - stop event propagation e.stopImmediatePropagation(); loading = jQuery('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info')); // stop loading on change file if not loaded yet preview.one('change', function() { jqxhr.state() === 'pending' && jqxhr.reject(); loading.remove(); }); if (Zlib) { req(); } else { if (window.Zlib) { _Zlib = window.Zlib; delete window.Zlib; } fm.loadScript( [ fm.options.cdns.zlibUnzip, fm.options.cdns.zlibGunzip ], function() { if (window.Zlib && (Zlib = window.Zlib)) { if (_Zlib) { window.Zlib = _Zlib; } else { delete window.Zlib; } req(); } else { error(); } } ); } } }); } }, /** * RAR Archive preview plugin using https://github.com/43081j/rar.js * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, mimes = fm.arrayFlip(['application/x-rar']), preview = ql.preview, RAR; if (window.DataView) { preview.on(ql.evUpdate, function(e) { var file = e.file; if (mimes[file.mime] && fm.options.cdns.rar && RAR !== false) { var loading, url, archive, abort, getList = function(url) { if (abort) { loading.remove(); return; } try { archive = RAR({ file: url, type: 2, xhrHeaders: fm.customHeaders, xhrFields: fm.xhrFields }, function(err) { loading.remove(); var filenames = [], header, doc; if (abort || err) { // An error occurred (not a rar, read error, etc) err && fm.debug('error', err); return; } jQuery.each(archive.entries, function() { filenames.push(this.path + (this.size? ' (' + fm.formatSize(this.size) + ')' : '')); }); if (filenames.length) { filenames = jQuery.map(filenames, function(str) { return fm.decodeRawString(str); }); filenames.sort(); header = '<strong>'+fm.escape(file.mime)+'</strong> ('+fm.formatSize(file.size)+')'+'<hr/>'; doc = jQuery('<div class="elfinder-quicklook-preview-archive-wrapper">'+header+'<pre class="elfinder-quicklook-preview-text">'+fm.escape(filenames.join("\n"))+'</pre></div>') .on('touchstart', function(e) { if (jQuery(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) { e.originalEvent._preventSwipeX = true; } }) .appendTo(preview); ql.hideinfo(); } }); } catch(e) { loading.remove(); } }, error = function() { RAR = false; loading.remove(); }, _RAR; // this is our file - stop event propagation e.stopImmediatePropagation(); loading = jQuery('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info')); // stop loading on change file if not loaded yet preview.one('change', function() { archive && (archive.abort = true); loading.remove(); abort = true; }); url = fm.openUrl(file.hash); if (!fm.isSameOrigin(url)) { url = fm.openUrl(file.hash, true); } if (RAR) { getList(url); } else { if (window.RarArchive) { _RAR = window.RarArchive; delete window.RarArchive; } fm.loadScript( [ fm.options.cdns.rar ], function() { if (fm.hasRequire) { require(['rar'], function(RarArchive) { RAR = RarArchive; getList(url); }, error); } else { if (RAR = window.RarArchive) { if (_RAR) { window.RarArchive = _RAR; } else { delete window.RarArchive; } getList(url); } else { error(); } } }, { tryRequire: true, error : error } ); } } }); } }, /** * CAD-Files and 3D-Models online viewer on sharecad.org * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, mimes = fm.arrayFlip(ql.options.sharecadMimes || []), preview = ql.preview, win = ql.window, node; if (ql.options.sharecadMimes.length) { ql.addIntegration({ title: 'ShareCAD.org CAD and 3D-Models viewer', link: 'https://sharecad.org/DWGOnlinePlugin' }); } preview.on(ql.evUpdate, function(e) { var file = e.file; if (mimes[file.mime.toLowerCase()] && !fm.option('onetimeUrl', file.hash)) { var win = ql.window, loading, url; e.stopImmediatePropagation(); if (file.url == '1') { preview.hide(); jQuery('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info')) .on('click', function() { var self = jQuery(this); self.html('<span class="elfinder-spinner">'); fm.request({ data : {cmd : 'url', target : file.hash}, preventDefault : true }) .always(function() { self.html(''); }) .done(function(data) { var rfile = fm.file(file.hash); file.url = rfile.url = data.url || ''; if (file.url) { preview.trigger({ type: ql.evUpdate, file: file, forceUpdate: true }); } }); }); } if (file.url !== '' && file.url != '1') { preview.one('change', function() { loading.remove(); node.off('load').remove(); node = null; }).addClass('elfinder-overflow-auto'); loading = jQuery('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info')); url = fm.convAbsUrl(fm.url(file.hash)); node = jQuery('<iframe class="elfinder-quicklook-preview-iframe" scrolling="no"/>') .css('background-color', 'transparent') .appendTo(preview) .on('load', function() { ql.hideinfo(); loading.remove(); ql.preview.after(ql.info); jQuery(this).css('background-color', '#fff').show(); }) .on('error', function() { loading.remove(); ql.preview.after(ql.info); }) .attr('src', '//sharecad.org/cadframe/load?url=' + encodeURIComponent(url)); ql.info.after(ql.preview); } } }); }, /** * KML preview with GoogleMaps API * * @param elFinder.commands.quicklook */ function(ql) { var fm = ql.fm, mimes = { 'application/vnd.google-earth.kml+xml' : true, 'application/vnd.google-earth.kmz' : true }, preview = ql.preview, gMaps, loadMap, wGmfail, fail, mapScr; if (ql.options.googleMapsApiKey) { ql.addIntegration({ title: 'Google Maps', link: 'https://www.google.com/intl/' + fm.lang.replace('_', '-') + '/help/terms_maps.html' }); gMaps = (window.google && google.maps); // start load maps loadMap = function(file, node) { var mapsOpts = ql.options.googleMapsOpts.maps; fm.forExternalUrl(file.hash).done(function(url) { if (url) { try { new gMaps.KmlLayer(url, Object.assign({ map: new gMaps.Map(node.get(0), mapsOpts) }, ql.options.googleMapsOpts.kml)); ql.hideinfo(); } catch(e) { fail(); } } else { fail(); } }); }; // keep stored error handler if exists wGmfail = window.gm_authFailure; // on error function fail = function() { mapScr = null; }; // API script url mapScr = 'https://maps.googleapis.com/maps/api/js?key=' + ql.options.googleMapsApiKey; // error handler window.gm_authFailure = function() { fail(); wGmfail && wGmfail(); }; preview.on(ql.evUpdate, function(e) { var file = e.file; if (mapScr && mimes[file.mime.toLowerCase()]) { var win = ql.window, getLink = (file.url == '1' && !fm.option('onetimeUrl', file.hash)), loading, url, node; e.stopImmediatePropagation(); if (getLink) { preview.hide(); jQuery('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info')) .on('click', function() { var self = jQuery(this); self.html('<span class="elfinder-spinner">'); fm.request({ data : {cmd : 'url', target : file.hash}, preventDefault : true }) .always(function() { self.html(''); }) .done(function(data) { var rfile = fm.file(file.hash); file.url = rfile.url = data.url || ''; if (file.url) { preview.trigger({ type: ql.evUpdate, file: file, forceUpdate: true }); } }); }); } if (file.url !== '' && !getLink) { node = jQuery('<div style="width:100%;height:100%;"/>').appendTo(preview); preview.one('change', function() { node.remove(); node = null; }); if (!gMaps) { fm.loadScript([mapScr], function() { gMaps = window.google && google.maps; gMaps && loadMap(file, node); }); } else { loadMap(file, node); } } } }); } }, /** * Any supported files preview plugin using (Google docs | MS Office) online viewer * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, mimes = Object.assign(fm.arrayFlip(ql.options.googleDocsMimes || [], 'g'), fm.arrayFlip(ql.options.officeOnlineMimes || [], 'm')), preview = ql.preview, win = ql.window, navi = ql.navbar, urls = { g: 'docs.google.com/gview?embedded=true&url=', m: 'view.officeapps.live.com/op/embed.aspx?wdStartOn=0&src=' }, navBottom = { g: '56px', m: '24px' }, mLimits = { xls : 5242880, // 5MB xlsb : 5242880, xlsx : 5242880, xlsm : 5242880, other: 10485760 // 10MB }, node, enable; if (ql.options.googleDocsMimes.length) { enable = true; ql.addIntegration({ title: 'Google Docs Viewer', link: 'https://docs.google.com/' }); } if (ql.options.officeOnlineMimes.length) { enable = true; ql.addIntegration({ title: 'MS Online Doc Viewer', link: 'https://products.office.com/office-online/view-office-documents-online' }); } if (enable) { preview.on(ql.evUpdate, function(e) { var file = e.file, type; // 25MB is maximum filesize of Google Docs prevew if (file.size <= 26214400 && (type = mimes[file.mime])) { var win = ql.window, setNavi = function() { navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? navBottom[type] : ''); }, ext = fm.mimeTypes[file.mime], getLink = (file.url == '1' && !fm.option('onetimeUrl', file.hash)), loading, url; if (type === 'm') { if ((mLimits[ext] && file.size > mLimits[ext]) || file.size > mLimits.other) { type = 'g'; } } if (getLink) { preview.hide(); jQuery('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info')) .on('click', function() { var self = jQuery(this); self.html('<span class="elfinder-spinner">'); fm.request({ data : {cmd : 'url', target : file.hash}, preventDefault : true }) .always(function() { self.html(''); }) .done(function(data) { var rfile = fm.file(file.hash); file.url = rfile.url = data.url || ''; if (file.url) { preview.trigger({ type: ql.evUpdate, file: file, forceUpdate: true }); } }); }); } if (file.url !== '' && !getLink) { e.stopImmediatePropagation(); preview.one('change', function() { win.off('viewchange.googledocs'); loading.remove(); node.off('load').remove(); node = null; }).addClass('elfinder-overflow-auto'); loading = jQuery('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info')); node = jQuery('<iframe class="elfinder-quicklook-preview-iframe"/>') .css('background-color', 'transparent') .appendTo(preview); fm.forExternalUrl(file.hash).done(function(url) { if (url) { if (file.ts) { url += (url.match(/\?/)? '&' : '?') + '_t=' + file.ts; } node.on('load', function() { ql.hideinfo(); loading.remove(); ql.preview.after(ql.info); jQuery(this).css('background-color', '#fff').show(); }) .on('error', function() { loading.remove(); ql.preview.after(ql.info); }).attr('src', 'https://' + urls[type] + encodeURIComponent(url)); } else { loading.remove(); node.remove(); } }); win.on('viewchange.googledocs', setNavi); setNavi(); ql.info.after(ql.preview); } } }); } }, /** * Texts preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, preview = ql.preview, textMaxlen = parseInt(ql.options.textMaxlen) || 2000, prettify = function() { if (fm.options.cdns.prettify) { fm.loadScript([fm.options.cdns.prettify + (fm.options.cdns.prettify.match(/\?/)? '&' : '?') + 'autorun=false']); prettify = function() { return true; }; } else { prettify = function() { return false; }; } }, PRcheck = function(node, cnt) { if (prettify()) { if (typeof window.PR === 'undefined' && cnt--) { setTimeout(function() { PRcheck(node, cnt); }, 100); } else { if (typeof window.PR === 'object') { node.css('cursor', 'wait'); requestAnimationFrame(function() { PR.prettyPrint && PR.prettyPrint(null, node.get(0)); node.css('cursor', ''); }); } else { prettify = function() { return false; }; } } } }; preview.on(ql.evUpdate, function(e) { var file = e.file, mime = file.mime, jqxhr, loading; if (fm.mimeIsText(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) { e.stopImmediatePropagation(); (typeof window.PR === 'undefined') && prettify(); loading = jQuery('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info')); // stop loading on change file if not loadin yet preview.one('change', function() { jqxhr.state() == 'pending' && jqxhr.reject(); }); jqxhr = fm.request({ data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts}, options : {type: 'get', cache : true}, preventDefault : true }) .done(function(data) { var reg = new RegExp('^(data:'+file.mime.replace(/([.+])/g, '\\$1')+';base64,)', 'i'), text = data.content, part, more, node, m; ql.hideinfo(); if (window.atob && (m = text.match(reg))) { text = atob(text.substr(m[1].length)); } more = text.length - textMaxlen; if (more > 100) { part = text.substr(0, textMaxlen) + '...'; } else { more = 0; } node = jQuery('<div class="elfinder-quicklook-preview-text-wrapper"><pre class="elfinder-quicklook-preview-text prettyprint"></pre></div>'); if (more) { node.append(jQuery('<div class="elfinder-quicklook-preview-charsleft"><hr/><span>' + fm.i18n('charsLeft', fm.toLocaleString(more)) + '</span></div>') .on('click', function() { var top = node.scrollTop(); jQuery(this).remove(); node.children('pre').removeClass('prettyprinted').text(text).scrollTop(top); PRcheck(node, 100); }) ); } node.children('pre').text(part || text); node.on('touchstart', function(e) { if (jQuery(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) { e.originalEvent._preventSwipeX = true; } }).appendTo(preview); PRcheck(node, 100); }) .always(function() { loading.remove(); }); } }); } ]; /* * File: /js/commands/reload.js */ /** * @class elFinder command "reload" * Sync files and folders * * @author Dmitry (dio) Levashov **/ (elFinder.prototype.commands.reload = function() { "use strict"; var self = this, search = false; this.alwaysEnabled = true; this.updateOnSelect = true; this.shortcuts = [{ pattern : 'ctrl+shift+r f5' }]; this.getstate = function() { return 0; }; this.init = function() { this.fm.bind('search searchend', function() { search = this.type == 'search'; }); }; this.fm.bind('contextmenu', function(){ var fm = self.fm; if (fm.options.sync >= 1000) { self.extra = { icon: 'accept', node: jQuery('<span/>') .attr({title: fm.i18n('autoSync')}) .on('click touchstart', function(e){ if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) { return; } e.stopPropagation(); e.preventDefault(); jQuery(this).parent() .toggleClass('ui-state-disabled', fm.options.syncStart) .parent().removeClass('ui-state-hover'); fm.options.syncStart = !fm.options.syncStart; fm.autoSync(fm.options.syncStart? null : 'stop'); }).on('ready', function(){ jQuery(this).parent().toggleClass('ui-state-disabled', !fm.options.syncStart).css('pointer-events', 'auto'); }) }; } }); this.exec = function() { var fm = this.fm; if (!search) { var dfrd = fm.sync(), timeout = setTimeout(function() { fm.notify({type : 'reload', cnt : 1, hideCnt : true}); dfrd.always(function() { fm.notify({type : 'reload', cnt : -1}); }); }, fm.notifyDelay); return dfrd.always(function() { clearTimeout(timeout); fm.trigger('reload'); }); } else { jQuery('div.elfinder-toolbar > div.'+fm.res('class', 'searchbtn') + ' > span.ui-icon-search').click(); } }; }).prototype = { forceLoad : true }; // this is required command /* * File: /js/commands/rename.js */ /** * @class elFinder command "rename". * Rename selected file. * * @author Dmitry (dio) Levashov, dio@std42.ru * @author Naoki Sawada **/ elFinder.prototype.commands.rename = function() { "use strict"; // set alwaysEnabled to allow root rename on client size this.alwaysEnabled = true; this.syncTitleOnChange = true; var self = this, fm = self.fm, request = function(dfrd, targtes, file, name) { var sel = targtes? [file.hash].concat(targtes) : [file.hash], cnt = sel.length, data = {}, rootNames; fm.lockfiles({files : sel}); if (fm.isRoot(file)) { if (!(rootNames = fm.storage('rootNames'))) { rootNames = {}; } if (name === '') { if (rootNames[file.hash]) { file.name = file._name; file.i18 = file._i18; delete rootNames[file.hash]; delete file._name; delete file._i18; } else { dfrd && dfrd.reject(); fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel}); return; } } else { if (typeof file._name === 'undefined') { file._name = file.name; file._i18 = file.i18; } file.name = rootNames[file.hash] = name; delete file.i18; } fm.storage('rootNames', rootNames); data = { changed: [file] }; fm.updateCache(data); fm.change(data); dfrd && dfrd.resolve(data); fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel}); return; } data = { cmd : 'rename', name : name, target : file.hash }; if (cnt > 1) { data['targets'] = targtes; if (name.match(/\*/)) { data['q'] = name; } } fm.request({ data : data, notify : {type : 'rename', cnt : cnt}, navigate : {} }) .fail(function(error) { var err = fm.parseError(error); dfrd && dfrd.reject(); if (! err || ! Array.isArray(err) || err[0] !== 'errRename') { fm.sync(); } }) .done(function(data) { var cwdHash; if (data.added && data.added.length && cnt === 1) { data.undo = { cmd : 'rename', callback : function() { return fm.request({ data : {cmd : 'rename', target : data.added[0].hash, name : file.name}, notify : {type : 'undo', cnt : 1} }); } }; data.redo = { cmd : 'rename', callback : function() { return fm.request({ data : {cmd : 'rename', target : file.hash, name : name}, notify : {type : 'rename', cnt : 1} }); } }; } dfrd && dfrd.resolve(data); if (!(cwdHash = fm.cwd().hash) || cwdHash === file.hash) { fm.exec('open', jQuery.map(data.added, function(f) { return (f.mime === 'directory')? f.hash : null; })[0]); } }) .always(function() { fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel}); } ); }, getHint = function(name, target) { var sel = target || fm.selected(), splits = fm.splitFileExtention(name), f1 = fm.file(sel[0]), f2 = fm.file(sel[1]), ext, hint, add; ext = splits[1]? ('.' + splits[1]) : ''; if (splits[1] && splits[0] === '*') { // change extention hint = '"' + fm.splitFileExtention(f1.name)[0] + ext + '", '; hint += '"' + fm.splitFileExtention(f2.name)[0] + ext + '"'; } else if (splits[0].length > 1) { if (splits[0].substr(-1) === '*') { // add prefix add = splits[0].substr(0, splits[0].length - 1); hint = '"' + add + f1.name+'", '; hint += '"' + add + f2.name+'"'; } else if (splits[0].substr(0, 1) === '*') { // add suffix add = splits[0].substr(1); hint = '"'+fm.splitFileExtention(f1.name)[0] + add + ext + '", '; hint += '"'+fm.splitFileExtention(f2.name)[0] + add + ext + '"'; } } if (!hint) { hint = '"'+splits[0] + '1' + ext + '", "' + splits[0] + '2' + ext + '"'; } if (sel.length > 2) { hint += ' ...'; } return hint; }, batchRename = function() { var sel = fm.selected(), tplr = '<input name="type" type="radio" class="elfinder-tabstop">', mkChk = function(node, label) { return jQuery('<label class="elfinder-rename-batch-checks">' + fm.i18n(label) + '</label>').prepend(node); }, name = jQuery('<input type="text" class="ui-corner-all elfinder-tabstop">'), num = jQuery(tplr), prefix = jQuery(tplr), suffix = jQuery(tplr), extention = jQuery(tplr), checks = jQuery('<div/>').append( mkChk(num, 'plusNumber'), mkChk(prefix, 'asPrefix'), mkChk(suffix, 'asSuffix'), mkChk(extention, 'changeExtention') ), preview = jQuery('<div class="elfinder-rename-batch-preview"/>'), node = jQuery('<div class="elfinder-rename-batch"/>').append( jQuery('<div class="elfinder-rename-batch-name"/>').append(name), jQuery('<div class="elfinder-rename-batch-type"/>').append(checks), preview ), opts = { title : fm.i18n('batchRename'), modal : true, destroyOnClose : true, width: Math.min(380, fm.getUI().width() - 20), buttons : {}, open : function() { name.on('input', mkPrev).trigger('focus'); } }, getName = function() { var vName = name.val(), ext = fm.splitFileExtention(fm.file(sel[0]).name)[1]; if (vName !== '' || num.is(':checked')) { if (prefix.is(':checked')) { vName += '*'; } else if (suffix.is(':checked')) { vName = '*' + vName + '.' + ext; } else if (extention.is(':checked')) { vName = '*.' + vName; } else if (ext) { vName += '.' + ext; } } return vName; }, mkPrev = function() { var vName = getName(); if (vName !== '') { preview.html(fm.i18n(['renameMultiple', sel.length, getHint(vName)])); } else { preview.empty(); } }, radios = checks.find('input:radio').on('change', mkPrev), dialog; opts.buttons[fm.i18n('btnApply')] = function() { var vName = getName(), file, targets; if (vName !== '') { dialog.elfinderdialog('close'); targets = sel; file = fm.file(targets.shift()); request(void(0), targets, file, vName); } }; opts.buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); }; if (jQuery.fn.checkboxradio) { radios.checkboxradio({ create: function(e, ui) { if (this === num.get(0)) { num.prop('checked', true).change(); } } }); } else { checks.buttonset({ create: function(e, ui) { num.prop('checked', true).change(); } }); } dialog = self.fmDialog(node, opts); }; this.noChangeDirOnRemovedCwd = true; this.shortcuts = [{ pattern : 'f2' + (fm.OS == 'mac' ? ' enter' : '') }, { pattern : 'shift+f2', description : 'batchRename', callback : function() { fm.selected().length > 1 && batchRename(); } }]; this.getstate = function(select) { var sel = this.files(select), cnt = sel.length, phash, ext, mime, brk, state, isRoot; if (!cnt) { return -1; } if (cnt > 1 && sel[0].phash) { phash = sel[0].phash; ext = fm.splitFileExtention(sel[0].name)[1].toLowerCase(); mime = sel[0].mime; } if (cnt === 1) { isRoot = fm.isRoot(sel[0]); } state = (cnt === 1 && (isRoot || !sel[0].locked) || (fm.api > 2.1030 && cnt === jQuery.grep(sel, function(f) { if (!brk && !f.locked && f.phash === phash && !fm.isRoot(f) && (mime === f.mime || ext === fm.splitFileExtention(f.name)[1].toLowerCase())) { return true; } else { brk && (brk = true); return false; } }).length)) ? 0 : -1; // because alwaysEnabled = true, it need check disabled on connector if (!isRoot && state === 0 && fm.option('disabledFlip', sel[0].hash)['rename']) { state = -1; } if (state !== -1 && cnt > 1) { self.extra = { icon: 'preference', node: jQuery('<span/>') .attr({title: fm.i18n('batchRename')}) .on('click touchstart', function(e){ if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) { return; } e.stopPropagation(); e.preventDefault(); fm.getUI().trigger('click'); // to close the context menu immediately batchRename(); }) }; } else { delete self.extra; } return state; }; this.exec = function(hashes, cOpts) { var cwd = fm.getUI('cwd'), sel = hashes || (fm.selected().length? fm.selected() : false) || [fm.cwd().hash], cnt = sel.length, file = fm.file(sel.shift()), filename = '.elfinder-cwd-filename', opts = cOpts || {}, incwd = (fm.cwd().hash == file.hash), type = (opts._currentType === 'navbar' || opts._currentType === 'files')? opts._currentType : (incwd? 'navbar' : 'files'), navbar = (type !== 'files'), target = fm[navbar? 'navHash2Elm' : 'cwdHash2Elm'](file.hash), tarea = (!navbar && fm.storage('view') != 'list'), split = function(name) { var ext = fm.splitFileExtention(name)[1]; return [name.substr(0, name.length - ext.length - 1), ext]; }, unselect = function() { requestAnimationFrame(function() { input && input.trigger('blur'); }); }, rest = function(){ if (!overlay.is(':hidden')) { overlay.elfinderoverlay('hide').off('click close', cancel); } pnode.removeClass('ui-front') .css('position', '') .off('unselect.'+fm.namespace, unselect); if (tarea) { node && node.css('max-height', ''); } else if (!navbar) { pnode.css('width', '') .parent('td').css('overflow', ''); } }, colwidth, dfrd = jQuery.Deferred() .fail(function(error) { var parent = input.parent(), name = fm.escape(file.i18 || file.name); input.off(); if (tarea) { name = name.replace(/([_.])/g, '​$1'); } requestAnimationFrame(function() { if (navbar) { input.replaceWith(name); } else { if (parent.length) { input.remove(); parent.html(name); } else { target.find(filename).html(name); } } }); error && fm.error(error); }) .always(function() { rest(); fm.unbind('resize', resize); fm.enable(); }), blur = function(e) { var name = jQuery.trim(input.val()), splits = fm.splitFileExtention(name), valid = true, req = function() { input.off(); rest(); if (navbar) { input.replaceWith(fm.escape(name)); } else { node.html(fm.escape(name)); } request(dfrd, sel, file, name); }; if (!overlay.is(':hidden')) { pnode.css('z-index', ''); } if (name === '') { if (!fm.isRoot(file)) { return cancel(); } if (navbar) { input.replaceWith(fm.escape(file.name)); } else { node.html(fm.escape(file.name)); } } if (!inError && pnode.length) { input.off('blur'); if (cnt === 1 && name === file.name) { return dfrd.reject(); } if (fm.options.validName && fm.options.validName.test) { try { valid = fm.options.validName.test(name); } catch(e) { valid = false; } } if (name === '.' || name === '..' || !valid) { inError = true; fm.error(file.mime === 'directory'? 'errInvDirname' : 'errInvName', {modal: true, close: function(){setTimeout(select, 120);}}); return false; } if (cnt === 1 && fm.fileByName(name, file.phash)) { inError = true; fm.error(['errExists', name], {modal: true, close: function(){setTimeout(select, 120);}}); return false; } if (cnt === 1) { req(); } else { fm.confirm({ title : 'cmdrename', text : ['renameMultiple', cnt, getHint(name, [file.hash].concat(sel))], accept : { label : 'btnYes', callback : req }, cancel : { label : 'btnCancel', callback : function() { setTimeout(function() { inError = true; select(); }, 120); } } }); setTimeout(function() { fm.trigger('unselectfiles', {files: fm.selected()}) .trigger('selectfiles', {files : [file.hash].concat(sel)}); }, 120); } } }, input = jQuery(tarea? '<textarea/>' : '<input type="text"/>') .on('keyup text', function(){ if (tarea) { this.style.height = '1px'; this.style.height = this.scrollHeight + 'px'; } else if (colwidth) { this.style.width = colwidth + 'px'; if (this.scrollWidth > colwidth) { this.style.width = this.scrollWidth + 10 + 'px'; } } }) .on('keydown', function(e) { e.stopImmediatePropagation(); if (e.keyCode == jQuery.ui.keyCode.ESCAPE) { dfrd.reject(); } else if (e.keyCode == jQuery.ui.keyCode.ENTER) { e.preventDefault(); input.trigger('blur'); } }) .on('mousedown click dblclick', function(e) { e.stopPropagation(); if (e.type === 'dblclick') { e.preventDefault(); } }) .on('blur', blur) .on('dragenter dragleave dragover drop', function(e) { // stop bubbling to prevent upload with native drop event e.stopPropagation(); }), select = function() { var name = fm.splitFileExtention(input.val())[0]; if (!inError && fm.UA.Mobile && !fm.UA.iOS) { // since iOS has a bug? (z-index not effect) so disable it overlay.on('click close', cancel).elfinderoverlay('show'); pnode.css('z-index', overlay.css('z-index') + 1); } ! fm.enabled() && fm.enable(); if (inError) { inError = false; input.on('blur', blur); } input.trigger('focus').trigger('select'); input[0].setSelectionRange && input[0].setSelectionRange(0, name.length); }, node = navbar? target.contents().filter(function(){ return this.nodeType==3 && jQuery(this).parent().attr('id') === fm.navHash2Id(file.hash); }) : target.find(filename), pnode = node.parent(), overlay = fm.getUI('overlay'), cancel = function(e) { if (!overlay.is(':hidden')) { pnode.css('z-index', ''); } if (! inError) { dfrd.reject(); if (e) { e.stopPropagation(); e.preventDefault(); } } }, resize = function() { target.trigger('scrolltoview', {blink : false}); }, inError = false; pnode.addClass('ui-front') .css('position', 'relative') .on('unselect.'+fm.namespace, unselect); fm.bind('resize', resize); if (navbar) { node.replaceWith(input.val(file.name)); } else { if (tarea) { node.css('max-height', 'none'); } else if (!navbar) { colwidth = pnode.width(); pnode.width(colwidth - 15) .parent('td').css('overflow', 'visible'); } node.empty().append(input.val(file.name)); } if (cnt > 1 && fm.api <= 2.1030) { return dfrd.reject(); } if (!file || !node.length) { return dfrd.reject('errCmdParams', this.title); } if (file.locked && !fm.isRoot(file)) { return dfrd.reject(['errLocked', file.name]); } fm.one('select', function() { input.parent().length && file && jQuery.inArray(file.hash, fm.selected()) === -1 && input.trigger('blur'); }); input.trigger('keyup'); select(); return dfrd; }; fm.bind('select contextmenucreate closecontextmenu', function(e) { var sel = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected(), file; if (sel && sel.length === 1 && (file = fm.file(sel[0])) && fm.isRoot(file)) { self.title = fm.i18n('kindAlias') + ' (' + fm.i18n('preference') + ')'; } else { self.title = fm.i18n('cmdrename'); } if (e.type !== 'closecontextmenu') { self.update(void(0), self.title); } else { requestAnimationFrame(function() { self.update(void(0), self.title); }); } }).remove(function(e) { var rootNames; if (e.data && e.data.removed && (rootNames = fm.storage('rootNames'))) { jQuery.each(e.data.removed, function(i, h) { if (rootNames[h]) { delete rootNames[h]; } }); fm.storage('rootNames', rootNames); } }); }; /* * File: /js/commands/resize.js */ /** * @class elFinder command "resize" * Open dialog to resize image * * @author Dmitry (dio) Levashov * @author Alexey Sukhotin * @author Naoki Sawada * @author Sergio Jovani **/ elFinder.prototype.commands.resize = function() { "use strict"; var losslessRotate = 0, getBounceBox = function(w, h, theta) { var srcPts = [ {x: w/2, y: h/2}, {x: -w/2, y: h/2}, {x: -w/2, y: -h/2}, {x: w/2, y: -h/2} ], dstPts = [], min = {x: Number.MAX_VALUE, y: Number.MAX_VALUE}, max = {x: Number.MIN_VALUE, y: Number.MIN_VALUE}; jQuery.each(srcPts, function(i, srcPt){ dstPts.push({ x: srcPt.x * Math.cos(theta) - srcPt.y * Math.sin(theta), y: srcPt.x * Math.sin(theta) + srcPt.y * Math.cos(theta) }); }); jQuery.each(dstPts, function(i, pt) { min.x = Math.min(min.x, pt.x); min.y = Math.min(min.y, pt.y); max.x = Math.max(max.x, pt.x); max.y = Math.max(max.y, pt.y); }); return { width: max.x - min.x, height: max.y - min.y }; }; this.updateOnSelect = false; this.getstate = function() { var sel = this.fm.selectedFiles(); return sel.length == 1 && sel[0].read && sel[0].write && sel[0].mime.indexOf('image/') !== -1 ? 0 : -1; }; this.resizeRequest = function(data, f, dfrd) { var fm = this.fm, file = f || fm.file(data.target), tmb = file? file.tmb : null, enabled = fm.isCommandEnabled('resize', data.target); if (enabled && (! file || (file && file.read && file.write && file.mime.indexOf('image/') !== -1 ))) { return fm.request({ data : Object.assign(data, { cmd : 'resize' }), notify : {type : 'resize', cnt : 1} }) .fail(function(error) { if (dfrd) { dfrd.reject(error); } }) .done(function() { if (data.quality) { fm.storage('jpgQuality', data.quality === fm.option('jpgQuality')? null : data.quality); } dfrd && dfrd.resolve(); }); } else { var error; if (file) { if (file.mime.indexOf('image/') === -1) { error = ['errResize', file.name, 'errUsupportType']; } else { error = ['errResize', file.name, 'errPerm']; } } else { error = ['errResize', data.target, 'errPerm']; } if (dfrd) { dfrd.reject(error); } else { fm.error(error); } return jQuery.Deferred().reject(error); } }; this.exec = function(hashes) { var self = this, fm = this.fm, files = this.files(hashes), dfrd = jQuery.Deferred(), api2 = (fm.api > 1), options = this.options, dialogWidth = 650, fmnode = fm.getUI(), ctrgrup = jQuery().controlgroup? 'controlgroup' : 'buttonset', grid8Def = typeof options.grid8px === 'undefined' || options.grid8px !== 'disable'? true : false, presetSize = Array.isArray(options.presetSize)? options.presetSize : [], clactive = 'elfinder-dialog-active', clsediting = fm.res('class', 'editing'), open = function(file, id) { var isJpeg = (file.mime === 'image/jpeg'), dialog = jQuery('<div class="elfinder-resize-container"/>'), input = '<input type="number" class="ui-corner-all"/>', row = '<div class="elfinder-resize-row"/>', label = '<div class="elfinder-resize-label"/>', changeTm = null, operate = false, opStart = function() { operate = true; }, opStop = function() { if (operate) { operate = false; control.trigger('change'); } }, control = jQuery('<div class="elfinder-resize-control"/>') .on('focus', 'input[type=text],input[type=number]', function() { jQuery(this).trigger('select'); }) .on('change', function() { changeTm && cancelAnimationFrame(changeTm); changeTm = requestAnimationFrame(function() { var panel, quty, canvas, ctx, img, sx, sy, sw, sh, deg, theta, bb; if (sizeImg && ! operate && (canvas = sizeImg.data('canvas'))) { panel = control.children('div.elfinder-resize-control-panel:visible'); quty = panel.find('input.elfinder-resize-quality'); if (quty.is(':visible')) { ctx = sizeImg.data('ctx'); img = sizeImg.get(0); if (panel.hasClass('elfinder-resize-uiresize')) { // resize sw = canvas.width = width.val(); sh = canvas.height = height.val(); ctx.drawImage(img, 0, 0, sw, sh); } else if (panel.hasClass('elfinder-resize-uicrop')) { // crop sx = pointX.val(); sy = pointY.val(); sw = offsetX.val(); sh = offsetY.val(); canvas.width = sw; canvas.height = sh; ctx.drawImage(img, sx, sy, sw, sh, 0, 0, sw, sh); } else { // rotate deg = degree.val(); theta = (degree.val() * Math.PI) / 180; bb = getBounceBox(owidth, oheight, theta); sw = canvas.width = bb.width; sh = canvas.height = bb.height; ctx.save(); if (deg % 90 !== 0) { ctx.fillStyle = bg.val() || '#FFF'; ctx.fillRect(0, 0, sw, sh); } ctx.translate(sw / 2, sh / 2); ctx.rotate(theta); ctx.drawImage(img, -img.width/2, -img.height/2, owidth, oheight); ctx.restore(); } canvas.toBlob(function(blob) { blob && quty.next('span').text(' (' + fm.formatSize(blob.size) + ')'); }, 'image/jpeg', Math.max(Math.min(quty.val(), 100), 1) / 100); } } }); }) .on('mouseup', 'input', function(e) { jQuery(e.target).trigger('change'); }), preview = jQuery('<div class="elfinder-resize-preview"/>') .on('touchmove', function(e) { if (jQuery(e.target).hasClass('touch-punch')) { e.stopPropagation(); e.preventDefault(); } }), spinner = jQuery('<div class="elfinder-resize-loading">'+fm.i18n('ntfloadimg')+'</div>'), rhandle = jQuery('<div class="elfinder-resize-handle touch-punch"/>'), rhandlec = jQuery('<div class="elfinder-resize-handle touch-punch"/>'), uiresize = jQuery('<div class="elfinder-resize-uiresize elfinder-resize-control-panel"/>'), uicrop = jQuery('<div class="elfinder-resize-uicrop elfinder-resize-control-panel"/>'), uirotate = jQuery('<div class="elfinder-resize-rotate elfinder-resize-control-panel"/>'), uideg270 = jQuery('<button/>').attr('title',fm.i18n('rotate-cw')).append(jQuery('<span class="elfinder-button-icon elfinder-button-icon-rotate-l"/>')), uideg90 = jQuery('<button/>').attr('title',fm.i18n('rotate-ccw')).append(jQuery('<span class="elfinder-button-icon elfinder-button-icon-rotate-r"/>')), uiprop = jQuery('<span />'), reset = jQuery('<button class="elfinder-resize-reset">').text(fm.i18n('reset')) .on('click', function() { resetView(); }) .button({ icons: { primary: 'ui-icon-arrowrefresh-1-n' }, text: false }), uitype = jQuery('<div class="elfinder-resize-type"/>') .append('<input type="radio" name="type" id="'+id+'-resize" value="resize" checked="checked" /><label for="'+id+'-resize">'+fm.i18n('resize')+'</label>', '<input class="api2" type="radio" name="type" id="'+id+'-crop" value="crop" /><label class="api2" for="'+id+'-crop">'+fm.i18n('crop')+'</label>', '<input class="api2" type="radio" name="type" id="'+id+'-rotate" value="rotate" /><label class="api2" for="'+id+'-rotate">'+fm.i18n('rotate')+'</label>'), mode = 'resize', type = uitype[ctrgrup]()[ctrgrup]('disable').find('input') .on('change', function() { mode = jQuery(this).val(); resetView(); resizable(true); croppable(true); rotateable(true); if (mode == 'resize') { uiresize.show(); uirotate.hide(); uicrop.hide(); resizable(); isJpeg && grid8px.insertAfter(uiresize.find('.elfinder-resize-grid8')); } else if (mode == 'crop') { uirotate.hide(); uiresize.hide(); uicrop.show(); croppable(); isJpeg && grid8px.insertAfter(uicrop.find('.elfinder-resize-grid8')); } else if (mode == 'rotate') { uiresize.hide(); uicrop.hide(); uirotate.show(); rotateable(); } }), width = jQuery(input) .on('change', function() { var w = round(parseInt(width.val())), h = round(cratio ? w/ratio : parseInt(height.val())); if (w > 0 && h > 0) { resize.updateView(w, h); width.val(w); height.val(h); } }).addClass('elfinder-focus'), height = jQuery(input) .on('change', function() { var h = round(parseInt(height.val())), w = round(cratio ? h*ratio : parseInt(width.val())); if (w > 0 && h > 0) { resize.updateView(w, h); width.val(w); height.val(h); } }), pointX = jQuery(input).on('change', function(){crop.updateView();}), pointY = jQuery(input).on('change', function(){crop.updateView();}), offsetX = jQuery(input).on('change', function(){crop.updateView('w');}), offsetY = jQuery(input).on('change', function(){crop.updateView('h');}), quality = isJpeg && api2? jQuery(input).val(fm.storage('jpgQuality') > 0? fm.storage('jpgQuality') : fm.option('jpgQuality')) .addClass('elfinder-resize-quality') .attr('min', '1').attr('max', '100').attr('title', '1 - 100') .on('blur', function(){ var q = Math.min(100, Math.max(1, parseInt(this.value))); control.find('input.elfinder-resize-quality').val(q); }) : null, degree = jQuery('<input type="number" class="ui-corner-all" maxlength="3" value="0" />') .on('change', function() { rotate.update(); }), uidegslider = jQuery('<div class="elfinder-resize-rotate-slider touch-punch"/>') .slider({ min: 0, max: 360, value: degree.val(), animate: true, start: opStart, stop: opStop, change: function(event, ui) { if (ui.value != uidegslider.slider('value')) { rotate.update(ui.value); } }, slide: function(event, ui) { rotate.update(ui.value, false); } }).find('.ui-slider-handle') .addClass('elfinder-tabstop') .off('keydown') .on('keydown', function(e) { if (e.keyCode == jQuery.ui.keyCode.LEFT || e.keyCode == jQuery.ui.keyCode.RIGHT) { e.stopPropagation(); e.preventDefault(); rotate.update(Number(degree.val()) + (e.keyCode == jQuery.ui.keyCode.RIGHT? 1 : -1), false); } }) .end(), pickimg, pickcanv, pickctx, pickc = {}, pick = function(e) { var color, r, g, b, h, s, l; try { color = pickc[Math.round(e.offsetX)][Math.round(e.offsetY)]; } catch(e) {} if (!color) return; r = color[0]; g = color[1]; b = color[2]; h = color[3]; s = color[4]; l = color[5]; setbg(r, g, b, (e.type === 'click')); }, palpick = function(e) { setbg(jQuery(this).css('backgroundColor'), '', '', (e.type === 'click')); }, setbg = function(r, g, b, off) { var s, m, cc; if (typeof r === 'string') { g = ''; if (r && (s = jQuery('<span>').css('backgroundColor', r).css('backgroundColor')) && (m = s.match(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i))) { r = Number(m[1]); g = Number(m[2]); b = Number(m[3]); } } cc = (g === '')? r : '#' + getColorCode(r, g, b); bg.val(cc).css({ backgroundColor: cc, backgroundImage: 'none', color: (r+g+b < 384? '#fff' : '#000') }); preview.css('backgroundColor', cc); if (off) { imgr.off('.picker').removeClass('elfinder-resize-picking'); pallet.off('.picker').removeClass('elfinder-resize-picking'); } }, getColorCode = function(r, g, b) { return jQuery.map([r,g,b], function(c){return ('0'+parseInt(c).toString(16)).slice(-2);}).join(''); }, picker = jQuery('<button>').text(fm.i18n('colorPicker')) .on('click', function() { imgr.on('mousemove.picker click.picker', pick).addClass('elfinder-resize-picking'); pallet.on('mousemove.picker click.picker', 'span', palpick).addClass('elfinder-resize-picking'); }) .button({ icons: { primary: 'ui-icon-pin-s' }, text: false }), reseter = jQuery('<button>').text(fm.i18n('reset')) .on('click', function() { setbg('', '', '', true); }) .button({ icons: { primary: 'ui-icon-arrowrefresh-1-n' }, text: false }), bg = jQuery('<input class="ui-corner-all elfinder-resize-bg" type="text">') .on('focus', function() { jQuery(this).attr('style', ''); }) .on('blur', function() { setbg(jQuery(this).val()); }), pallet = jQuery('<div class="elfinder-resize-pallet">').on('click', 'span', function() { setbg(jQuery(this).css('backgroundColor')); }), ratio = 1, prop = 1, owidth = 0, oheight = 0, cratio = true, cratioc = false, pwidth = 0, pheight = 0, rwidth = 0, rheight = 0, rdegree = 0, grid8 = isJpeg? grid8Def : false, constr = jQuery('<button>').html(fm.i18n('aspectRatio')) .on('click', function() { cratio = ! cratio; constr.button('option', { icons : { primary: cratio? 'ui-icon-locked' : 'ui-icon-unlocked'} }); resize.fixHeight(); rhandle.resizable('option', 'aspectRatio', cratio).data('uiResizable')._aspectRatio = cratio; }) .button({ icons : { primary: cratio? 'ui-icon-locked' : 'ui-icon-unlocked' }, text: false }), constrc = jQuery('<button>').html(fm.i18n('aspectRatio')) .on('click', function() { cratioc = ! cratioc; constrc.button('option', { icons : { primary: cratioc? 'ui-icon-locked' : 'ui-icon-unlocked'} }); rhandlec.resizable('option', 'aspectRatio', cratioc).data('uiResizable')._aspectRatio = cratioc; }) .button({ icons : { primary: cratioc? 'ui-icon-locked' : 'ui-icon-unlocked' }, text: false }), grid8px = jQuery('<button>').html(fm.i18n(grid8? 'enabled' : 'disabled')).toggleClass('ui-state-active', grid8) .on('click', function() { grid8 = ! grid8; grid8px.html(fm.i18n(grid8? 'enabled' : 'disabled')).toggleClass('ui-state-active', grid8); setStep8(); }) .button(), setStep8 = function() { var step = grid8? 8 : 1; jQuery.each([width, height, offsetX, offsetY, pointX, pointY], function() { this.attr('step', step); }); if (grid8) { width.val(round(width.val())); height.val(round(height.val())); offsetX.val(round(offsetX.val())); offsetY.val(round(offsetY.val())); pointX.val(round(pointX.val())); pointY.val(round(pointY.val())); if (uiresize.is(':visible')) { resize.updateView(width.val(), height.val()); } else if (uicrop.is(':visible')) { crop.updateView(); } } }, setuprimg = function() { var r_scale, fail = function() { bg.parent().hide(); pallet.hide(); }; r_scale = Math.min(pwidth, pheight) / Math.sqrt(Math.pow(owidth, 2) + Math.pow(oheight, 2)); rwidth = Math.ceil(owidth * r_scale); rheight = Math.ceil(oheight * r_scale); imgr.width(rwidth) .height(rheight) .css('margin-top', (pheight-rheight)/2 + 'px') .css('margin-left', (pwidth-rwidth)/2 + 'px'); if (imgr.is(':visible') && bg.is(':visible')) { if (file.mime !== 'image/png') { preview.css('backgroundColor', bg.val()); pickimg = jQuery('<img>'); if (fm.isCORS) { pickimg.attr('crossorigin', 'use-credentials'); } pickimg.on('load', function() { if (pickcanv && pickcanv.width !== rwidth) { setColorData(); } }) .on('error', fail) .attr('src', canvSrc); } else { fail(); } } }, setupimg = function() { resize.updateView(owidth, oheight); setuprimg(); basec .width(img.width()) .height(img.height()); imgc .width(img.width()) .height(img.height()); crop.updateView(); jpgCalc(); }, setColorData = function() { if (pickctx) { var n, w, h, r, g, b, a, s, l, hsl, hue, data, scale, tx1, tx2, ty1, ty2, rgb, domi = {}, domic = [], domiv, palc, rgbToHsl = function (r, g, b) { var h, s, l, max = Math.max(Math.max(r, g), b), min = Math.min(Math.min(r, g), b); // Hue, 0 ~ 359 if (max === min) { h = 0; } else if (r === max) { h = ((g - b) / (max - min) * 60 + 360) % 360; } else if (g === max) { h = (b - r) / (max - min) * 60 + 120; } else if (b === max) { h = (r - g) / (max - min) * 60 + 240; } // Saturation, 0 ~ 1 s = (max - min) / max; // Lightness, 0 ~ 1 l = (r * 0.3 + g * 0.59 + b * 0.11) / 255; return [h, s, l, 'hsl']; }, rgbRound = function(c) { return Math.round(c / 8) * 8; }; calc: try { w = pickcanv.width = imgr.width(); h = pickcanv.height = imgr.height(); scale = w / owidth; pickctx.scale(scale, scale); pickctx.drawImage(pickimg.get(0), 0, 0); data = pickctx.getImageData(0, 0, w, h).data; // Range to detect the dominant color tx1 = w * 0.1; tx2 = w * 0.9; ty1 = h * 0.1; ty2 = h * 0.9; for (var y = 0; y < h - 1; y++) { for (var x = 0; x < w - 1; x++) { n = x * 4 + y * w * 4; // RGB r = data[n]; g = data[n + 1]; b = data[n + 2]; a = data[n + 3]; // check alpha ch if (a !== 255) { bg.parent().hide(); pallet.hide(); break calc; } // HSL hsl = rgbToHsl(r, g, b); hue = Math.round(hsl[0]); s = Math.round(hsl[1] * 100); l = Math.round(hsl[2] * 100); if (! pickc[x]) { pickc[x] = {}; } // set pickc pickc[x][y] = [r, g, b, hue, s, l]; // detect the dominant color if ((x < tx1 || x > tx2) && (y < ty1 || y > ty2)) { rgb = rgbRound(r) + ',' + rgbRound(g) + ',' + rgbRound(b); if (! domi[rgb]) { domi[rgb] = 1; } else { ++domi[rgb]; } } } } if (! pallet.children(':first').length) { palc = 1; jQuery.each(domi, function(c, v) { domic.push({c: c, v: v}); }); jQuery.each(domic.sort(function(a, b) { return (a.v > b.v)? -1 : 1; }), function() { if (this.v < 2 || palc > 10) { return false; } pallet.append(jQuery('<span style="width:20px;height:20px;display:inline-block;background-color:rgb('+this.c+');">')); ++palc; }); } } catch(e) { picker.hide(); pallet.hide(); } } }, setupPicker = function() { try { pickcanv = document.createElement('canvas'); pickctx = pickcanv.getContext('2d'); } catch(e) { picker.hide(); pallet.hide(); } }, setupPreset = function() { preset.on('click', 'span.elfinder-resize-preset', function() { var btn = jQuery(this), w = btn.data('s')[0], h = btn.data('s')[1], r = owidth / oheight; btn.data('s', [h, w]).text(h + 'x' + w); if (owidth > w || oheight > h) { if (owidth <= w) { w = round(h * r); } else if (oheight <= h) { h = round(w / r); } else { if (owidth - w > oheight - h) { h = round(w / r); } else { w = round(h * r); } } } else { w = owidth; h = oheight; } width.val(w); height.val(h); resize.updateView(w, h); jpgCalc(); }); presetc.on('click', 'span.elfinder-resize-preset', function() { var btn = jQuery(this), w = btn.data('s')[0], h = btn.data('s')[1], x = pointX.val(), y = pointY.val(); btn.data('s', [h, w]).text(h + 'x' + w); if (owidth >= w && oheight >= h) { if (owidth - w - x < 0) { x = owidth - w; } if (oheight - h - y < 0) { y = oheight - h; } pointX.val(x); pointY.val(y); offsetX.val(w); offsetY.val(h); crop.updateView(); jpgCalc(); } }); presetc.children('span.elfinder-resize-preset').each(function() { var btn = jQuery(this), w = btn.data('s')[0], h = btn.data('s')[1]; btn[(owidth >= w && oheight >= h)? 'show' : 'hide'](); }); }, dimreq = null, inited = false, setdim = function(dim) { var rfile = fm.file(file.hash); rfile.width = dim[0]; rfile.height = dim[1]; }, init = function() { var elm, memSize, r_scale, imgRatio; if (inited) { return; } inited = true; dimreq && dimreq.state && dimreq.state() === 'pending' && dimreq.reject(); // check lossless rotete if (fm.api >= 2.1030) { if (losslessRotate === 0) { fm.request({ data: { cmd : 'resize', target : file.hash, degree : 0, mode : 'rotate' }, preventDefault : true }).done(function(data) { losslessRotate = data.losslessRotate? 1 : -1; if (losslessRotate === 1 && (degree.val() % 90 === 0)) { uirotate.children('div.elfinder-resize-quality').hide(); } }).fail(function() { losslessRotate = -1; }); } } else { losslessRotate = -1; } elm = img.get(0); memSize = file.width && file.height? {w: file.width, h: file.height} : (elm.naturalWidth? null : {w: img.width(), h: img.height()}); memSize && img.removeAttr('width').removeAttr('height'); owidth = file.width || elm.naturalWidth || elm.width || img.width(); oheight = file.height || elm.naturalHeight || elm.height || img.height(); if (!file.width || !file.height) { setdim([owidth, oheight]); } memSize && img.width(memSize.w).height(memSize.h); dMinBtn.show(); imgRatio = oheight / owidth; if (imgRatio < 1 && preview.height() > preview.width() * imgRatio) { preview.height(preview.width() * imgRatio); } if (preview.height() > img.height() + 20) { preview.height(img.height() + 20); } pheight = preview.height() - (rhandle.outerHeight() - rhandle.height()); spinner.remove(); ratio = owidth/oheight; rhandle.append(img.show()).show(); width.val(owidth); height.val(oheight); setupPicker(); setupPreset(); setupimg(); uitype[ctrgrup]('enable'); control.find('input,select').prop('disabled', false) .filter(':text').on('keydown', function(e) { var cOpts; if (e.keyCode == jQuery.ui.keyCode.ENTER) { e.stopPropagation(); e.preventDefault(); cOpts = { title : jQuery('input:checked', uitype).val(), text : 'confirmReq', accept : { label : 'btnApply', callback : function() { save(); } }, cancel : { label : 'btnCancel', callback : function(){ jQuery(this).trigger('focus'); } } }; if (useSaveAs) { cOpts['buttons'] = [{ label : 'btnSaveAs', callback : function() { requestAnimationFrame(saveAs); } }]; } fm.confirm(cOpts); return; } }) .on('keyup', function() { var $this = jQuery(this); if (! $this.hasClass('elfinder-resize-bg')) { requestAnimationFrame(function() { $this.val($this.val().replace(/[^0-9]/g, '')); }); } }) .filter(':first'); setStep8(); !fm.UA.Mobile && width.trigger('focus'); resizable(); }, img = jQuery('<img/>') .on('load', init) .on('error', function() { spinner.text('Unable to load image').css('background', 'transparent'); }), basec = jQuery('<div/>'), imgc = jQuery('<img/>'), coverc = jQuery('<div/>'), imgr = jQuery('<img class="elfinder-resize-imgrotate" />'), round = function(v, max) { v = grid8? Math.round(v/8)*8 : Math.round(v); v = Math.max(0, v); if (max && v > max) { v = grid8? Math.floor(max/8)*8 : max; } return v; }, resetView = function() { width.val(owidth); height.val(oheight); resize.updateView(owidth, oheight); pointX.val(0); pointY.val(0); offsetX.val(owidth); offsetY.val(oheight); crop.updateView(); jpgCalc(); }, resize = { update : function() { width.val(round(img.width()/prop)); height.val(round(img.height()/prop)); jpgCalc(); }, updateView : function(w, h) { if (w > pwidth || h > pheight) { if (w / pwidth > h / pheight) { prop = pwidth / w; img.width(pwidth).height(round(h*prop)); } else { prop = pheight / h; img.height(pheight).width(round(w*prop)); } } else { img.width(round(w)).height(round(h)); } prop = img.width()/w; uiprop.text('1 : '+(1/prop).toFixed(2)); resize.updateHandle(); }, updateHandle : function() { rhandle.width(img.width()).height(img.height()); }, fixHeight : function() { var w, h; if (cratio) { w = width.val(); h = round(w/ratio); resize.updateView(w, h); height.val(h); } } }, crop = { update : function(change) { pointX.val(round(((rhandlec.data('x')||rhandlec.position().left))/prop, owidth)); pointY.val(round(((rhandlec.data('y')||rhandlec.position().top))/prop, oheight)); if (change !== 'xy') { offsetX.val(round((rhandlec.data('w')||rhandlec.width())/prop, owidth - pointX.val())); offsetY.val(round((rhandlec.data('h')||rhandlec.height())/prop, oheight - pointY.val())); } jpgCalc(); }, updateView : function(change) { var r, x, y, w, h; pointX.val(round(pointX.val(), owidth - (grid8? 8 : 1))); pointY.val(round(pointY.val(), oheight - (grid8? 8 : 1))); offsetX.val(round(offsetX.val(), owidth - pointX.val())); offsetY.val(round(offsetY.val(), oheight - pointY.val())); if (cratioc) { r = coverc.width() / coverc.height(); if (change === 'w') { offsetY.val(round(parseInt(offsetX.val()) / r)); } else if (change === 'h') { offsetX.val(round(parseInt(offsetY.val()) * r)); } } x = Math.round(parseInt(pointX.val()) * prop); y = Math.round(parseInt(pointY.val()) * prop); if (change !== 'xy') { w = Math.round(parseInt(offsetX.val()) * prop); h = Math.round(parseInt(offsetY.val()) * prop); } else { w = rhandlec.data('w'); h = rhandlec.data('h'); } rhandlec.data({x: x, y: y, w: w, h: h}) .width(w) .height(h) .css({left: x, top: y}); coverc.width(w) .height(h); }, resize_update : function(e, ui) { rhandlec.data({x: ui.position.left, y: ui.position.top, w: ui.size.width, h: ui.size.height}); crop.update(); crop.updateView(); }, drag_update : function(e, ui) { rhandlec.data({x: ui.position.left, y: ui.position.top}); crop.update('xy'); } }, rotate = { mouseStartAngle : 0, imageStartAngle : 0, imageBeingRotated : false, setQuality : function() { uirotate.children('div.elfinder-resize-quality')[(losslessRotate > 0 && (degree.val() % 90) === 0)? 'hide' : 'show'](); }, update : function(value, animate) { if (typeof value == 'undefined') { rdegree = value = parseInt(degree.val()); } if (typeof animate == 'undefined') { animate = true; } if (! animate || fm.UA.Opera || fm.UA.ltIE8) { imgr.rotate(value); } else { imgr.animate({rotate: value + 'deg'}); } value = value % 360; if (value < 0) { value += 360; } degree.val(parseInt(value)); uidegslider.slider('value', degree.val()); rotate.setQuality(); }, execute : function ( e ) { if ( !rotate.imageBeingRotated ) return; var imageCentre = rotate.getCenter( imgr ); var ev = e.originalEvent.touches? e.originalEvent.touches[0] : e; var mouseXFromCentre = ev.pageX - imageCentre[0]; var mouseYFromCentre = ev.pageY - imageCentre[1]; var mouseAngle = Math.atan2( mouseYFromCentre, mouseXFromCentre ); var rotateAngle = mouseAngle - rotate.mouseStartAngle + rotate.imageStartAngle; rotateAngle = Math.round(parseFloat(rotateAngle) * 180 / Math.PI); if ( e.shiftKey ) { rotateAngle = Math.round((rotateAngle + 6)/15) * 15; } imgr.rotate(rotateAngle); rotateAngle = rotateAngle % 360; if (rotateAngle < 0) { rotateAngle += 360; } degree.val(rotateAngle); uidegslider.slider('value', degree.val()); rotate.setQuality(); return false; }, start : function ( e ) { if (imgr.hasClass('elfinder-resize-picking')) { return; } opStart(); rotate.imageBeingRotated = true; var imageCentre = rotate.getCenter( imgr ); var ev = e.originalEvent.touches? e.originalEvent.touches[0] : e; var mouseStartXFromCentre = ev.pageX - imageCentre[0]; var mouseStartYFromCentre = ev.pageY - imageCentre[1]; rotate.mouseStartAngle = Math.atan2( mouseStartYFromCentre, mouseStartXFromCentre ); rotate.imageStartAngle = parseFloat(imgr.rotate()) * Math.PI / 180.0; jQuery(document).on('mousemove', rotate.execute); imgr.on('touchmove', rotate.execute); return false; }, stop : function ( e ) { if ( !rotate.imageBeingRotated ) return; jQuery(document).off('mousemove', rotate.execute); imgr.off('touchmove', rotate.execute); requestAnimationFrame(function() { rotate.imageBeingRotated = false; }); opStop(); return false; }, getCenter : function ( image ) { var currentRotation = imgr.rotate(); imgr.rotate(0); var imageOffset = imgr.offset(); var imageCentreX = imageOffset.left + imgr.width() / 2; var imageCentreY = imageOffset.top + imgr.height() / 2; imgr.rotate(currentRotation); return Array( imageCentreX, imageCentreY ); } }, resizable = function(destroy) { if (destroy) { rhandle.filter(':ui-resizable').resizable('destroy'); rhandle.hide(); } else { rhandle.show(); rhandle.resizable({ alsoResize : img, aspectRatio : cratio, resize : resize.update, start : opStart, stop : function(e) { resize.fixHeight; resize.updateView(width.val(), height.val()); opStop(); } }); dinit(); } }, croppable = function(destroy) { if (destroy) { rhandlec.filter(':ui-resizable').resizable('destroy') .filter(':ui-draggable').draggable('destroy'); basec.hide(); } else { basec.show(); rhandlec .resizable({ containment : basec, aspectRatio : cratioc, resize : crop.resize_update, start : opStart, stop : opStop, handles : 'all' }) .draggable({ handle : coverc, containment : imgc, drag : crop.drag_update, start : opStart, stop : function() { crop.updateView('xy'); opStop(); } }); dinit(); crop.update(); } }, rotateable = function(destroy) { if (destroy) { imgr.hide(); } else { imgr.show(); dinit(); } }, checkVals = function() { var w, h, x, y, d, q, b = ''; if (mode == 'resize') { w = parseInt(width.val()) || 0; h = parseInt(height.val()) || 0; } else if (mode == 'crop') { w = parseInt(offsetX.val()) || 0; h = parseInt(offsetY.val()) || 0; x = parseInt(pointX.val()) || 0; y = parseInt(pointY.val()) || 0; } else if (mode == 'rotate') { w = owidth; h = oheight; d = parseInt(degree.val()) || 0; if (d < 0 || d > 360) { fm.error('Invalid rotate degree'); return false; } if (d == 0 || d == 360) { fm.error('errResizeNoChange'); return false; } b = bg.val(); } q = quality? parseInt(quality.val()) : 0; if (mode != 'rotate') { if (w <= 0 || h <= 0) { fm.error('Invalid image size'); return false; } if (w == owidth && h == oheight) { fm.error('errResizeNoChange'); return false; } } return {w: w, h: h, x: x, y: y, d: d, q: q, b: b}; }, save = function() { var vals; if (vals = checkVals()) { dialog.elfinderdialog('close'); self.resizeRequest({ target : file.hash, width : vals.w, height : vals.h, x : vals.x, y : vals.y, degree : vals.d, quality: vals.q, bg : vals.b, mode : mode }, file, dfrd); } }, saveAs = function() { var fail = function() { dialogs.addClass(clsediting).fadeIn(function() { base.addClass(clactive); }); fm.disable(); }, make = function() { self.mime = file.mime; self.prefix = file.name.replace(/ \d+(\.[^.]+)?$/, '$1'); self.requestCmd = 'mkfile'; self.nextAction = {}; self.data = {target : file.phash}; jQuery.proxy(fm.res('mixin', 'make'), self)() .done(function(data) { var hash, dfd; if (data.added && data.added.length) { hash = data.added[0].hash; dfd = fm.api < 2.1032? fm.url(file.hash, { async: true, temporary: true }) : null; jQuery.when(dfd).done(function(url) { fm.request({ options : {type : 'post'}, data : { cmd : 'put', target : hash, encoding: dfd? 'scheme' : 'hash', content : dfd? fm.convAbsUrl(url) : file.hash }, notify : {type : 'copy', cnt : 1}, syncOnFail : true }) .fail(fail) .done(function(data) { data = fm.normalize(data); fm.updateCache(data); file = fm.file(hash); data.changed && data.changed.length && fm.change(data); base.show().find('.elfinder-dialog-title').html(fm.escape(file.name)); save(); dialogs.fadeIn(); }); }).fail(fail); } else { fail(); } }) .fail(fail) .always(function() { delete self.mime; delete self.prefix; delete self.nextAction; delete self.data; }); fm.trigger('unselectfiles', { files: [ file.hash ] }); }, reqOpen = null, dialogs; if (checkVals()) { dialogs = fmnode.children('.' + self.dialogClass + ':visible').removeClass(clsediting).fadeOut(); base.removeClass(clactive); fm.enable(); if (fm.searchStatus.state < 2 && file.phash !== fm.cwd().hash) { reqOpen = fm.exec('open', [file.phash], {thash: file.phash}); } jQuery.when([reqOpen]).done(function() { reqOpen? fm.one('cwdrender', make) : make(); }).fail(fail); } }, buttons = {}, hline = 'elfinder-resize-handle-hline', vline = 'elfinder-resize-handle-vline', rpoint = 'elfinder-resize-handle-point', src = fm.openUrl(file.hash), canvSrc = fm.openUrl(file.hash, !fm.isSameOrigin(src)), sizeImg = quality? jQuery('<img>').attr('crossorigin', fm.isCORS? 'use-credentials' : '').attr('src', canvSrc).on('load', function() { try { var canv = document.createElement('canvas'); sizeImg.data('canvas', canv).data('ctx', canv.getContext('2d')); jpgCalc(); } catch(e) { sizeImg.removeData('canvas').removeData('ctx'); } }) : null, jpgCalc = function() { control.find('input.elfinder-resize-quality:visible').trigger('change'); }, dinit = function(e) { if (base.hasClass('elfinder-dialog-minimized') || base.is(':hidden')) { return; } preset.hide(); presetc.hide(); var win = fm.options.dialogContained? fmnode : jQuery(window), winH = win.height(), winW = win.width(), presW = 'auto', presIn = true, dw, ctrW, prvW; base.width(Math.min(dialogWidth, winW - 30)); preview.attr('style', ''); if (owidth && oheight) { pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width()); pheight = preview.height() - (rhandle.outerHeight() - rhandle.height()); resize.updateView(owidth, oheight); } ctrW = dialog.find('div.elfinder-resize-control').width(); prvW = preview.width(); dw = dialog.width() - 20; if (prvW > dw) { preview.width(dw); presIn = false; } else if ((dw - prvW) < ctrW) { if (winW > winH) { preview.width(dw - ctrW - 20); } else { preview.css({ float: 'none', marginLeft: 'auto', marginRight: 'auto'}); presIn = false; } } if (presIn) { presW = ctrW; } pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width()); if (fmnode.hasClass('elfinder-fullscreen')) { if (base.height() > winH) { winH -= 2; preview.height(winH - base.height() + preview.height()); base.css('top', 0 - fmnode.offset().top); } } else { winH -= 30; (preview.height() > winH) && preview.height(winH); } pheight = preview.height() - (rhandle.outerHeight() - rhandle.height()); if (owidth && oheight) { setupimg(); } if (img.height() && preview.height() > img.height() + 20) { preview.height(img.height() + 20); pheight = preview.height() - (rhandle.outerHeight() - rhandle.height()); setuprimg(); } preset.css('width', presW).show(); presetc.css('width', presW).show(); if (!presetc.children('span.elfinder-resize-preset:visible').length) { presetc.hide(); } }, preset = (function() { var sets = jQuery('<fieldset class="elfinder-resize-preset-container">').append(jQuery('<legend>').html(fm.i18n('presets'))).hide(), hasC; jQuery.each(presetSize, function(i, s) { if (s.length === 2) { hasC = true; sets.append(jQuery('<span class="elfinder-resize-preset"/>') .data('s', s) .text(s[0]+'x'+s[1]) .button() ); } }); if (!hasC) { return jQuery(); } else { return sets; } })(), presetc = preset.clone(true), useSaveAs = fm.uploadMimeCheck(file.mime, file.phash), dMinBtn, base; uiresize.append( jQuery(row).append(jQuery(label).text(fm.i18n('width')), width), jQuery(row).append(jQuery(label).text(fm.i18n('height')), height, jQuery('<div class="elfinder-resize-whctrls">').append(constr, reset)), (quality? jQuery(row).append(jQuery(label).text(fm.i18n('quality')), quality, jQuery('<span/>')) : jQuery()), (isJpeg? jQuery(row).append(jQuery(label).text(fm.i18n('8pxgrid')).addClass('elfinder-resize-grid8'), grid8px) : jQuery()), jQuery(row).append(jQuery(label).text(fm.i18n('scale')), uiprop), jQuery(row).append(preset) ); if (api2) { uicrop.append( jQuery(row).append(jQuery(label).text('X'), pointX), jQuery(row).append(jQuery(label).text('Y')).append(pointY), jQuery(row).append(jQuery(label).text(fm.i18n('width')), offsetX), jQuery(row).append(jQuery(label).text(fm.i18n('height')), offsetY, jQuery('<div class="elfinder-resize-whctrls">').append(constrc, reset.clone(true))), (quality? jQuery(row).append(jQuery(label).text(fm.i18n('quality')), quality.clone(true), jQuery('<span/>')) : jQuery()), (isJpeg? jQuery(row).append(jQuery(label).text(fm.i18n('8pxgrid')).addClass('elfinder-resize-grid8')) : jQuery()), jQuery(row).append(presetc) ); uirotate.append( jQuery(row).addClass('elfinder-resize-degree').append( jQuery(label).text(fm.i18n('rotate')), degree, jQuery('<span/>').text(fm.i18n('degree')), jQuery('<div/>').append(uideg270, uideg90)[ctrgrup]() ), jQuery(row).css('height', '20px').append(uidegslider), ((quality)? jQuery(row)[losslessRotate < 1? 'show' : 'hide']().addClass('elfinder-resize-quality').append( jQuery(label).text(fm.i18n('quality')), quality.clone(true), jQuery('<span/>')) : jQuery() ), jQuery(row).append(jQuery(label).text(fm.i18n('bgcolor')), bg, picker, reseter), jQuery(row).css('height', '20px').append(pallet) ); uideg270.on('click', function() { rdegree = rdegree - 90; rotate.update(rdegree); }); uideg90.on('click', function(){ rdegree = rdegree + 90; rotate.update(rdegree); }); } dialog.append(uitype).on('resize', function(e){ e.stopPropagation(); }); if (api2) { control.append(/*jQuery(row), */uiresize, uicrop.hide(), uirotate.hide()); } else { control.append(/*jQuery(row), */uiresize); } rhandle.append('<div class="'+hline+' '+hline+'-top"/>', '<div class="'+hline+' '+hline+'-bottom"/>', '<div class="'+vline+' '+vline+'-left"/>', '<div class="'+vline+' '+vline+'-right"/>', '<div class="'+rpoint+' '+rpoint+'-e"/>', '<div class="'+rpoint+' '+rpoint+'-se"/>', '<div class="'+rpoint+' '+rpoint+'-s"/>'); preview.append(spinner).append(rhandle.hide()).append(img.hide()); if (api2) { rhandlec.css('position', 'absolute') .append('<div class="'+hline+' '+hline+'-top"/>', '<div class="'+hline+' '+hline+'-bottom"/>', '<div class="'+vline+' '+vline+'-left"/>', '<div class="'+vline+' '+vline+'-right"/>', '<div class="'+rpoint+' '+rpoint+'-n"/>', '<div class="'+rpoint+' '+rpoint+'-e"/>', '<div class="'+rpoint+' '+rpoint+'-s"/>', '<div class="'+rpoint+' '+rpoint+'-w"/>', '<div class="'+rpoint+' '+rpoint+'-ne"/>', '<div class="'+rpoint+' '+rpoint+'-se"/>', '<div class="'+rpoint+' '+rpoint+'-sw"/>', '<div class="'+rpoint+' '+rpoint+'-nw"/>'); preview.append(basec.css('position', 'absolute').hide().append(imgc, rhandlec.append(coverc))); preview.append(imgr.hide()); } preview.css('overflow', 'hidden'); dialog.append(preview, control); buttons[fm.i18n('btnApply')] = save; if (useSaveAs) { buttons[fm.i18n('btnSaveAs')] = function() { requestAnimationFrame(saveAs); }; } buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); }; dialog.find('input,button').addClass('elfinder-tabstop'); base = self.fmDialog(dialog, { title : fm.escape(file.name), width : dialogWidth, resizable : false, buttons : buttons, open : function() { var substituteImg = (fm.option('substituteImg', file.hash) && file.size > options.dimSubImgSize)? true : false, hasSize = (file.width && file.height)? true : false; dialog.parent().css('overflow', 'hidden'); dMinBtn = base.find('.ui-dialog-titlebar .elfinder-titlebar-minimize').hide(); fm.bind('resize', dinit); img.attr('src', src); imgc.attr('src', src); imgr.attr('src', src); if (api2) { imgr.on('mousedown touchstart', rotate.start) .on('touchend', rotate.stop); base.on('mouseup', rotate.stop); } if (hasSize && !substituteImg) { return init(); } if (file.size > (options.getDimThreshold || 0)) { dimreq = fm.request({ data : {cmd : 'dim', target : file.hash, substitute : (substituteImg? 400 : '')}, preventDefault : true }) .done(function(data) { if (data.dim) { var dim = data.dim.split('x'); file.width = dim[0]; file.height = dim[1]; setdim(dim); if (data.url) { img.attr('src', data.url); imgc.attr('src', data.url); imgr.attr('src', data.url); } return init(); } }); } else if (hasSize) { return init(); } }, close : function() { if (api2) { imgr.off('mousedown touchstart', rotate.start) .off('touchend', rotate.stop); jQuery(document).off('mouseup', rotate.stop); } fm.unbind('resize', dinit); jQuery(this).elfinderdialog('destroy'); }, resize : function(e, data) { if (data && data.minimize === 'off') { dinit(); } } }).attr('id', id).closest('.ui-dialog').addClass(clsediting); // for IE < 9 dialog mising at open second+ time. if (fm.UA.ltIE8) { jQuery('.elfinder-dialog').css('filter', ''); } coverc.css({ 'opacity': 0.2, 'background-color': '#fff', 'position': 'absolute'}), rhandlec.css('cursor', 'move'); rhandlec.find('.elfinder-resize-handle-point').css({ 'background-color' : '#fff', 'opacity': 0.5, 'border-color':'#000' }); if (! api2) { uitype.find('.api2').remove(); } control.find('input,select').prop('disabled', true); control.find('input.elfinder-resize-quality') .next('span').addClass('elfinder-resize-jpgsize').attr('title', fm.i18n('roughFileSize')); }, id, dialog ; if (!files.length || files[0].mime.indexOf('image/') === -1) { return dfrd.reject(); } id = 'resize-'+fm.namespace+'-'+files[0].hash; dialog = fmnode.find('#'+id); if (dialog.length) { dialog.elfinderdialog('toTop'); return dfrd.resolve(); } open(files[0], id); return dfrd; }; }; (function ($) { var findProperty = function (styleObject, styleArgs) { var i = 0 ; for( i in styleArgs) { if (typeof styleObject[styleArgs[i]] != 'undefined') return styleArgs[i]; } styleObject[styleArgs[i]] = ''; return styleArgs[i]; }; jQuery.cssHooks.rotate = { get: function(elem, computed, extra) { return jQuery(elem).rotate(); }, set: function(elem, value) { jQuery(elem).rotate(value); return value; } }; jQuery.cssHooks.transform = { get: function(elem, computed, extra) { var name = findProperty( elem.style , ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] ); return elem.style[name]; }, set: function(elem, value) { var name = findProperty( elem.style , ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] ); elem.style[name] = value; return value; } }; jQuery.fn.rotate = function(val) { var r; if (typeof val == 'undefined') { if (!!window.opera) { r = this.css('transform').match(/rotate\((.*?)\)/); return ( r && r[1])? Math.round(parseFloat(r[1]) * 180 / Math.PI) : 0; } else { r = this.css('transform').match(/rotate\((.*?)\)/); return ( r && r[1])? parseInt(r[1]) : 0; } } this.css('transform', this.css('transform').replace(/none|rotate\(.*?\)/, '') + 'rotate(' + parseInt(val) + 'deg)'); return this; }; jQuery.fx.step.rotate = function(fx) { if ( fx.state == 0 ) { fx.start = jQuery(fx.elem).rotate(); fx.now = fx.start; } jQuery(fx.elem).rotate(fx.now); }; if (typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined") { // IE & IE<9 var GetAbsoluteXY = function(element) { var pnode = element; var x = pnode.offsetLeft; var y = pnode.offsetTop; while ( pnode.offsetParent ) { pnode = pnode.offsetParent; if (pnode != document.body && pnode.currentStyle['position'] != 'static') { break; } if (pnode != document.body && pnode != document.documentElement) { x -= pnode.scrollLeft; y -= pnode.scrollTop; } x += pnode.offsetLeft; y += pnode.offsetTop; } return { x: x, y: y }; }; var StaticToAbsolute = function (element) { if ( element.currentStyle['position'] != 'static') { return ; } var xy = GetAbsoluteXY(element); element.style.position = 'absolute' ; element.style.left = xy.x + 'px'; element.style.top = xy.y + 'px'; }; var IETransform = function(element,transform){ var r; var m11 = 1; var m12 = 1; var m21 = 1; var m22 = 1; if (typeof element.style['msTransform'] != 'undefined'){ return true; } StaticToAbsolute(element); r = transform.match(/rotate\((.*?)\)/); var rotate = ( r && r[1]) ? parseInt(r[1]) : 0; rotate = rotate % 360; if (rotate < 0) rotate = 360 + rotate; var radian= rotate * Math.PI / 180; var cosX =Math.cos(radian); var sinY =Math.sin(radian); m11 *= cosX; m12 *= -sinY; m21 *= sinY; m22 *= cosX; element.style.filter = (element.style.filter || '').replace(/progid:DXImageTransform\.Microsoft\.Matrix\([^)]*\)/, "" ) + ("progid:DXImageTransform.Microsoft.Matrix(" + "M11=" + m11 + ",M12=" + m12 + ",M21=" + m21 + ",M22=" + m22 + ",FilterType='bilinear',sizingMethod='auto expand')") ; var ow = parseInt(element.style.width || element.width || 0 ); var oh = parseInt(element.style.height || element.height || 0 ); radian = rotate * Math.PI / 180; var absCosX =Math.abs(Math.cos(radian)); var absSinY =Math.abs(Math.sin(radian)); var dx = (ow - (ow * absCosX + oh * absSinY)) / 2; var dy = (oh - (ow * absSinY + oh * absCosX)) / 2; element.style.marginLeft = Math.floor(dx) + "px"; element.style.marginTop = Math.floor(dy) + "px"; return(true); }; var transform_set = jQuery.cssHooks.transform.set; jQuery.cssHooks.transform.set = function(elem, value) { transform_set.apply(this, [elem, value] ); IETransform(elem,value); return value; }; } })(jQuery); /* * File: /js/commands/restore.js */ /** * @class elFinder command "restore" * Restore items from the trash * * @author Naoki Sawada **/ (elFinder.prototype.commands.restore = function() { "use strict"; var self = this, fm = this.fm, fakeCnt = 0, getFilesRecursively = function(files) { var dfd = jQuery.Deferred(), dirs = [], results = [], reqs = [], phashes = [], getFile; dfd._xhrReject = function() { jQuery.each(reqs, function() { this && this.reject && this.reject(); }); getFile && getFile._xhrReject(); }; jQuery.each(files, function(i, f) { f.mime === 'directory'? dirs.push(f) : results.push(f); }); if (dirs.length) { jQuery.each(dirs, function(i, d) { reqs.push(fm.request({ data : {cmd : 'open', target : d.hash}, preventDefault : true, asNotOpen : true })); phashes[i] = d.hash; }); jQuery.when.apply($, reqs).fail(function() { dfd.reject(); }).done(function() { var items = []; jQuery.each(arguments, function(i, r) { var files; if (r.files) { if (r.files.length) { items = items.concat(r.files); } else { items.push({ hash: 'fakefile_' + (fakeCnt++), phash: phashes[i], mime: 'fakefile', name: 'fakefile', ts: 0 }); } } }); fm.cache(items); getFile = getFilesRecursively(items).done(function(res) { results = results.concat(res); dfd.resolve(results); }); }); } else { dfd.resolve(results); } return dfd; }, restore = function(dfrd, files, targets, ops) { var rHashes = {}, others = [], found = false, dirs = [], opts = ops || {}, id = +new Date(), tm, getFile; fm.lockfiles({files : targets}); dirs = jQuery.map(files, function(f) { return f.mime === 'directory'? f.hash : null; }); dfrd.done(function() { dirs && fm.exec('rm', dirs, {forceRm : true, quiet : true}); }).always(function() { fm.unlockfiles({files : targets}); }); tm = setTimeout(function() { fm.notify({type : 'search', id : id, cnt : 1, hideCnt : true, cancel : function() { getFile && getFile._xhrReject(); dfrd.reject(); }}); }, fm.notifyDelay); fakeCnt = 0; getFile = getFilesRecursively(files).always(function() { tm && clearTimeout(tm); fm.notify({type : 'search', id: id, cnt : -1, hideCnt : true}); }).fail(function() { dfrd.reject('errRestore', 'errFileNotFound'); }).done(function(res) { var errFolderNotfound = ['errRestore', 'errFolderNotFound'], dirTop = ''; if (res.length) { jQuery.each(res, function(i, f) { var phash = f.phash, pfile, srcRoot, tPath; while(phash) { if (srcRoot = fm.trashes[phash]) { if (! rHashes[srcRoot]) { if (found) { // Keep items of other trash others.push(f.hash); return null; // continue jQuery.each } rHashes[srcRoot] = {}; found = true; } tPath = fm.path(f.hash).substr(fm.path(phash).length).replace(/\\/g, '/'); tPath = tPath.replace(/\/[^\/]+?$/, ''); if (tPath === '') { tPath = '/'; } if (!rHashes[srcRoot][tPath]) { rHashes[srcRoot][tPath] = []; } if (f.mime === 'fakefile') { fm.updateCache({removed:[f.hash]}); } else { rHashes[srcRoot][tPath].push(f.hash); } if (!dirTop || dirTop.length > tPath.length) { dirTop = tPath; } break; } // Go up one level for next check pfile = fm.file(phash); if (!pfile) { phash = false; // Detection method for search results jQuery.each(fm.trashes, function(ph) { var file = fm.file(ph), filePath = fm.path(ph); if ((!file.volumeid || f.hash.indexOf(file.volumeid) === 0) && fm.path(f.hash).indexOf(filePath) === 0) { phash = ph; return false; } }); } else { phash = pfile.phash; } } }); if (found) { jQuery.each(rHashes, function(src, dsts) { var dirs = Object.keys(dsts), cnt = dirs.length; fm.request({ data : {cmd : 'mkdir', target : src, dirs : dirs}, notify : {type : 'chkdir', cnt : cnt}, preventFail : true }).fail(function(error) { dfrd.reject(error); fm.unlockfiles({files : targets}); }).done(function(data) { var cmdPaste, hashes; if (hashes = data.hashes) { cmdPaste = fm.getCommand('paste'); if (cmdPaste) { // wait until file cache made fm.one('mkdirdone', function() { var hasErr = false; jQuery.each(dsts, function(dir, files) { if (hashes[dir]) { if (files.length) { if (fm.file(hashes[dir])) { fm.clipboard(files, true); fm.exec('paste', [ hashes[dir] ], {_cmd : 'restore', noToast : (opts.noToast || dir !== dirTop)}) .done(function(data) { if (data && (data.error || data.warning)) { hasErr = true; } }) .fail(function() { hasErr = true; }) .always(function() { if (--cnt < 1) { dfrd[hasErr? 'reject' : 'resolve'](); if (others.length) { // Restore items of other trash fm.exec('restore', others); } } }); } else { dfrd.reject(errFolderNotfound); } } else { if (--cnt < 1) { dfrd.resolve(); if (others.length) { // Restore items of other trash fm.exec('restore', others); } } } } }); }); } else { dfrd.reject(['errRestore', 'errCmdNoSupport', '(paste)']); } } else { dfrd.reject(errFolderNotfound); } }); }); } else { dfrd.reject(errFolderNotfound); } } else { dfrd.reject('errFileNotFound'); dirs && fm.exec('rm', dirs, {forceRm : true, quiet : true}); } }); }; // for to be able to overwrite this.restore = restore; this.linkedCmds = ['copy', 'paste', 'mkdir', 'rm']; this.updateOnSelect = false; this.init = function() { // re-assign for extended command self = this; fm = this.fm; }; this.getstate = function(sel, e) { sel = sel || fm.selected(); return sel.length && jQuery.grep(sel, function(h) {var f = fm.file(h); return f && ! f.locked && ! fm.isRoot(f)? true : false; }).length == sel.length ? 0 : -1; }; this.exec = function(hashes, opts) { var dfrd = jQuery.Deferred() .fail(function(error) { error && fm.error(error); }), files = self.files(hashes); if (! files.length) { return dfrd.reject(); } jQuery.each(files, function(i, file) { if (fm.isRoot(file)) { return !dfrd.reject(['errRestore', file.name]); } if (file.locked) { return !dfrd.reject(['errLocked', file.name]); } }); if (dfrd.state() === 'pending') { this.restore(dfrd, files, hashes, opts); } return dfrd; }; }).prototype = { forceLoad : true }; // this is required command /* * File: /js/commands/rm.js */ /** * @class elFinder command "rm" * Delete files * * @author Dmitry (dio) Levashov * @author Naoki Sawada **/ elFinder.prototype.commands.rm = function() { "use strict"; var self = this, fm = this.fm, tpl = '<div class="ui-helper-clearfix elfinder-rm-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}<div class="elfinder-rm-desc">{desc}</div></div>', confirm = function(dfrd, targets, files, tHash, addTexts) { var cnt = targets.length, cwd = fm.cwd().hash, descs = [], spinner = fm.i18n('calc') + '<span class="elfinder-spinner"/>', dialog, text, tmb, size, f, fname; if (cnt > 1) { size = 0; jQuery.each(files, function(h, f) { if (f.size && f.size != 'unknown' && f.mime !== 'directory') { var s = parseInt(f.size); if (s >= 0 && size >= 0) { size += s; } } else { size = 'unknown'; return false; } }); getSize = (size === 'unknown'); descs.push(fm.i18n('size')+': '+(getSize? spinner : fm.formatSize(size))); text = [jQuery(tpl.replace('{class}', 'elfinder-cwd-icon-group').replace('{title}', '<strong>' + fm.i18n('items')+ ': ' + cnt + '</strong>').replace('{desc}', descs.join('<br>')))]; } else { f = files[0]; tmb = fm.tmb(f); getSize = (f.mime === 'directory'); descs.push(fm.i18n('size')+': '+(getSize? spinner : fm.formatSize(f.size))); descs.push(fm.i18n('modify')+': '+fm.formatDate(f)); fname = fm.escape(f.i18 || f.name).replace(/([_.])/g, '​$1'); text = [jQuery(tpl.replace('{class}', fm.mime2class(f.mime)).replace('{title}', '<strong>' + fname + '</strong>').replace('{desc}', descs.join('<br>')))]; } if (addTexts) { text = text.concat(addTexts); } text.push(tHash? 'confirmTrash' : 'confirmRm'); dialog = fm.confirm({ title : self.title, text : text, accept : { label : 'btnRm', callback : function() { if (tHash) { self.toTrash(dfrd, targets, tHash); } else { remove(dfrd, targets); } } }, cancel : { label : 'btnCancel', callback : function() { fm.unlockfiles({files : targets}); if (targets.length === 1 && fm.file(targets[0]).phash !== cwd) { fm.select({selected : targets}); } else { fm.selectfiles({files : targets}); } dfrd.reject(); } } }); // load thumbnail if (tmb) { jQuery('<img/>') .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); }) .attr('src', tmb.url); } if (getSize) { getSize = fm.getSize(jQuery.map(files, function(f) { return f.mime === 'directory'? f.hash : null; })).done(function(data) { dialog.find('span.elfinder-spinner').parent().html(fm.i18n('size')+': '+data.formated); }).fail(function() { dialog.find('span.elfinder-spinner').parent().html(fm.i18n('size')+': '+fm.i18n('unknown')); }).always(function() { getSize = false; }); } }, toTrash = function(dfrd, targets, tHash) { var dsts = {}, itemCnt = targets.length, maxCnt = self.options.toTrashMaxItems, checkDirs = [], reqDfd = jQuery.Deferred(), req, dirs, cnt; if (itemCnt > maxCnt) { self.confirm(dfrd, targets, self.files(targets), null, [fm.i18n('tooManyToTrash')]); return; } // Directory preparation preparation and directory enumeration jQuery.each(targets, function(i, h) { var file = fm.file(h), path = fm.path(h).replace(/\\/g, '/'), m = path.match(/^[^\/]+?(\/(?:[^\/]+?\/)*)[^\/]+?$/); if (file) { if (m) { m[1] = m[1].replace(/(^\/.*?)\/?$/, '$1'); if (! dsts[m[1]]) { dsts[m[1]] = []; } dsts[m[1]].push(h); } if (file.mime === 'directory') { checkDirs.push(h); } } }); // Check directory information if (checkDirs.length) { req = fm.request({ data : {cmd : 'size', targets : checkDirs}, notify : {type: 'readdir', cnt: 1, hideCnt: true}, preventDefault : true }).done(function(data) { var cnt = 0; data.fileCnt && (cnt += parseInt(data.fileCnt)); data.dirCnt && (cnt += parseInt(data.dirCnt)); reqDfd[cnt > maxCnt ? 'reject' : 'resolve'](); }).fail(function() { reqDfd.reject(); }); setTimeout(function() { var xhr = (req && req.xhr)? req.xhr : null; if (xhr && xhr.state() == 'pending') { req.syncOnFail(false); req.reject(); reqDfd.reject(); } }, self.options.infoCheckWait * 1000); } else { reqDfd.resolve(); } // Directory creation and paste command execution reqDfd.done(function() { dirs = Object.keys(dsts); cnt = dirs.length; if (cnt) { fm.request({ data : {cmd : 'mkdir', target : tHash, dirs : dirs}, notify : {type : 'chkdir', cnt : cnt}, preventFail : true }) .fail(function(error) { dfrd.reject(error); fm.unlockfiles({files : targets}); }) .done(function(data) { var margeRes = function(data, phash, reqData) { var undo, prevUndo, redo, prevRedo; jQuery.each(data, function(k, v) { if (Array.isArray(v)) { if (res[k]) { res[k] = res[k].concat(v); } else { res[k] = v; } } }); if (data.sync) { res.sync = 1; } if (data.added && data.added.length) { undo = function() { var targets = [], dirs = jQuery.map(data.added, function(f) { return f.mime === 'directory'? f.hash : null; }); jQuery.each(data.added, function(i, f) { if (jQuery.inArray(f.phash, dirs) === -1) { targets.push(f.hash); } }); return fm.exec('restore', targets, {noToast: true}); }; redo = function() { return fm.request({ data : reqData, notify : {type : 'redo', cnt : targets.length} }); }; if (res.undo) { prevUndo = res.undo; res.undo = function() { undo(); prevUndo(); }; } else { res.undo = undo; } if (res.redo) { prevRedo = res.redo; res.redo = function() { redo(); prevRedo(); }; } else { res.redo = redo; } } }, err = ['errTrash'], res = {}, hasNtf = function() { return fm.ui.notify.children('.elfinder-notify-trash').length; }, hashes, tm, prg, prgSt; if (hashes = data.hashes) { prg = 1 / cnt * 100; prgSt = cnt === 1? 100 : 5; tm = setTimeout(function() { fm.notify({type : 'trash', cnt : 1, hideCnt : true, progress : prgSt}); }, fm.notifyDelay); jQuery.each(dsts, function(dir, files) { var phash = fm.file(files[0]).phash, reqData; if (hashes[dir]) { reqData = {cmd : 'paste', dst : hashes[dir], targets : files, cut : 1}; fm.request({ data : reqData, preventDefault : true }) .fail(function(error) { if (error) { err = err.concat(error); } }) .done(function(data) { data = fm.normalize(data); fm.updateCache(data); margeRes(data, phash, reqData); if (data.warning) { err = err.concat(data.warning); delete data.warning; } // fire some event to update cache/ui data.removed && data.removed.length && fm.remove(data); data.added && data.added.length && fm.add(data); data.changed && data.changed.length && fm.change(data); // fire event with command name fm.trigger('paste', data); // fire event with command name + 'done' fm.trigger('pastedone'); // force update content data.sync && fm.sync(); }) .always(function() { var hashes = [], addTexts, end = 2; if (hasNtf()) { fm.notify({type : 'trash', cnt : 0, hideCnt : true, progress : prg}); } else { prgSt+= prg; } if (--cnt < 1) { tm && clearTimeout(tm); hasNtf() && fm.notify({type : 'trash', cnt : -1}); fm.unlockfiles({files : targets}); if (Object.keys(res).length) { if (err.length > 1) { if (res.removed || res.removed.length) { hashes = jQuery.grep(targets, function(h) { return jQuery.inArray(h, res.removed) === -1? true : false; }); } if (hashes.length) { if (err.length > end) { end = (fm.messages[err[end-1]] || '').indexOf('$') === -1? end : end + 1; } dfrd.reject(); fm.exec('rm', hashes, { addTexts: err.slice(0, end), forceRm: true }); } else { fm.error(err); } } res._noSound = true; if (res.undo && res.redo) { res.undo = { cmd : 'trash', callback : res.undo, }; res.redo = { cmd : 'trash', callback : res.redo }; } dfrd.resolve(res); } else { dfrd.reject(err); } } }); } }); } else { dfrd.reject('errFolderNotFound'); fm.unlockfiles({files : targets}); } }); } else { dfrd.reject(['error', 'The folder hierarchy to be deleting can not be determined.']); fm.unlockfiles({files : targets}); } }).fail(function() { self.confirm(dfrd, targets, self.files(targets), null, [fm.i18n('tooManyToTrash')]); }); }, remove = function(dfrd, targets, quiet) { var notify = quiet? {} : {type : 'rm', cnt : targets.length}; fm.request({ data : {cmd : 'rm', targets : targets}, notify : notify, preventFail : true }) .fail(function(error) { dfrd.reject(error); }) .done(function(data) { if (data.error || data.warning) { data.sync = true; } dfrd.resolve(data); }) .always(function() { fm.unlockfiles({files : targets}); }); }, getTHash = function(targets) { var thash = null, root1st; if (targets && targets.length) { if (targets.length > 1 && fm.searchStatus.state === 2) { root1st = fm.file(fm.root(targets[0])).volumeid; if (!jQuery.grep(targets, function(h) { return h.indexOf(root1st) !== 0? true : false ; }).length) { thash = fm.option('trashHash', targets[0]); } } else { thash = fm.option('trashHash', targets[0]); } } return thash; }, getSize = false; // for to be able to overwrite this.confirm = confirm; this.toTrash = toTrash; this.remove = remove; this.syncTitleOnChange = true; this.updateOnSelect = false; this.shortcuts = [{ pattern : 'delete ctrl+backspace shift+delete' }]; this.value = 'rm'; this.init = function() { // re-assign for extended command self = this; fm = this.fm; // bind function of change self.change(function() { var targets; delete self.extra; self.title = fm.i18n('cmd' + self.value); self.className = self.value; self.button && self.button.children('span.elfinder-button-icon')[self.value === 'trash'? 'addClass' : 'removeClass']('elfinder-button-icon-trash'); if (self.value === 'trash') { self.extra = { icon: 'rm', node: jQuery('<span/>') .attr({title: fm.i18n('cmdrm')}) .on('ready', function(e, data) { targets = data.targets; }) .on('click touchstart', function(e){ if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) { return; } e.stopPropagation(); e.preventDefault(); fm.getUI().trigger('click'); // to close the context menu immediately fm.exec('rm', targets, {_userAction: true, forceRm : true}); }) }; } }); }; this.getstate = function(select) { var sel = this.hashes(select); return sel.length && jQuery.grep(sel, function(h) { var f = fm.file(h); return f && ! f.locked && ! fm.isRoot(f)? true : false; }).length == sel.length ? 0 : -1; }; this.exec = function(hashes, cOpts) { var opts = cOpts || {}, dfrd = jQuery.Deferred() .always(function() { if (getSize && getSize.state && getSize.state() === 'pending') { getSize.reject(); } }) .fail(function(error) { error && fm.error(error); }).done(function(data) { !opts.quiet && !data._noSound && data.removed && data.removed.length && fm.trigger('playsound', {soundFile : 'rm.wav'}); }), files = self.files(hashes), cnt = files.length, tHash = null, addTexts = opts.addTexts? opts.addTexts : null, forceRm = opts.forceRm, quiet = opts.quiet, targets; if (! cnt) { return dfrd.reject(); } jQuery.each(files, function(i, file) { if (fm.isRoot(file)) { return !dfrd.reject(['errRm', file.name, 'errPerm']); } if (file.locked) { return !dfrd.reject(['errLocked', file.name]); } }); if (dfrd.state() === 'pending') { targets = self.hashes(hashes); cnt = files.length; if (forceRm || (self.event && self.event.originalEvent && self.event.originalEvent.shiftKey)) { tHash = ''; self.title = fm.i18n('cmdrm'); } if (tHash === null) { tHash = getTHash(targets); } fm.lockfiles({files : targets}); if (tHash && self.options.quickTrash) { self.toTrash(dfrd, targets, tHash); } else { if (quiet) { remove(dfrd, targets, quiet); } else { self.confirm(dfrd, targets, files, tHash, addTexts); } } } return dfrd; }; fm.bind('select contextmenucreate closecontextmenu', function(e) { var targets = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected(); if (targets && targets.length) { self.update(void(0), (targets? getTHash(targets) : fm.option('trashHash'))? 'trash' : 'rm'); } }); }; /* * File: /js/commands/search.js */ /** * @class elFinder command "search" * Find files * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.search = function() { "use strict"; this.title = 'Find files'; this.options = {ui : 'searchbutton'}; this.alwaysEnabled = true; this.updateOnSelect = false; /** * Return command status. * Search does not support old api. * * @return Number **/ this.getstate = function() { return 0; }; /** * Send search request to backend. * * @param String search string * @return jQuery.Deferred **/ this.exec = function(q, target, mime, type) { var fm = this.fm, reqDef = [], sType = type || '', onlyMimes = fm.options.onlyMimes, phash, targetVolids = [], setType = function(data) { if (sType && sType !== 'SearchName' && sType !== 'SearchMime') { data.type = sType; } return data; }; if (typeof q == 'string' && q) { if (typeof target == 'object') { mime = target.mime || ''; target = target.target || ''; } target = target? target : ''; if (mime) { mime = jQuery.trim(mime).replace(',', ' ').split(' '); if (onlyMimes.length) { mime = jQuery.map(mime, function(m){ m = jQuery.trim(m); return m && (jQuery.inArray(m, onlyMimes) !== -1 || jQuery.grep(onlyMimes, function(om) { return m.indexOf(om) === 0? true : false; }).length )? m : null; }); } } else { mime = [].concat(onlyMimes); } fm.trigger('searchstart', setType({query : q, target : target, mimes : mime})); if (! onlyMimes.length || mime.length) { if (target === '' && fm.api >= 2.1) { jQuery.each(fm.roots, function(id, hash) { reqDef.push(fm.request({ data : setType({cmd : 'search', q : q, target : hash, mimes : mime}), notify : {type : 'search', cnt : 1, hideCnt : (reqDef.length? false : true)}, cancel : true, preventDone : true })); }); } else { reqDef.push(fm.request({ data : setType({cmd : 'search', q : q, target : target, mimes : mime}), notify : {type : 'search', cnt : 1, hideCnt : true}, cancel : true, preventDone : true })); if (target !== '' && fm.api >= 2.1 && Object.keys(fm.leafRoots).length) { jQuery.each(fm.leafRoots, function(hash, roots) { phash = hash; while(phash) { if (target === phash) { jQuery.each(roots, function() { var f = fm.file(this); f && f.volumeid && targetVolids.push(f.volumeid); reqDef.push(fm.request({ data : setType({cmd : 'search', q : q, target : this, mimes : mime}), notify : {type : 'search', cnt : 1, hideCnt : false}, cancel : true, preventDone : true })); }); } phash = (fm.file(phash) || {}).phash; } }); } } } else { reqDef = [jQuery.Deferred().resolve({files: []})]; } fm.searchStatus.mixed = (reqDef.length > 1)? targetVolids : false; return jQuery.when.apply($, reqDef).done(function(data) { var argLen = arguments.length, i; data.warning && fm.error(data.warning); if (argLen > 1) { data.files = (data.files || []); for(i = 1; i < argLen; i++) { arguments[i].warning && fm.error(arguments[i].warning); if (arguments[i].files) { data.files.push.apply(data.files, arguments[i].files); } } } // because "preventDone : true" so update files cache data.files && data.files.length && fm.cache(data.files); fm.lazy(function() { fm.trigger('search', data); }).then(function() { // fire event with command name + 'done' return fm.lazy(function() { fm.trigger('searchdone'); }); }).then(function() { // force update content data.sync && fm.sync(); }); }); } fm.getUI('toolbar').find('.'+fm.res('class', 'searchbtn')+' :text').trigger('focus'); return jQuery.Deferred().reject(); }; }; /* * File: /js/commands/selectall.js */ /** * @class elFinder command "selectall" * Select ALL of cwd items * * @author Naoki Sawada **/ elFinder.prototype.commands.selectall = function() { "use strict"; var self = this, state = 0; this.fm.bind('select', function(e) { state = (e.data && e.data.selectall)? -1 : 0; }); this.state = 0; this.updateOnSelect = false; this.getstate = function() { return state; }; this.exec = function() { jQuery(document).trigger(jQuery.Event('keydown', { keyCode: 65, ctrlKey : true, shiftKey : false, altKey : false, metaKey : false })); return jQuery.Deferred().resolve(); }; }; /* * File: /js/commands/selectinvert.js */ /** * @class elFinder command "selectinvert" * Invert Selection of cwd items * * @author Naoki Sawada **/ elFinder.prototype.commands.selectinvert = function() { "use strict"; this.updateOnSelect = false; this.getstate = function() { return 0; }; this.exec = function() { jQuery(document).trigger(jQuery.Event('keydown', { keyCode: 73, ctrlKey : true, shiftKey : true, altKey : false, metaKey : false })); return jQuery.Deferred().resolve(); }; }; /* * File: /js/commands/selectnone.js */ /** * @class elFinder command "selectnone" * Unselect ALL of cwd items * * @author Naoki Sawada **/ elFinder.prototype.commands.selectnone = function() { "use strict"; var self = this, fm = this.fm, state = -1; fm.bind('select', function(e) { state = (e.data && e.data.unselectall)? -1 : 0; }); this.state = -1; this.updateOnSelect = false; this.getstate = function() { return state; }; this.exec = function() { fm.getUI('cwd').trigger('unselectall'); return jQuery.Deferred().resolve(); }; }; /* * File: /js/commands/sort.js */ /** * @class elFinder command "sort" * Change sort files rule * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.sort = function() { "use strict"; var self = this, fm = self.fm, setVar = function() { self.variants = []; jQuery.each(fm.sortRules, function(name, value) { if (fm.sorters[name]) { var arr = (name === fm.sortType)? (fm.sortOrder === 'asc'? 'n' : 's') : ''; self.variants.push([name, (arr? '<span class="ui-icon ui-icon-arrowthick-1-'+arr+'"></span>' : '') + ' ' + fm.i18n('sort'+name)]); } }); self.variants.push('|'); self.variants.push([ 'stick', (fm.sortStickFolders? '<span class="ui-icon ui-icon-check"/>' : '') + ' ' + fm.i18n('sortFoldersFirst') ]); if (fm.ui.tree && fm.options.sortAlsoTreeview !== null) { self.variants.push('|'); self.variants.push([ 'tree', (fm.sortAlsoTreeview? '<span class="ui-icon ui-icon-check"/>' : '') + ' ' + fm.i18n('sortAlsoTreeview') ]); } updateContextmenu(); }, updateContextmenu = function() { var cm = fm.getUI('contextmenu'), icon, sub; if (cm.is(':visible')) { icon = cm.find('span.elfinder-button-icon-sort'); sub = icon.siblings('div.elfinder-contextmenu-sub'); sub.find('span.ui-icon').remove(); sub.children('div.elfinder-contextsubmenu-item').each(function() { var tgt = jQuery(this).children('span'), name = tgt.text().trim(), arr; if (name === (i18Name.stick || (i18Name.stick = fm.i18n('sortFoldersFirst')))) { if (fm.sortStickFolders) { tgt.prepend('<span class="ui-icon ui-icon-check"/>'); } } else if (name === (i18Name.tree || (i18Name.tree = fm.i18n('sortAlsoTreeview')))) { if (fm.sortAlsoTreeview) { tgt.prepend('<span class="ui-icon ui-icon-check"/>'); } } else if (name === (i18Name[fm.sortType] || (i18Name[fm.sortType] = fm.i18n('sort' + fm.sortType)))) { arr = fm.sortOrder === 'asc'? 'n' : 's'; tgt.prepend('<span class="ui-icon ui-icon-arrowthick-1-'+arr+'"></span>'); } }); } }, i18Name = {}; /** * Command options * * @type Object */ this.options = {ui : 'sortbutton'}; this.keepContextmenu = true; fm.bind('sortchange', setVar) .bind('sorterupdate', function() { setVar(); fm.getUI('toolbar').find('.elfiner-button-sort .elfinder-button-menu .elfinder-button-menu-item').each(function() { var tgt = jQuery(this), rel = tgt.attr('rel'); tgt.toggle(! rel || fm.sorters[rel]); }); }) .bind('cwdrender', function() { var cols = jQuery(fm.cwd).find('div.elfinder-cwd-wrapper-list table'); if (cols.length) { jQuery.each(fm.sortRules, function(name, value) { var td = cols.find('thead tr td.elfinder-cwd-view-th-'+name); if (td.length) { var current = ( name == fm.sortType), sort = { type : name, order : current ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder },arr; if (current) { td.addClass('ui-state-active'); arr = fm.sortOrder == 'asc' ? 'n' : 's'; jQuery('<span class="ui-icon ui-icon-triangle-1-'+arr+'"/>').appendTo(td); } jQuery(td).on('click', function(e){ if (! jQuery(this).data('dragging')) { e.stopPropagation(); if (! fm.getUI('cwd').data('longtap')) { fm.exec('sort', [], sort); } } }) .on('mouseenter mouseleave', function(e) { jQuery(this).toggleClass('ui-state-hover', e.type === 'mouseenter'); }); } }); } }); this.getstate = function() { return 0; }; this.exec = function(hashes, cOpt) { var fm = this.fm, sortopt = jQuery.isPlainObject(cOpt)? cOpt : (function() { cOpt += ''; var sOpts = {}; if (cOpt === 'stick') { sOpts.stick = !fm.sortStickFolders; } else if (cOpt === 'tree') { sOpts.tree = !fm.sortAlsoTreeview; } else if (fm.sorters[cOpt]) { if (fm.sortType === cOpt) { sOpts.order = fm.sortOrder === 'asc'? 'desc' : 'asc'; } else { sOpts.type = cOpt; } } return sOpts; })(), sort = Object.assign({ type : fm.sortType, order : fm.sortOrder, stick : fm.sortStickFolders, tree : fm.sortAlsoTreeview }, sortopt); return fm.lazy(function() { fm.setSort(sort.type, sort.order, sort.stick, sort.tree); this.resolve(); }); }; }; /* * File: /js/commands/undo.js */ /** * @class elFinder command "undo" * Undo previous commands * * @author Naoki Sawada **/ elFinder.prototype.commands.undo = function() { "use strict"; var self = this, fm = this.fm, setTitle = function(undo) { if (undo) { self.title = fm.i18n('cmdundo') + ' ' + fm.i18n('cmd'+undo.cmd); self.state = 0; } else { self.title = fm.i18n('cmdundo'); self.state = -1; } self.change(); }, cmds = []; this.alwaysEnabled = true; this.updateOnSelect = false; this.shortcuts = [{ pattern : 'ctrl+z' }]; this.syncTitleOnChange = true; this.getstate = function() { return cmds.length? 0 : -1; }; this.setUndo = function(undo, redo) { var _undo = {}; if (undo) { if (jQuery.isPlainObject(undo) && undo.cmd && undo.callback) { Object.assign(_undo, undo); if (redo) { delete redo.undo; _undo.redo = redo; } else { fm.getCommand('redo').setRedo(null); } cmds.push(_undo); setTitle(_undo); } } }; this.exec = function() { var redo = fm.getCommand('redo'), dfd = jQuery.Deferred(), undo, res, _redo = {}; if (cmds.length) { undo = cmds.pop(); if (undo.redo) { Object.assign(_redo, undo.redo); delete undo.redo; } else { _redo = null; } dfd.done(function() { if (_redo) { redo.setRedo(_redo, undo); } }); setTitle(cmds.length? cmds[cmds.length-1] : void(0)); res = undo.callback(); if (res && res.done) { res.done(function() { dfd.resolve(); }).fail(function() { dfd.reject(); }); } else { dfd.resolve(); } if (cmds.length) { this.update(0, cmds[cmds.length - 1].name); } else { this.update(-1, ''); } } else { dfd.reject(); } return dfd; }; fm.bind('exec', function(e) { var data = e.data || {}; if (data.opts && data.opts._userAction) { if (data.dfrd && data.dfrd.done) { data.dfrd.done(function(res) { if (res && res.undo && res.redo) { res.undo.redo = res.redo; self.setUndo(res.undo); } }); } } }); }; /** * @class elFinder command "redo" * Redo previous commands * * @author Naoki Sawada **/ elFinder.prototype.commands.redo = function() { "use strict"; var self = this, fm = this.fm, setTitle = function(redo) { if (redo && redo.callback) { self.title = fm.i18n('cmdredo') + ' ' + fm.i18n('cmd'+redo.cmd); self.state = 0; } else { self.title = fm.i18n('cmdredo'); self.state = -1; } self.change(); }, cmds = []; this.alwaysEnabled = true; this.updateOnSelect = false; this.shortcuts = [{ pattern : 'shift+ctrl+z ctrl+y' }]; this.syncTitleOnChange = true; this.getstate = function() { return cmds.length? 0 : -1; }; this.setRedo = function(redo, undo) { if (redo === null) { cmds = []; setTitle(); } else { if (redo && redo.cmd && redo.callback) { if (undo) { redo.undo = undo; } cmds.push(redo); setTitle(redo); } } }; this.exec = function() { var undo = fm.getCommand('undo'), dfd = jQuery.Deferred(), redo, res, _undo = {}, _redo = {}; if (cmds.length) { redo = cmds.pop(); if (redo.undo) { Object.assign(_undo, redo.undo); Object.assign(_redo, redo); delete _redo.undo; dfd.done(function() { undo.setUndo(_undo, _redo); }); } setTitle(cmds.length? cmds[cmds.length-1] : void(0)); res = redo.callback(); if (res && res.done) { res.done(function() { dfd.resolve(); }).fail(function() { dfd.reject(); }); } else { dfd.resolve(); } return dfd; } else { return dfd.reject(); } }; }; /* * File: /js/commands/up.js */ /** * @class elFinder command "up" * Go into parent directory * * @author Dmitry (dio) Levashov **/ (elFinder.prototype.commands.up = function() { "use strict"; this.alwaysEnabled = true; this.updateOnSelect = false; this.shortcuts = [{ pattern : 'ctrl+up' }]; this.getstate = function() { return this.fm.cwd().phash ? 0 : -1; }; this.exec = function() { var fm = this.fm, cwdhash = fm.cwd().hash; return this.fm.cwd().phash ? this.fm.exec('open', this.fm.cwd().phash).done(function() { fm.one('opendone', function() { fm.selectfiles({files : [cwdhash]}); }); }) : jQuery.Deferred().reject(); }; }).prototype = { forceLoad : true }; // this is required command /* * File: /js/commands/upload.js */ /** * @class elFinder command "upload" * Upload files using iframe or XMLHttpRequest & FormData. * Dialog allow to send files using drag and drop * * @type elFinder.command * @author Dmitry (dio) Levashov */ elFinder.prototype.commands.upload = function() { "use strict"; var hover = this.fm.res('class', 'hover'); this.disableOnSearch = true; this.updateOnSelect = false; // Shortcut opens dialog this.shortcuts = [{ pattern : 'ctrl+u' }]; /** * Return command state * * @return Number **/ this.getstate = function(select) { var fm = this.fm, f, sel = (select || [fm.cwd().hash]); if (!this._disabled && sel.length == 1) { f = fm.file(sel[0]); } return (f && f.mime == 'directory' && f.write)? 0 : -1; }; this.exec = function(data) { var fm = this.fm, cwdHash = fm.cwd().hash, getTargets = function() { var tgts = data && (data instanceof Array)? data : null, sel; if (! data || data instanceof Array) { if (! tgts && (sel = fm.selected()).length === 1 && fm.file(sel[0]).mime === 'directory') { tgts = sel; } else if (!tgts || tgts.length !== 1 || fm.file(tgts[0]).mime !== 'directory') { tgts = [ cwdHash ]; } } return tgts; }, targets = getTargets(), check = targets? targets[0] : (data && data.target? data.target : null), targetDir = check? fm.file(check) : fm.cwd(), fmUpload = function(data) { fm.upload(data) .fail(function(error) { dfrd.reject(error); }) .done(function(data) { var cwd = fm.getUI('cwd'), node; dfrd.resolve(data); if (data && data.added && data.added[0] && ! fm.ui.notify.children('.elfinder-notify-upload').length) { var newItem = fm.findCwdNodes(data.added); if (newItem.length) { newItem.trigger('scrolltoview'); } else { if (targetDir.hash !== cwdHash) { node = jQuery('<div/>').append( jQuery('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all elfinder-tabstop"><span class="ui-button-text">'+fm.i18n('cmdopendir')+'</span></button>') .on('mouseenter mouseleave', function(e) { jQuery(this).toggleClass('ui-state-hover', e.type == 'mouseenter'); }).on('click', function() { fm.exec('open', check).done(function() { fm.one('opendone', function() { fm.trigger('selectfiles', {files : jQuery.map(data.added, function(f) {return f.hash;})}); }); }); }) ); } else { fm.trigger('selectfiles', {files : jQuery.map(data.added, function(f) {return f.hash;})}); } fm.toast({msg: fm.i18n(['complete', fm.i18n('cmdupload')]), extNode: node}); } } }) .progress(function() { dfrd.notifyWith(this, Array.from(arguments)); }); }, upload = function(data) { dialog.elfinderdialog('close'); if (targets) { data.target = targets[0]; } fmUpload(data); }, getSelector = function() { var hash = targetDir.hash, dirs = jQuery.map(fm.files(hash), function(f) { return (f.mime === 'directory' && f.write)? f : null; }); if (! dirs.length) { return jQuery(); } return jQuery('<div class="elfinder-upload-dirselect elfinder-tabstop" title="' + fm.i18n('folders') + '"/>') .on('click', function(e) { e.stopPropagation(); e.preventDefault(); dirs = fm.sortFiles(dirs); var $this = jQuery(this), cwd = fm.cwd(), base = dialog.closest('div.ui-dialog'), getRaw = function(f, icon) { return { label : fm.escape(f.i18 || f.name), icon : icon, remain : false, callback : function() { var title = base.children('.ui-dialog-titlebar:first').find('span.elfinder-upload-target'); targets = [ f.hash ]; title.html(' - ' + fm.escape(f.i18 || f.name)); $this.trigger('focus'); }, options : { className : (targets && targets.length && f.hash === targets[0])? 'ui-state-active' : '', iconClass : f.csscls || '', iconImg : f.icon || '' } }; }, raw = [ getRaw(targetDir, 'opendir'), '|' ]; jQuery.each(dirs, function(i, f) { raw.push(getRaw(f, 'dir')); }); $this.trigger('blur'); fm.trigger('contextmenu', { raw: raw, x: e.pageX || jQuery(this).offset().left, y: e.pageY || jQuery(this).offset().top, prevNode: base, fitHeight: true }); }).append('<span class="elfinder-button-icon elfinder-button-icon-dir" />'); }, inputButton = function(type, caption) { var button, input = jQuery('<input type="file" ' + type + '/>') .on('click', function() { // for IE's bug if (fm.UA.IE) { setTimeout(function() { form.css('display', 'none').css('position', 'relative'); requestAnimationFrame(function() { form.css('display', '').css('position', ''); }); }, 100); } }) .on('change', function() { upload({input : input.get(0), type : 'files'}); }) .on('dragover', function(e) { e.originalEvent.dataTransfer.dropEffect = 'copy'; }), form = jQuery('<form/>').append(input).on('click', function(e) { e.stopPropagation(); }); return jQuery('<div class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only elfinder-tabstop elfinder-focus"><span class="ui-button-text">'+fm.i18n(caption)+'</span></div>') .append(form) .on('click', function(e) { e.stopPropagation(); e.preventDefault(); input.trigger('click'); }) .on('mouseenter mouseleave', function(e) { jQuery(this).toggleClass(hover, e.type === 'mouseenter'); }); }, dfrd = jQuery.Deferred(), dialog, dropbox, pastebox, dropUpload, paste, dirs, spinner, uidialog; dropUpload = function(e) { e.stopPropagation(); e.preventDefault(); var file = false, type = '', elfFrom = null, mycwd = '', data = null, target = e._target || null, trf = e.dataTransfer || null, kind = (trf.items && trf.items.length && trf.items[0].kind)? trf.items[0].kind : '', errors; if (trf) { try { elfFrom = trf.getData('elfinderfrom'); if (elfFrom) { mycwd = window.location.href + fm.cwd().hash; if ((!target && elfFrom === mycwd) || target === mycwd) { dfrd.reject(); return; } } } catch(e) {} if (kind === 'file' && (trf.items[0].getAsEntry || trf.items[0].webkitGetAsEntry)) { file = trf; type = 'data'; } else if (kind !== 'string' && trf.files && trf.files.length && jQuery.inArray('Text', trf.types) === -1) { file = trf.files; type = 'files'; } else { try { if ((data = trf.getData('text/html')) && data.match(/<(?:img|a)/i)) { file = [ data ]; type = 'html'; } } catch(e) {} if (! file) { if (data = trf.getData('text')) { file = [ data ]; type = 'text'; } else if (trf && trf.files) { // maybe folder uploading but this UA dose not support it kind = 'file'; } } } } if (file) { fmUpload({files : file, type : type, target : target, dropEvt : e}); } else { errors = ['errUploadNoFiles']; if (kind === 'file') { errors.push('errFolderUpload'); } fm.error(errors); dfrd.reject(); } }; if (!targets && data) { if (data.input || data.files) { data.type = 'files'; fmUpload(data); } else if (data.dropEvt) { dropUpload(data.dropEvt); } return dfrd; } paste = function(ev) { var e = ev.originalEvent || ev; var files = [], items = []; var file; if (e.clipboardData) { if (e.clipboardData.items && e.clipboardData.items.length){ items = e.clipboardData.items; for (var i=0; i < items.length; i++) { if (e.clipboardData.items[i].kind == 'file') { file = e.clipboardData.items[i].getAsFile(); files.push(file); } } } else if (e.clipboardData.files && e.clipboardData.files.length) { files = e.clipboardData.files; } if (files.length) { upload({files : files, type : 'files', clipdata : true}); return; } } var my = e.target || e.srcElement; requestAnimationFrame(function() { var type = 'text', src; if (my.innerHTML) { jQuery(my).find('img').each(function(i, v){ if (v.src.match(/^webkit-fake-url:\/\//)) { // For Safari's bug. // ref. https://bugs.webkit.org/show_bug.cgi?id=49141 // https://dev.ckeditor.com/ticket/13029 jQuery(v).remove(); } }); if (jQuery(my).find('a,img').length) { type = 'html'; } src = my.innerHTML; my.innerHTML = ''; upload({files : [ src ], type : type}); } }); }; dialog = jQuery('<div class="elfinder-upload-dialog-wrapper"/>') .append(inputButton('multiple', 'selectForUpload')); if (! fm.UA.Mobile && (function(input) { return (typeof input.webkitdirectory !== 'undefined' || typeof input.directory !== 'undefined');})(document.createElement('input'))) { dialog.append(inputButton('multiple webkitdirectory directory', 'selectFolder')); } if (targetDir.dirs) { if (targetDir.hash === cwdHash || fm.navHash2Elm(targetDir.hash).hasClass('elfinder-subtree-loaded')) { getSelector().appendTo(dialog); } else { spinner = jQuery('<div class="elfinder-upload-dirselect" title="' + fm.i18n('nowLoading') + '"/>') .append('<span class="elfinder-button-icon elfinder-button-icon-spinner" />') .appendTo(dialog); fm.request({cmd : 'tree', target : targetDir.hash}) .done(function() { fm.one('treedone', function() { spinner.replaceWith(getSelector()); uidialog.elfinderdialog('tabstopsInit'); }); }) .fail(function() { spinner.remove(); }); } } if (fm.dragUpload) { dropbox = jQuery('<div class="ui-corner-all elfinder-upload-dropbox elfinder-tabstop" contenteditable="true" data-ph="'+fm.i18n('dropPasteFiles')+'"></div>') .on('paste', function(e){ paste(e); }) .on('mousedown click', function(){ jQuery(this).trigger('focus'); }) .on('focus', function(){ this.innerHTML = ''; }) .on('mouseover', function(){ jQuery(this).addClass(hover); }) .on('mouseout', function(){ jQuery(this).removeClass(hover); }) .on('dragenter', function(e) { e.stopPropagation(); e.preventDefault(); jQuery(this).addClass(hover); }) .on('dragleave', function(e) { e.stopPropagation(); e.preventDefault(); jQuery(this).removeClass(hover); }) .on('dragover', function(e) { e.stopPropagation(); e.preventDefault(); e.originalEvent.dataTransfer.dropEffect = 'copy'; jQuery(this).addClass(hover); }) .on('drop', function(e) { dialog.elfinderdialog('close'); targets && (e.originalEvent._target = targets[0]); dropUpload(e.originalEvent); }) .prependTo(dialog) .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0]; } else { pastebox = jQuery('<div class="ui-corner-all elfinder-upload-dropbox" contenteditable="true">'+fm.i18n('dropFilesBrowser')+'</div>') .on('paste drop', function(e){ paste(e); }) .on('mousedown click', function(){ jQuery(this).trigger('focus'); }) .on('focus', function(){ this.innerHTML = ''; }) .on('dragenter mouseover', function(){ jQuery(this).addClass(hover); }) .on('dragleave mouseout', function(){ jQuery(this).removeClass(hover); }) .prependTo(dialog) .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0]; } uidialog = this.fmDialog(dialog, { title : this.title + '<span class="elfinder-upload-target">' + (targetDir? ' - ' + fm.escape(targetDir.i18 || targetDir.name) : '') + '</span>', modal : true, resizable : false, destroyOnClose : true, propagationEvents : ['mousemove', 'mouseup', 'click'], close : function() { var cm = fm.getUI('contextmenu'); if (cm.is(':visible')) { cm.click(); } } }); return dfrd; }; }; /* * File: /js/commands/view.js */ /** * @class elFinder command "view" * Change current directory view (icons/list) * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.view = function() { "use strict"; var self = this, fm = this.fm, subMenuRaw; this.value = fm.viewType; this.alwaysEnabled = true; this.updateOnSelect = false; this.options = { ui : 'viewbutton'}; this.getstate = function() { return 0; }; this.extra = { icon: 'menu', node: jQuery('<span/>') .attr({title: fm.i18n('viewtype')}) .on('click touchstart', function(e){ if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) { return; } var node = jQuery(this); e.stopPropagation(); e.preventDefault(); fm.trigger('contextmenu', { raw: getSubMenuRaw(), x: node.offset().left, y: node.offset().top }); }) }; this.exec = function() { var self = this, value = fm.storage('view', this.value == 'list' ? 'icons' : 'list'); return fm.lazy(function() { fm.viewchange(); self.update(void(0), value); this.resolve(); }); }; fm.bind('init', function() { subMenuRaw = (function() { var cwd = fm.getUI('cwd'), raws = [], sizeNames = fm.options.uiOptions.cwd.iconsView.sizeNames, max = fm.options.uiOptions.cwd.iconsView.sizeMax, i, size; for (i = 0; i <= max; i++) { raws.push( { label : fm.i18n(sizeNames[i] || ('Size-' + i + ' icons')), icon : 'view', callback : (function(s) { return function() { cwd.trigger('iconpref', {size: s}); fm.storage('iconsize', s); if (self.value === 'list') { self.exec(); } }; })(i) } ); } raws.push('|'); raws.push( { label : fm.i18n('viewlist'), icon : 'view-list', callback : function() { if (self.value !== 'list') { self.exec(); } } } ); return raws; })(); }).bind('contextmenucreate', function() { self.extra = { icon: 'menu', node: jQuery('<span/>') .attr({title: fm.i18n('cmdview')}) .on('click touchstart', function(e){ if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) { return; } var node = jQuery(this), raw = subMenuRaw.concat(), idx, i; if (self.value === 'list') { idx = subMenuRaw.length - 1; } else { idx = parseInt(fm.storage('iconsize') || 0); } for (i = 0; i < subMenuRaw.length; i++) { if (subMenuRaw[i] !== '|') { subMenuRaw[i].options = (i === idx? {'className': 'ui-state-active'} : void(0)) ; } } e.stopPropagation(); e.preventDefault(); fm.trigger('contextmenu', { raw: subMenuRaw, x: node.offset().left, y: node.offset().top }); }) }; }); }; return elFinder; }));
Save Changes
Rename File
Rename