/*! * elFinder - file manager for web * Version 2.1.57 (2020-06-05) * http://elfinder.org * * Copyright 2009-2020, 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 = $(elm), /** * Object of events originally registered in this node * * @type Object */ prevEvents = $.extend(true, {}, $._data(node.get(0), 'events')), /** * Store node contents. * * @see this.destroy * @type jQuery **/ prevContent = $('
').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') || node.attr('id', 'elfauto' + $('.elfinder').length).attr('id'), /** * Events namespace * * @type String **/ namespace = 'elfinder-' + id, /** * 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, tmbReqCustomData : 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 | $(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, /** * Disabled page unload function * @type Boolean */ diableUnloadCheck = false, beeper = $(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, diff; 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) { $.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]); } else { diff = self.diff([data.cwd], true); if (diff.changed.length) { cache(diff.changed, 'change'); self.change({changed: diff.changed}); } } data.changed && data.changed.length && cache(data.changed, 'change'); // 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 type = type || 'files', keeps = ['sizeInfo', 'encoding'], defsorter = { name: true, perm: true, date: true, size: true, kind: true }, sorterChk = !self.sorters._checked && (type === 'files'), l = data.length, setSorter = function(file) { var f = file || {}, sorters = []; $.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; }, changedParents = {}, hideData = self.storage('hide') || {}, hides = hideData.items || {}, f, i, i1, 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' && (!files[f.hash] || f.size !== files[f.hash])))) { if (parents = self.parents(f.phash)) { $.each(parents, function() { changedParents[this] = true; }); } } } if (files[f.hash]) { for (i1 =0; i1 < keeps.length; i1++) { if(files[f.hash][keeps[i1]] && ! f[keeps[i1]]) { f[keeps[i1]] = files[f.hash][keeps[i1]]; } } 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 $.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 $.each(self.leafRoots, function(phash, roots) { var idx, pdir; if ((idx = $.inArray(hash, roots))!== -1) { if (roots.length === 1) { if ((pdir = Object.assign({}, files[phash])) && pdir._realStats) { $.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) { $.each(files, function(h, f) { f.phash == hash && rm(h); }); } } if (file.phash) { if (parents = self.parents(file.phash)) { $.each(parents, function() { changedParents[this] = true; }); } } deleteCache(files[hash]); } }, changedParents = {}, parents; $.each(self.roots, function(k, v) { roots[v] = k; }); while (l--) { rm(removed[l]); } // delete sizeInfo cache $.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 * @deprecated should be use `cache(updatesArrayData, 'change');` */ change = function(changed) { $.each(changed, function(i, file) { var hash = file.hash; if (files[hash]) { $.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 = [], /** * Current open command instance * * @type Object */ currentOpenCmd = null, /** * 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) { $.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 == $.ui.keyCode.TAB && !$(e.target).is(':input')) { e.preventDefault(); } // cancel any actions by [Esc] key if (e.type === 'keydown' && code == $.ui.keyCode.ESCAPE) { // copy or cut if (! node.find('.ui-widget:visible').length) { self.clipboard().length && self.clipboard([]); } // dragging if ($.ui.ddmanager) { ddm = $.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 = $('iframe', window.parent.document); if (ifms.length) { $.each(ifms, function(i, ifm) { if (ifm.contentWindow === window) { pifm = $(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) { $(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; /** * Callback function at reload(restart) elFinder * * @type Function */ this.reloadCallback; /** * 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; } })(); /** * Set pause page unload check function or Get state * * @param Boolean state To set state * @param Boolean keep Keep disabled * @return Boolean|void */ this.pauseUnloadCheck = function(state, keep) { if (typeof state === 'undefined') { return diableUnloadCheck; } else { diableUnloadCheck = !!state; if (state && !keep) { requestAnimationFrame(function() { diableUnloadCheck = false; }); } } }; /** * Configuration options * * @type Object **/ //this.options = $.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 ($.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 ($.isPlainObject(obj)) { $.each(obj, function(k, v) { if ($.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 = $.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 = $.Event(type); if (data) { data._getEvent = function() { return event; }; } if (allowModify) { event.data = data; } for (i = 0; i < l; i++) { if (! handlers[i]) { // probably un-binded this handler continue; } // handler is $.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) { $.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, base, baseUrl; if (self.options.baseUrl) { return self.options.baseUrl; } else { baseUrl = ''; myTag = null; $('head > script').each(function() { if (this.src && this.src.match(/js\/elfinder(?:-[a-z0-9_-]+)?\.(?:min|full)\.js$/i)) { myTag = $(this); return false; } }); if (myTag) { baseUrl = myTag.attr('src').replace(/js\/[^\/]+$/, ''); if (! baseUrl.match(/^(https?\/\/|\/)/)) { // check tag if (base = $('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, myCss = $('head > link[href$="css/elfinder.min.css"],link[href$="css/elfinder.full.css"]:first').length, rmTag = function() { if (node.data('cssautoloadHide')) { node.data('cssautoloadHide').remove(); node.removeData('cssautoloadHide'); } }, loaded = function() { if (!self.cssloaded) { rmTag(); self.cssloaded = true; self.trigger('cssloaded'); } }; if (! myCss) { // to request CSS auto loading self.cssloaded = null; } // additional CSS files if (Array.isArray(self.options.cssAutoLoad)) { if (!self.options.themes.default) { // set as default theme self.options.themes = Object.assign({ 'default' : { 'name': 'default', 'cssurls': self.options.cssAutoLoad } }, self.options.themes); if (!self.options.theme) { self.options.theme = 'default'; } } else { 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.addClass('elfinder') .data('cssautoloadHide', $('')); $('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'; } } // Delay 'visibility' check it required for browsers such as Safari requestAnimationFrame(function() { if (node.css('visibility') === 'hidden') { // load CSS self.loadCss([baseUrl+'css/elfinder.min.css'], { dfd: $.Deferred().done(function() { loaded(); }).fail(function() { rmTag(); if (!self.cssloaded) { self.cssloaded = false; self.bind('init', function() { if (!self.cssloaded) { self.error(['errRead', 'CSS (elfinder.min)']); } }); } }) }); } else { loaded(); } }); } })(); } // load theme if exists (function() { var theme, themes = self.options.themes, ids = Object.keys(themes || {}); if (ids.length) { theme = self.storage('theme') || self.options.theme; if (!themes[theme]) { theme = ids[0]; } if (self.cssloaded) { self.changeTheme(theme); } else { self.bind('cssloaded', function() { self.changeTheme(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 && $('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 = {}; $.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() { $.each(conflictChecks, function(i, p) { if ($.fn[p] && typeof $.fn[p].noConflict === 'function') { self.noConflicts[p] = $.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 (!$.isPlainObject(self.options.customHeaders)) { self.options.customHeaders = {}; } if (!$.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 = $.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 = $.isPlainObject(this.options.customHeaders) ? this.options.customHeaders : {}; /** * Any custom xhrFields to send across every ajax request * * @type Object * @default {} */ this.xhrFields = $.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) { $.each(self.customHeaders, function(key) { xhr.setRequestHeader(key, this); }); } // set xhrFields if (self.xhrFields) { $.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 = $.extend(true, {}, this._sortRules, this.options.sortRules); $.each(this.sortRules, function(name, method) { if (typeof method != 'function') { delete self.sortRules[name]; } }); this.compare = $.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 = $(this), targets = $.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) { $.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 = ''; $.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 ]; } $.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 {}; } $.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 = $.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)? $.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 = getBaseUrl(); } if (baseUrl) { return filter(baseUrl + $.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 ? '?' : '&') + $.param(params, true)); }, getBaseUrl = function() { return self.option('url', (!self.isRoot(file) && file.phash) || file.hash); }, baseUrl, res; if (!file || !file.read) { return async? dfrd.resolve('') : ''; } if (onetm && (!file.url || file.url == '1') && !(baseUrl = getBaseUrl())) { async = true; this.request({ data : { cmd : 'url', target : hash, options : { onetime: 1 } }, preventDefault : true, options: {async: async}, notify: {type : 'file', cnt : 1, hideCnt : true}, progressBar: opts.progressBar }).done(function(data) { dfrd.resolve(filter(data.url || '')); }).fail(function() { dfrd.resolve(''); }); } else { if (file.url == '1' || (temp && !file.url && !(baseUrl = getBaseUrl()))) { 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} : {}, progressBar: opts.progressBar }) .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 * @param Object requestOpts The request options * @return String */ this.openUrl = function(hash, download, callback, requestOpts) { var file = files[hash], url = '', onetimeSize = (requestOpts || {}).onetimeSize || (5 * 1024 * 1024); if (!file || !file.read) { return ''; } if (!download || download === 'sameorigin') { if (file.url) { if (file.url != 1) { url = file.url; } } else if (cwdOptions.url && file.hash.indexOf(self.cwd().volumeid) === 0) { url = cwdOptions.url + $.map(this.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/'); } if (!download || this.isSameOrigin(url)) { 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)); if (callback) { callback(url); return; } else { return url; } } } } if (callback && this.hasParrotHeaders()) { if (!requestOpts) { requestOpts = {}; } else { delete requestOpts.onetimeSize; } if (!requestOpts.onetime && !requestOpts.temporary && file.size > onetimeSize) { if (file.mime.match(/^video|audio/)) { requestOpts.temporary = true; } else { requestOpts.onetime = true; } } if (requestOpts.onetime || requestOpts.temporary) { return this.url(file.hash, Object.assign({ async: true }, requestOpts)).done(function(url) { callback(url); }).fail(function() { callback(''); }); } else { return this.getContents(hash, 'blob', requestOpts).done(function(blob){ url = (window.URL || window.webkitURL).createObjectURL(blob); callback(url); }).fail(function() { callback(''); }); } } else { 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 === true) { url += '&download=1'; } $.each(this.customData, function(key, val) { url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(val); }); if (callback) { callback(url); return; } else { return url; } } }; /** * Return thumbnail url * * @param Object file object * @return String */ this.tmb = function(file) { var tmbUrl, tmbCrop, cls = 'elfinder-cwd-bgurl', url = '', cData = {}, n = 0; if ($.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 (tmbUrl !== 'self') { if (file.ts) { cData._t = file.ts; } if (cwdOptions.tmbReqCustomData && Object.keys(this.customData).length) { cData = Object.assign(cData, this.customData); } if (Object.keys(cData).length) { url += (url.match(/\?/) ? '&' : '?'); $.each(cData, function (key, val) { url += ((n++ === 0)? '' : '&') + encodeURIComponent(key) + '=' + encodeURIComponent(val); }); } } 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 $.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 $.Deferred */ this.request = function(opts) { var self = this, o = this.options, dfrd = $.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), // current progress of receive data prog = opts.progressVal || 20, // timer of fake progress progTm = null, // whether the notification dialog is currently displayed hasNotify= false, // options for notify dialog notify = !opts.progressBar? (opts.notify? Object.assign({progress: prog * opts.notify.cnt}, 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, progress : function(e) { var p = e.loaded / e.total * 100; progTm && clearTimeout(progTm); if (opts.progressBar) { try { opts.progressBar.width(p + '%'); } catch(e) {} } else { if (hasNotify && notify.type) { p = p * notify.cnt; if (prog < p) { self.notify({ type: notify.type, progress: p - prog, cnt: 0, hideCnt: notify.hideCnt }); prog = p; } } } if (opts.progress) { try { opts.progress(e); } catch(e) {} } } }, 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); } 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)) { $.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) { // Set currrent request command name self.currentReqCmd = cmd; response.debug && self.responseDebug(response); self.setCustomHeaderByXhr(xhr); 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 (!$.isPlainObject(response)) { return dfrd.reject({error :['errResponse', 'errDataNotJSON']}, xhr, response); } else if (response.error) { if (isOpen) { // check leafRoots $.each(self.leafRoots, function(phash, roots) { self.leafRoots[phash] = $.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]) { $.each(self.leafRoots[data.target], function(i, h) { var root; if (root = self.file(h)) { response[name].push(root); } }); } }, setTextMimes = function() { self.textMimes = {}; $.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 $.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 = $('
') .append( $('') .on('mouseenter mouseleave', function(e) { $(this).toggleClass('ui-state-hover', e.type == 'mouseenter'); }) .on('click', function() { self.exec(cmd, data || hashes, {_userAction: true, _currentType: 'toast', _currentNode: $(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 ? $.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 ($.isPlainObject(navigate.toast.incwd)) { self.toast(makeToast(navigate.toast.incwd)); } } else { if ($.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 (currentOpenCmd && currentOpenCmd.state() === 'pending') { if (currentOpenCmd._target === data.target) { return dfrd.reject('openabort'); } else { if (currentOpenCmd.xhr) { currentOpenCmd.xhr.queueAbort(); } else { currentOpenCmd.reject('openabort'); } } } currentOpenCmd = dfrd; currentOpenCmd._target = data.target; } dfrd.always(function() { delete options.headers['X-elFinderReqid']; if (isOpen) { currentOpenCmd = null; } }).fail(function(error, xhr, response) { var errData, errMsg; if (isOpen && error === 'openabort') { error = ''; syncOnFail = false; } 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 = $.grep(requestQueue, function(req) { return (req('cmd') === cmd) ? false : true; }); } } // trigger "requestError" event self.trigger('requestError', errData); if (errData._getEvent && errData._getEvent().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); errMsg = (typeof error === 'object')? error.error : error; if (errMsg) { deffail ? self.error(errMsg) : self.debug('error', self.i18n(errMsg)); } 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 ($.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()(); } }).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 = $.inArray(xhr, queue); data.compare && self.unbind(self.cmdsToAdd + ' autosync openxhrabort', abort); ndx !== -1 && queue.splice(ndx, 1); }); } else if ($.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 = $.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() { // start fake count up progTm = setTimeout(progFakeUp, 1000); self.notify(notify); hasNotify = true; dfrd.always(function() { notify.cnt = -(parseInt(notify.cnt)||0); self.notify(notify); hasNotify = false; }); }, self.notifyDelay); dfrd.always(function() { clearTimeout(timeout); }); } // queueing if (requestCnt < requestMaxConn) { // do request return request(); } else { if (isOpen) { requestQueue.unshift(request); } else { requestQueue.push(request); } return dfrd; } }, progFakeUp = function() { var add; if (hasNotify && progTm) { add = 1 * notify.cnt; progTm = null; self.notify({ type: notify.type, progress: add, cnt: 0, hideCnt: notify.hideCnt }); prog += add; if ((prog / notify.cnt) < 80) { progTm = setTimeout(progFakeUp, 500); } } }, 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 * @param String type * @return void */ this.cache = function(dataArray, type) { if (! Array.isArray(dataArray)) { dataArray = [ dataArray ]; } cache(dataArray, type); }; /** * Update file object caches by respose data object * * @param Object respose data object * @return void */ this.updateCache = function(data) { if ($.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 && cache(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; } } }; $.each(incoming, function(i, f) { raw[f.hash] = f; }); // make excludes object if (excludeProps && excludeProps.length) { excludes = {}; $.each(excludeProps, function() { excludes[this] = true; }); } // find removed $.each(files, function(hash, f) { if (! raw[hash] && (! onlydir || f.phash === onlydir)) { removed.push(hash); } }); // compare files $.each(raw, function(hash, file) { var origin = files[hash], orgKeys = {}, chkKeyLen; if (!origin) { added.push(file); } else { // make orgKeys object $.each(Object.keys(origin), function() { orgKeys[this] = true; }); $.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) { $.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) $.each(removed, function(i, hash) { var file = files[hash], phash = file.phash; if (phash && file.mime == 'directory' && $.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) { $.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 = $.Deferred().always(function() { !reqFail && 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; }, reqFail; if (! onlydir && self.api >= 2) { (cwd !== this.root()) && opts.push(this.request({ data : {cmd : 'parents', target : cwd}, preventDefault : true })); $.each(exParents(), function(i, data) { opts.push(self.request({ data : {cmd : data.cmd, target : data.target}, preventDefault : true })); }); } $.when.apply($, opts) .fail(function(error, xhr) { reqFail = (xhr && xhr.status != 200); if (! polling || $.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 && $.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 : $.ui.keyCode[code]); if (code && !shortcuts[pattern]) { shortcuts[pattern] = { keyCode : code, altKey : $.inArray('ALT', parts) != -1, ctrlKey : $.inArray('CTRL', parts) != -1, shiftKey : $.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 = []; $.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 $.map(clipboard, function(f) { return f.hash; }); }; if (hashes !== void(0)) { clipboard.length && this.trigger('unlockfiles', {files : map()}); remember = {}; clipboard = $.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 $.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 ($.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) : $.Deferred().reject('errUnknownCmd'); resType = typeof dfrd; if (!(resType === 'object' && dfrd.promise)) { self.debug('warning', '"cmd.exec()" should be returned "$.Deferred" but cmd "' + cmd + '" returned "' + resType + '"'); dfrd = $.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 = $('
').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 $('
').appendTo(this.ui.toast).elfindertoast(options || {}, this); }; /** * Return UI widget or node * * @param String ui name * @return jQuery */ this.getUI = function(ui) { return ui? (this.ui[ui] || $()) : 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 = $(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 = $.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 = {}; $(window).off('.' + namespace); $(document).off('.' + namespace); self.trigger = function(){}; $(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 $.each(prevEvents, function(n, arr) { $.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) && $.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', ''); $(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 = $(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, funcObj, cls = 'elfinder-fullscreen', clsN = 'elfinder-fullscreen-native', checkDialog = function() { var t = 0, l = 0; $.each(node.children('.ui-dialog,.ui-draggable'), function(i, d) { var $d = $(d), pos = $d.position(); if (pos.top < 0) { $d.css('top', t); t += 20; } if (pos.left < 0) { $d.css('left', l); l += 20; } }); }, setFuncObj = function() { var useFullscreen = self.storage('useFullscreen'); funcObj = self.UA.Fullscreen && (useFullscreen? useFullscreen > 0 : self.options.commandsOptions.fullscreen.mode === 'screen') ? { // 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; $(window).off('resize.' + namespace, resize); if (bodyOvf !== void(0)) { $('body').css('overflow', bodyOvf); } bodyOvf = void(0); if (orgStyle) { elm = orgStyle.elm; restoreStyle(elm); $(elm).trigger('resize', {fullscreen: 'off'}); } $(window).trigger('resize'); }, toFull: function(elem) { bodyOvf = $('body').css('overflow') || ''; $('body').css('overflow', 'hidden'); $(elem).css(self.getMaximizeCss()) .addClass(cls) .trigger('resize', {fullscreen: 'on'}); checkDialog(); $(window).on('resize.' + namespace, resize).trigger('resize'); return true; } }; }, restoreStyle = function(elem) { if (orgStyle && orgStyle.elm == elem) { $(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()) { $(elm).trigger('resize', {fullscreen: 'on'}); } }); } }; setFuncObj(); $(document).on('fullscreenchange.' + namespace + ' webkitfullscreenchange.' + namespace + ' mozfullscreenchange.' + namespace + ' MSFullscreenChange.' + namespace, function(e){ if (self.UA.Fullscreen) { var elm = funcObj.fullElm(), win = $(window); resizeTm && cancelAnimationFrame(resizeTm); if (elm === null) { win.off('resize.' + namespace, resize); if (orgStyle) { elm = orgStyle.elm; restoreStyle(elm); $(elm).trigger('resize', {fullscreen: 'off'}); } } else { $(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 = $(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; } } setFuncObj(); orgStyle = {elm: elm, style: $(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) { $(window).off('resize.' + namespace, resize); $('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', $('body').css('overflow') || '') .data('orgStyle', elm.attr('style')) .addClass(cls) .css(self.getMaximizeCss()); $('body').css('overflow', 'hidden'); $(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 = $(target), maximized = elm.hasClass(cls); if (maximized) { if (max === true) { return; } exitMax(elm); } else { if (max === false) { return; } toMax(elm); } }; })(); /************* init stuffs ****************/ Object.assign($.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() { if (opts.progress) { opts._xhr.addEventListener('progress', opts.progress); } return opts._xhr; }; } return $.ajax(opts); }; } if (this.transport.upload == 'iframe') { this.transport.upload = $.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 = $.proxy(this.uploads.xhr, this); this.dragUpload = true; } else { this.transport.upload = $.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= 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= 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) * @param Object requestOpts The request options * @return arraybuffer|blob The contents. */ this.getContents = function(hash, responseType, requestOpts) { var self = this, dfd = $.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(Object.assign({ data : {cmd : 'get'}, options : { url: url, type: 'get', cache : true, dataType : 'binary', responseType : type, processData: false }, notify : { type: 'file', cnt: 1, hideCnt: true }, cancel : true }, requestOpts || {})) .fail(function() { dfd.reject(); }) .done(function(data) { dfd.resolve(data); }); return dfd; }; /** * Gets the binary by url. * * @param {Object} opts The options * @param {Function} callback The callback * @param {Object} requestOpts The request options * @return arraybuffer|blob The contents. */ this.getBinaryByUrl = function(opts, callback, requestOpts) { var self = this, dfd = $.Deferred(), url, req; dfd.fail(function() { req && req.state() === 'pending' && req.reject(); }); req = self.request(Object.assign({ data : {cmd : 'get'}, options : Object.assign({ type: 'get', cache : true, dataType : 'binary', responseType : 'blob', processData: false }, opts) }, requestOpts || {})) .fail(function() { dfd.reject(); }) .done(function(data) { callback && callback(data); dfd.resolve(data); }); return dfd; }; /** * Gets the mimetype. * * @param {string} name The name * @param {string} orgMime The organization mime * @return {string} The mimetype. */ 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 = {}; if (window.Worker && window.ArrayBuffer) { // make fm.hashCheckers if (self.options.cdns.sparkmd5) { hashLibs.SparkMD5 = true; self.hashCheckers.push('md5'); } if (self.options.cdns.jssha) { hashLibs.jsSHA = true; 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 * @param Object requestOpts The request options * @return Object hashes with lib name as key */ self.getContentsHashes = function(target, needHashes, hashOpts, requestOpts) { var dfd = $.Deferred(), needs = self.arrayFlip(needHashes || ['md5'], true), libs = [], jobs = [], res = {}, opts = hashOpts? hashOpts : { shake128len : 256, shake256len : 512 }, req; dfd.fail(function() { req && req.reject(); }); if (Object.keys(hashLibs).length) { req = self.getContents(target, 'arraybuffer', requestOpts).done(function(arrayBuffer) { if (needs.md5 && hashLibs.SparkMD5) { jobs.push((function() { var job = $.Deferred(); try { var wk = self.getWorker(); job.fail(function() { wk && wk.terminate(); }); wk.onmessage = function(ans) { wk && wk.terminate(); if (ans.data.hash) { var f; res.md5 = ans.data.hash; if (f = self.file(target)) { f.md5 = res.md5; } } else if (ans.data.error) { res.md5 = ans.data.error; } dfd.notify(res); job.resolve(); }; wk.onerror = function(e) { job.reject(); }; wk.postMessage({ scripts: [self.options.cdns.sparkmd5, self.getWorkerUrl('calcfilehash.js')], data: { type: 'md5', bin: arrayBuffer } }); dfd.fail(function() { job.reject(); }); } catch(e) { job.reject(); delete hashLibs.SparkMD5; } return job; })()); } if (hashLibs.jsSHA) { $.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 = $.Deferred(); try { var wk = self.getWorker(); job.fail(function() { wk && wk.terminate(); }); wk.onmessage = function(ans) { wk && wk.terminate(); if (ans.data.hash) { var f; res['sha' + v] = ans.data.hash; if (f = self.file(target)) { f['sha' + v] = res['sha' + v]; } } else if (ans.data.error) { res['sha' + v] = ans.data.error; } dfd.notify(res); job.resolve(); }; wk.onerror = function(e) { job.reject(); }; wk.postMessage({ scripts: [self.options.cdns.jssha, self.getWorkerUrl('calcfilehash.js')], data: { type: v, bin: arrayBuffer, hashOpts: opts } }); dfd.fail(function() { job.reject(); }); } catch(e) { job.reject(); delete hashLibs.jsSHA; } return job; })()); } }); } if (jobs.length) { $.when.apply(null, 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 ($.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 $.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, $.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 = $.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() { $(this).elfinderdialog('close'); }; if (e.data.opts && $.isPlainObject(e.data.opts)) { Object.assign(opts, e.data.opts); } self.dialog(''+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 = $(''+ self.i18n(['moreErrors', 1]) +'').data('cnt', 1); last.append('
', counter); } } }) .bind('tmb', function(e) { $.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; } $.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' && $(beeper).html('')[0].play(); }); } // bind external event handlers $.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 ($.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 = $(this.options.heightBase); } if (this.options.soundPath) { soundPath = this.options.soundPath.replace(/\/+$/, '') + '/'; } else { soundPath = this.baseUrl + soundPath; } if (this.options.parrotHeaders && Array.isArray(this.options.parrotHeaders) && this.options.parrotHeaders.length) { this.parrotHeaders = this.options.parrotHeaders; // check sessionStorage $.each(this.parrotHeaders, function(i, h) { var v = self.sessionStorage('core-ph:' + h); if (v) { self.customHeaders[h] = v; } }); } else { this.parrotHeaders = []; } self.one('opendone', function() { var tm; // attach events to document $(document) // disable elfinder on click outside elfinder .on('click.'+namespace, function(e) { enabled && ! self.options.enableAlways && !$(e.target).closest(node).length && self.disable(); }) // exec shortcuts .on(keydown+' '+keypress+' '+keyup+' '+mousedown, execShortcut); // attach events to window self.options.useBrowserHistory && $(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 $('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')) { $(document).trigger($.Event('keydown', { keyCode: $.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($.Event('keydown', { keyCode: $.ui.keyCode.ESCAPE, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false })); } } else { if (hasThash) { !$.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); } }); } } }); $(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 (!self.pauseUnloadCheck()) { if (node.is(':visible')) { if (self.ui.notify.children().length && $.inArray('hasNotifyDialog', self.options.windowCloseConfirm) !== -1) { msg = self.i18n('ntfsmth'); } else if (node.find('.'+self.res('class', 'editing')).length && $.inArray('editingFile', self.options.windowCloseConfirm) !== -1) { msg = self.i18n('editingFile'); } else if ((cnt = Object.keys(self.selected()).length) && $.inArray('hasSelectedItem', self.options.windowCloseConfirm) !== -1) { msg = self.i18n('hasSelected', ''+cnt); } else if ((cnt = Object.keys(self.clipboard()).length) && $.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 $(window).on('message.' + namespace, function(e){ var res = e.originalEvent || null, obj, data; if (res && (self.convAbsUrl(self.options.url).indexOf(res.origin) === 0 || self.convAbsUrl(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) { $(window).on('focus.' + namespace, function(e){ (e.target === this) && self.enable(); }); if (inFrame) { $(window.top).on('focus.' + namespace, function() { if (self.enable() && (! parentIframe || parentIframe.is(':visible'))) { requestAnimationFrame(function() { $(window).trigger('focus'); }); } }); } } else if (inFrame) { $(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) { $(window).trigger('focus'); }); } // elFinder to enable by mouse over if (self.options.enableByMouseOver) { node.on('mouseenter touchstart', function(e) { (inFrame) && $(window).trigger('focus'); ! self.enabled() && self.enable(); }); } // When the browser tab turn to foreground/background $(window).on('visibilitychange.' + namespace, function(e) { var background = document.hidden || document.webkitHidden || document.msHidden; // AutoSync turn On/Off if (self.options.syncStart) { self.autoSync(background? 'stop' : void(0)); } }); }); // 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 = $.Deferred().done(function() { if (self.i18[lang]) { self.lang = lang; } self.trigger('i18load'); i18n = self.lang === 'en' ? self.i18['en'] : $.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 (!($.fn.selectable && $.fn.draggable && $.fn.droppable && $.fn.resizable && $.fn.button && $.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 = $.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') && $(this).elfUiWidgetInstance('draggable')) { if (helper.data('refreshPositions') > 0) { $(this).draggable('option', { refreshPositions : true, elfRefresh : true }); helper.data('refreshPositions', -1); } else { $(this).draggable('option', { refreshPositions : false, elfRefresh : false }); helper.data('refreshPositions', null); } } }, stop : function(e, ui) { var helper = ui.helper, files; $(document).off(keyEvt); $(this).elfUiWidgetInstance('draggable') && $(this).draggable('option', { refreshPositions : false }); self.draggingUiHelper = null; self.trigger('focus').trigger('dragstop'); if (! helper.data('droped')) { files = $.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 ? $(this) : $(this).parents('[id]:first'), helper = $('
'), icon = function(f) { var mime = f.mime, i, tmb = self.tmb(f); i = '
'; if (tmb) { i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML; } else if (f.icon) { i = $(i).css(self.getIconStyle(f, true)).get(0).outerHTML; } if (f.csscls) { i = '
' + i + '
'; } 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]]) + ''+l+''); } $(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 $.each(self.commands, function(name, cmd) { var proto = Object.assign({}, cmd.prototype), extendsCmd, opts; if ($.isFunction(cmd) && !self._commands[name] && (cmd.prototype.forceLoad || $.inArray(name, self.options.commands) !== -1)) { extendsCmd = cmd.prototype.extendsCmd || ''; if (extendsCmd) { if ($.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 = $.extend(true, {}, self.options.commandsOptions[extendsCmd], opts); } self._commands[name].setup(name, opts); // setup linked commands if (self._commands[name].linkedCmds.length) { $.each(self._commands[name].linkedCmds, function(i, n) { var lcmd = self.commands[n]; if ($.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 : $('
').appendTo(node).elfinderworkzone(self), // contaainer for folders tree / places navbar : $('
').appendTo(node).elfindernavbar(self, self.options.uiOptions.navbar || {}), // container for for preview etc at below the navbar navdock : $('
').appendTo(node).elfindernavdock(self, self.options.uiOptions.navdock || {}), // contextmenu contextmenu : $('
').appendTo(node).elfindercontextmenu(self), // overlay overlay : $('
').appendTo(node).elfinderoverlay({ show : function() { self.disable(); }, hide : function() { prevEnabled && self.enable(); } }), // current folder container cwd : $('
').appendTo(node).elfindercwd(self, self.options.uiOptions.cwd || {}), // notification dialog window notify : self.dialog('', { cssClass : 'elfinder-dialog-notify' + (self.options.notifyDialog.canClose? '' : ' elfinder-titlebar-button-hide'), position : self.options.notifyDialog.position, absolute : true, resizable : false, autoOpen : false, allowMinimize : true, closeOnEscape : self.options.notifyDialog.canClose? true : false, title : ' ', width : self.options.notifyDialog.width? parseInt(self.options.notifyDialog.width) : null, minHeight : null, minimize : function() { self.ui.notify.trigger('minimize'); } }), statusbar : $('
').hide().appendTo(node), toast : $('
').appendTo(node), bottomtray : $('
').appendTo(node), progressbar : $('
').appendTo(node) }; self.trigger('uiready'); // load required ui $.each(self.options.ui || [], function(i, ui) { var name = 'elfinder'+ui, opts = self.options.uiOptions[ui] || {}; if (!self.ui[ui] && $.fn[name]) { // regist to self.ui before make instance self.ui[ui] = $('<'+(opts.tag || 'div')+'/>').appendTo(node); self.ui[ui][name](self, opts); } }); self.ui.progressbar.appendTo(self.ui.workzone); self.ui.notify.prev('.ui-dialog-titlebar').append('
'); // 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' && $(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 = $(e.currentTarget), id = e.currentTarget.id || null, cwd = null, elfFrom; if (!id) { // target is cwd cwd = self.cwd(); $elm.data(disable, false); try { $.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 = $(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 = $(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 = $(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 cssAutoLoad disabled if (self.cssloaded === false) { 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 = {}; $(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) { $(window).trigger('focus'); } // check self.trashes $.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) { $.each(data.files, function(i, dir) { if (dir.mime === 'directory' && dir.write) { delete toChkTh[dir.hash]; } }); } }).always(function() { $.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 $.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 && 'localStorage' in window && '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)/), Mac : navigator.platform.match(/^Mac/), 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; })(), /** * Is cookie enabled * * @type Boolean */ cookieEnabled : (function() { var res = false, test = 'elftest='; document.cookie = test + '1'; res = document.cookie.split(test).length === 2; document.cookie = test + ';max-age=0'; return res; })(), /** * 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 && $.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 && ($.isPlainObject(data.images) || Array.isArray(data.images)); }, upload : function(data) { return data && ($.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 (!$.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 : (data.norError || ['errResponse'])}; } data.removed = $.merge((data.removed || []), $.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 = $.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) { 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 = $.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 : $.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 = $.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 = $.grep(names, function(name){ return $.inArray(name.name, existed) !== -1 ? true : false ; }); if (exists.length && existed.length && target == fm.cwd().hash) { cwdItems = $.map(fm.files(target), function(file) { return file.name; } ); if ($.grep(existed, function(n) { return $.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 = $.Deferred(), scanDfd = $.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 = $.map(data.files.items, function(item){ return item.getAsEntry? item.getAsEntry() : item.webkitGetAsEntry(); }); $.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 = $.grep(items, function(item){ var i, bak, hash, dfd, hi; if (item.isDirectory && renames.length) { i = $.inArray(item.name, renames); if (i !== -1) { renames.splice(i, 1); bak = fm.uniqueName(item.name + fm.options.backupSuffix , null, ''); $.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; }); } $.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 = $("").append($.parseHTML(str.replace(/ src=/ig, ' _elfsrc='))), atag; $('img[_elfsrc]', tmp).each(function(){ var url, purl, self = $(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) { $.inArray(purl, ret) == -1 && ret.push(purl); $.inArray(url, check) == -1 && check.push(url); } else { $.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 = $('a[href]', tmp); atag.each(function(){ var text, loc, parseUrl = function(url) { var a = document.createElement('a'); a.href = url; return a; }; if (text = $(this).text()) { loc = parseUrl($(this).attr('href')); if (loc.href && loc.href.match(/^(?:ht|f)tp/i) && (atag.length === 1 || ! loc.pathname.match(/(?:\.html?|\/[^\/.]*)$/i) || $.trim(text).match(/\.[a-z0-9-]{1,10}$/i))) { if ($.inArray(loc.href, ret) == -1 && $.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 ($.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, notifyto1 = 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 = $.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 = $.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)) { $.each(data.toasts, function() { this.msg && self.toast(this); }); } data.sync && self.sync(); if (data.debug) { self.responseDebug(data); fm.debug('backend-debug', data); } } }) .always(function() { self.abortXHR(xhr); // unregist fnAbort function node.off('uploadabort', fnAbort); $(window).off('unload', fnAbort); notifyto && clearTimeout(notifyto); notifyto1 && clearTimeout(notifyto1); notifyto2 && clearTimeout(notifyto2); dataChecked && !data.multiupload && checkNotify() && self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0}); notifyto1 && uploadedNtf && self.notify({type : 'chunkmerge', cnt : -cnt}); 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, isChunked = false, loaded = 0, prev = 0, filesize = 0, notify = false, notifyElm = self.ui.notify, cancelBtn = true, uploadedNtf = false, 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, hasChunk) { ntfUpload.children('.elfinder-notify-cancel')[show? 'show':'hide'](); cancelBtn = show; }, 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 = $(); // regist fnAbort function node.one('uploadabort', fnAbort); $(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 && $.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 && $.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; self.setCustomHeaderByXhr(xhr); 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._getEvent && errData._getEvent().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 (!uploadedNtf && loaded >= filesize && !isChunked) { // Use "chunkmerge" for "server-in-process" notification uploadedNtf = true; notifyto1 = setTimeout(function() { self.notify({type : 'chunkmerge', cnt : cnt}); }, self.options.notifyDelay); } if (cancelBtn && ! data.multiupload && loaded >= filesize) { checkNotify() && 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, blobName, i, start, end, chunks, blob, chunk, added, done, last, failChunk, multi = function(files, num){ var sfiles = [], cid, sfilesLen = 0, cancelChk, hasChunk; 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); hasChunk = (hasChunk || cid)? true : false; 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, clipdata: data.clipdata }, 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 = $.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) { if (cancelBtn) { cancelToggle(false, hasChunk); } } dfrd.resolve(); } }); } } } 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; blobName = data.clipdata? fm.date(fm.nonameDateFormat) + '.png' : blob.name; totalSize += blobSize; chunked[chunkID] = 0; while(start < blobSize) { chunk = blob[blobSlice](start, end); chunk._chunk = blobName + '.' + (++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) { $.each(fm.customHeaders, function(key) { xhr.setRequestHeader(key, this); }); } // set xhrFields if (fm.xhrFields) { $.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) { $.each(renames, function(i, v) { formData.append('renames[]', v); }); formData.append('suffix', fm.options.backupSuffix); } if (hashes) { $.each(hashes, function(i, v) { formData.append('hashes['+ i +']', v); }); } $.each(self.customData, function(key, val) { formData.append(key, val); }); $.each(self.options.onlyMimes, function(i, mime) { formData.append('mimes[]', mime); }); $.each(files, function(i, file) { var name, relpath; if (file._chunkmerged) { formData.append('chunk', file._chunkmerged); formData.append('upload[]', file._name); formData.append('mtime[]', file._mtime); data.clipdata && formData.append('overwrite', 0); isChunked = true; } else { if (file._chunkfail) { formData.append('upload[]', 'chunkfail'); formData.append('mimes', 'chunkfail'); } else { if (data.clipdata) { if (!file._chunk) { data.overwrite = 0; name = fm.date(fm.nonameDateFormat) + '.png'; } } else { if (file.name) { name = file.name; if (fm.UA.iOS) { if (name.match(/^image\.jpe?g$/i)) { data.overwrite = 0; name = fm.date(fm.nonameDateFormat) + '.jpg'; } else if (name.match(/^capturedvideo\.mov$/i)) { data.overwrite = 0; name = fm.date(fm.nonameDateFormat) + '.mov'; } } relpath = (file.webkitRelativePath || file.relativePath || file._relativePath || '').replace(/[^\/]+$/, ''); name = relpath + name; } } name? formData.append('upload[]', file, name) : formData.append('upload[]', file); } if (file._chunk) { formData.append('chunk', file._chunk); formData.append('cid' , file._cid); formData.append('range', file._range); formData.append('mtime[]', file._mtime); isChunked = true; } else { formData.append('mtime[]', file.lastModified? Math.round(file.lastModified/1000) : 0); } } }); if (isDataType) { $.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) { $.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; $.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 && $.inArray(relPath, mkdirs) === -1) { mkdirs.push(relPath); // checking the root directory to supports see #2378 idx = relPath.substr(1).indexOf('/'); if (idx !== -1 && (rootDir = relPath.substr(0, idx + 1)) && $.inArray(rootDir, mkdirs) === -1) { mkdirs.unshift(rootDir); } } } paths.push(relPath); }); renames = []; hashes = {}; if (mkdirs.length) { (function() { var checkDirs = $.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 = $.map(checkDirs, function(dir) { return dir._remove? dir.name : null ;} ); checkDirs = $.grep(checkDirs, function(dir) { return !dir._remove? true : false ;} ); } if (cancelDirs.length) { $.each(paths.concat(), function(i, path) { if ($.inArray(path, cancelDirs) === 0) { files[i]._remove = true; paths[i] = void(0); } }); } files = $.grep(files, function(file) { return file._remove? false : true; }); paths = $.grep(paths, function(path) { return path === void 0 ? false : true; }); if (checkDirs.length) { dfd = $.Deferred(); if (res.length) { $.each(res, function(i, existName) { // backup bak = fm.uniqueName(existName + fm.options.backupSuffix , null, ''); $.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); } $.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 = $.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 = $.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 = $.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] = $.map(result[1], function(p, i) { result[0][i]._relativePath = p.replace(/^\//, ''); 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] = $.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] = $.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 = $.Deferred() .fail(function(error) { error && self.error(error); }), name = 'iframe-'+fm.namespace+(++self.iframeCnt), 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 && $('').appendTo(form); form.remove(); iframe.remove(); }, 100); }, iframe = $('') .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) { $.each(files, function(i, val) { form.append(''); }); cnt = 1; } else if (input && $(input).is(':file') && $(input).val()) { if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) { names = input.files? input.files : [{ name: $(input).val().replace(/^(?:.+[\\\/])?([^\\\/]+)$/, '$1') }]; //names = $.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 = $.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(); } $.when.apply($, dfds).done(function() { if (cnt < 1) { return dfrd.reject(); } form.append('') .append('') .append('') .append($(input).attr('name', 'upload[]')); if (renames.length > 0) { $.each(renames, function(i, rename) { form.append(''); }); form.append(''); } if (hashes) { $.each(renames, function(i, v) { form.append(''); }); } if (data.overwrite === 0) { form.append(''); } $.each(self.options.onlyMimes||[], function(i, mime) { form.append(''); }); $.each(self.customData, function(key, val) { form.append(''); }); 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; $.each(s, function(key) { if (key.substr(0, precnt) === prefix && key.substr(sufcnt) === suffix) { clrs.push(key); } }); $.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; }, /** * Set/get data into/from sessionStorage * * @param String key * @param String|void value * @return String|null */ sessionStorage : function(key, val) { var self = this, s, retval, t; try { s = window.sessionStorage; } catch(e) {} if (!s) { return; } if (val === null) { return s.removeItem(key); } 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 (this.cookieEnabled && document.cookie && document.cookie != '') { c = document.cookie.split(';'); name += '='; for (i=0; i'), /** * 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 ($.isPlainObject(opts.uiCmdMap) && Object.keys(opts.uiCmdMap).length) { if (!opts.disabledFlip) { opts.disabledFlip = {}; } disabled = opts.disabledFlip; $.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; }; $.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); $.each(self.options.disabledCmdsRels, function(com, rels) { var m, flg; if (opts.disabledFlip[com]) { flg = true; } else if (m = com.match(/^([^&]+)&([^=]+)=(.*)$/)) { if (opts.disabledFlip[m[1]] && opts[m[2]] == m[3]) { flg = true; } } if (flg) { $.each(rels, function(i, rel) { if (!opts.disabledFlip[rel]) { opts.disabledFlip[rel] = true; opts.disabled.push(rel); } }); } }); } 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 ($.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 $.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 = []; $.each(self.files(), function(h, f) { $.each(self.parents(h), function(i, ph) { if ($.inArray(ph, hashes) !== -1 && $.inArray(h, hashes) === -1) { res.push(h); return false; } }); }); return res; }, applyLeafRootStats = function(dataArr, type) { $.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 { $.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 && (!self.prevCustomData || (JSON.stringify(data.customData) !== JSON.stringify(self.prevCustomData)))) { self.prevCustomData = data.customData; try { cData = JSON.parse(data.customData); if ($.isPlainObject(cData)) { self.prevCustomData = cData; $.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 = $.grep(data.files, filter); } if (data.tree) { data.tree = $.grep(data.tree, filter); } if (data.added) { data.added = $.grep(data.added, filter); } if (data.changed) { data.changed = $.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 self = this, 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')), hiddens = this.arrayFlip(this.options.notifyDialog.hiddens || []), ndialog = this.ui.notify, dialog = ndialog.closest('.ui-dialog'), notify = ndialog.children('.elfinder-notify-'+type+(id? ('.'+id) : '')), button = notify.children('div.elfinder-notify-cancel').children('button'), ntpl = '
{msg}
', delta = opts.cnt + 0, size = (typeof opts.size != 'undefined')? parseInt(opts.size) : null, progress = (typeof opts.progress != 'undefined' && opts.progress >= 0) ? opts.progress : null, fakeint = opts.fakeinterval || 200, cancel = opts.cancel, clhover = 'ui-state-hover', close = function() { var prog = notify.find('.elfinder-notify-progress'), rm = function() { notify.remove(); if (!ndialog.children(dialog.data('minimized')? void(0) : ':visible').length) { if (dialog.data('minimized')) { dialog.data('minimized').hide(); } else { ndialog.elfinderdialog('close'); } } setProgressbar(); }; notify._esc && $(document).off('keydown', notify._esc); if (notify.data('cur') < 100) { prog.animate({ width : '100%' }, 50, function() { requestAnimationFrame(function() { rm(); }); }); } else { rm(); } }, fakeUp = function(interval) { var cur; if (notify.length) { cur = notify.data('cur') + 1; if (cur <= 98) { notify.find('.elfinder-notify-progress').width(cur + '%'); notify.data('cur', cur); setProgressbar(); setTimeout(function() { interval *= 1.05; fakeUp(interval); }, interval); } } }, setProgressbar = function() { var cnt = 0, val = 0, ntfs = ndialog.children('.elfinder-notify'), w; if (ntfs.length) { ntfs.each(function() { cnt++; val += Math.min($(this).data('cur'), 100); }); w = cnt? Math.floor(val / (cnt * 100) * 100) + '%' : 0; self.ui.progressbar.width(w); if (dialog.data('minimized')) { dialog.data('minimized').title(w); dialog.data('minimized').dialog().children('.ui-dialog-titlebar').children('.elfinder-ui-progressbar').width(w); } } else { self.ui.progressbar.width(0); dialog.data('minimized') && dialog.data('minimized').hide(); } }, cnt, total, prc; if (!type) { return this; } if (!notify.length) { notify = $(ntpl.replace(/\{type\}/g, type).replace(/\{msg\}/g, msg)); if (hiddens[type]) { notify.hide(); } else { ndialog.on('minimize', function(e) { dialog.data('minimized') && setProgressbar(); }); } notify.appendTo(ndialog).data('cnt', 0); if (progress != null) { notify.data({progress : 0, total : 0, cur : 0}); } else { notify.data({cur : 0}); fakeUp(fakeint); } if (cancel) { button = $('') .on('mouseenter mouseleave', function(e) { $(this).toggleClass(clhover, e.type === 'mouseenter'); }); notify.children('div.elfinder-notify-cancel').append(button); } ndialog.trigger('resize'); } 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 ($.isFunction(cancel) || (typeof cancel === 'object' && cancel.promise)) { notify._esc = function(e) { if (e.type == 'keydown' && e.keyCode != $.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); }); $(document).on('keydown.' + this.namespace, notify._esc); } } !opts.hideCnt && notify.children('.elfinder-notify-cnt').text('('+cnt+')'); if (delta > 0 && ndialog.is(':hidden') && !hiddens[type]) { if (dialog.data('minimized')) { dialog.data('minimized').show(); } else { 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 = Math.min(parseInt(prc/total), 100); notify.find('.elfinder-notify-progress') .animate({ width : (progress < 100 ? progress : 100)+'%' }, 20, function() { notify.data('cur', progress); setProgressbar(); }); } } 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(); $(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; $(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; $(this).elfinderdialog('close'); }; options.buttons[this.i18n(opts.reject.label)]._cssClass = 'elfinder-confirm-reject'; } if (opts.buttons && opts.buttons.length > 0) { btnNum = 1; $.each(opts.buttons, function(i, v){ options.buttons[self.i18n(v.label)] = function() { v.callback(!!(checkbox && checkbox.prop('checked'))); complete = true; $(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() { $(this).elfinderdialog('close'); }; options.buttons[this.i18n(opts.cancel.label)]._cssClass = 'elfinder-confirm-cancel'; if (opts.all) { options.create = function() { var base = $('
'); checkbox = $(''); $(this).next().find('.ui-dialog-buttonset') .prepend(base.append($('').prepend(checkbox))); }; } if (opts.optionsCallback && $.isFunction(opts.optionsCallback)) { opts.optionsCallback(options); } return this.dialog('' + 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 ($.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 = '' + res + ''; } return res; }); } else { // get HTML from jQuery object m = m.get(0).outerHTML; } input[i] = m; } return $.grep(input, function(m, i) { return $.inArray(i, ignore) === -1 ? true : false; }).join('
'); }, /** * 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="'; $.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 = $.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 ($.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) { $.each(checker, function(i, v) { v = v.toLowerCase(); if (v === 'all' || mime.indexOf(v) === 0) { ret = true; return false; } }); } return ret; }; if (mime && $.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 = $.Deferred(), ifm; try { ifm = $(''; } } } link.remove(); $(iframes) .appendTo('body') .ready(function() { setTimeout(function() { $(iframes).each(function() { $('#' + $(this).attr('id')).remove(); }); }, 20000 + (10000 * i)); // give 20 sec + 10 sec for each file to be saved }); fm.trigger('download', {files : files}); dfrd.resolve(); }); fileCnt = files.length; urls = []; for (i = 0; i < files.length; i++) { fm.openUrl(files[i].hash, true, function(v) { v && urls.push(v); if (--fileCnt < 1) { getUrlDfrd.resolve(urls); } }); } return dfrd; } }; }; /* * 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 && $.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 = $.Deferred() .fail(function(error) { error && fm.error(error); }), args = []; if (! cnt) { return dfrd.reject(); } $.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 = $(''), hval; if (heads) { $.each(heads, function(i, head) { hval = fm.escape(head.value); sel.append(''); }); } $.each(self.options.encodings, function(i, v) { sel.append(''); }); return sel; }, getDlgWidth = function() { var win = fm.options.dialogContained? fm.getUI() : $(window), m, width; if (typeof self.options.dialogWidth === 'string' && (m = self.options.dialogWidth.match(/(\d+)%/))) { width = parseInt(win.width() * (m[1] / 100)); } else { width = parseInt(self.options.dialogWidth || 650); } return Math.min(width, win.width()); }, getDlgHeight = function() { if (!self.options.dialogHeight) { return void(0); } var win = fm.options.dialogContained? fm.getUI() : $(window), m, height; if (typeof self.options.dialogHeight === 'string' && (m = self.options.dialogHeight.match(/(\d+)%/))) { height = parseInt(win.height() * (m[1] / 100)); } else { height = parseInt(self.options.dialogHeight || win.height()); } return Math.min(height, win.height()); }, /** * 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 $.grep(files, function(file) { var res; if (skip || file.mime === 'directory') { return false; } res = file.read && (allowAll || fm.mimeIsText(file.mime) || $.inArray(file.mime, cnt === 1? mimesSingle : mimes) !== -1) && (!self.onlyMimes.length || $.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 $.Deferred **/ dialog = function(id, file, content, encoding, editor, toasts) { var dfrd = $.Deferred(), _loaded = false, loaded = function() { if (!_loaded) { fm.toast({ mode: 'warning', msg: fm.i18n('nowLoading') }); return false; } return true; }, makeToasts = function() { // make toast message if (toasts && Array.isArray(toasts)) { $.each(toasts, function() { this.msg && fm.toast(this); }); } }, save = function() { var encord = selEncoding? selEncoding.val():void(0), saveDfd = $.Deferred().fail(function(err) { dialogNode.show().find('button.elfinder-btncnt-0,button.elfinder-btncnt-1').hide(); }), conf, res, tm; 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) { tm = setTimeout(function() { fm.notify({ type : 'chkcontent', cnt : 1, hideCnt: true, cancel : function() { res.reject(); } }); }, 100); res.always(function() { tm && clearTimeout(tm); fm.notify({ type : 'chkcontent', cnt: -1 }); }).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; } dialogNode.hide(); save().done(function() { _loaded = false; dialogNode.show(); cancel(); }).fail(function(err) { dialogNode.show(); err && fm.error(err); }); }, saveAs = function() { if (!loaded()) { return; } var prevOld = old, phash = file.phash, 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}; $.proxy(fm.res('mixin', 'make'), self)() .done(function(data) { var oldHash; if (data.added && data.added.length) { oldHash = ta.data('hash'); ta.data('hash', data.added[0].hash); save().done(function() { _loaded = false; dialogNode.show(); cancel(); dialogs.fadeIn(); }).fail(function() { fm.exec('rm', [data.added[0].hash], { forceRm: true, quiet: true }); ta.data('hash', oldHash); dialogNode.find('button.elfinder-btncnt-2').hide(); 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, reqInfo = 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}); } else if (!fm.file(phash)) { reqInfo = fm.request({cmd: 'info', targets: [phash]}); } $.when([reqOpen, reqInfo]).done(function() { if (reqInfo) { fm.one('infodone', function() { fm.file(phash)? make() : fail('errFolderNotFound'); }); } else { reqOpen? fm.one('cwdrender', make) : make(); } }).fail(fail); }, changed = function() { var dfd = $.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, cancel : function() { res.reject(); } }); }, 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 || (old === undefined? false : true)); }); } else { dfd.resolve(old !== res); } return dfd; }, opts = { title : fm.escape(file.name), width : getDlgWidth(), height : getDlgHeight(), 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; }).always(makeToasts); } 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'); }); makeToasts(); } 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() { var res = ta.getContent.call(ta, ta[0]); if (res === undefined || res === false || res === null) { res = $.Deferred().reject(); } return res; }, 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); } }, 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'); } }); } }, saveAsFile = {}, ta, old, dialogNode, selEncoding, extEditor, maxW, syncInterval; if (editor) { if (editor.html) { ta = $(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() { ta = $('') .on('input propertychange', stateChange); if (!editor || !editor.info || editor.info.useTextAreaEvent) { ta.on('keydown', function(e) { var code = e.keyCode, value, start; e.stopPropagation(); if (code == $.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) { // ta.hide() for performance tune. Need ta.show() in `load()` if use textarea node. ta.hide().val(content); this._setupSelEncoding(content); }; })(); } // extended function to setup selector of encoding for text editor ta._setupSelEncoding = function(content) { var heads = (encoding && encoding !== 'unknown')? [{value: encoding}] : [], wfake = $('').hide(), setSelW = function(init) { init && wfake.appendTo(selEncoding.parent()); wfake.empty().append($('').text(selEncoding.val())); selEncoding.width(wfake.width()); }; 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($('
').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 = $(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? fm.getUI() : $(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 = $.Deferred(), id = 'edit-'+fm.namespace+'-'+file.hash, d = fm.getUI().find('#'+id), conv = !convert? 0 : convert, noContent = false, 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; } noContent = editor.info.preventGet || editor.info.noContent; if (editor.info.urlAsContent || noContent) { req = $.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 { if (conv) { file.encoding = conv; fm.cache(file, 'change'); } 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 = $('
'), head = {value: data.doconv}, detected; if (data.doconv === 'unknown') { head.caption = '-'; } selEncoding = getEncSelect([head]); $(this).next().find('.ui-dialog-buttonset') .prepend(base.append($('').append(selEncoding))); }; } }); } else { if (!noContent && 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, data.toasts) .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; if (file.encoding) { file.encoding = ''; fm.cache(file, 'change'); } (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); $(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] === '*' || $.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]; } $.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 !== false && fm.mimesCanMakeEmpty[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 : ('Editor ' + i); 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 (!$.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 = []; $.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; // make public method this.getEncSelect = getEncSelect; 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'])}); $.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 = $.Deferred().resolve(); } } else { dfd = $.Deferred().resolve(); } dfd.always(function() { if (ccData) { opts.editors = $.grep(opts.editors, function(e) { if (e.info && e.info.cmdCheck) { return ccData[e.info.cmdCheck]? true : false; } else { return true; } }); } $.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: Array.isArray(editor.info.canMakeEmpty)? editor.info.canMakeEmpty : editor.mimes}); } } }); mimesSingle = ($.uniqueSort || $.unique)(mimesSingle); mimes = ($.uniqueSort || $.unique)(mimes); opts.editors = $.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 = []; $.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: $('') .attr({title: fm.i18n('select')}) .on('click touchstart', function(e){ if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) { return; } var node = $(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 = self.getMkfileHides(); $.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 = $.map(files, function(f) { return f.hash; }), list = [], editor = opts && opts.editor? opts.editor : null, node = $(opts && opts._currentNode? opts._currentNode : fm.cwdHash2Elm(hashes[0])), getEditor = function() { var dfd = $.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 = $.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, (file.encoding || void(0)), editor).fail(function(error) { error && fm.error(error); })); } if (list.length) { $.when.apply(null, list).done(function() { dfrd.resolve(); }).fail(function() { dfrd.reject(); }); } else { dfrd.reject(); } }).fail(function() { dfrd.reject(); }); return dfrd; }; this.getMkfileHides = function() { return fm.storage('mkfileHides') || fm.arrayFlip(self.options.mkfileHideMimes || []); }; }; /* * 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 $.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 = $.Deferred() .done(function() { var data = {changed: {}}; fm.toast({msg: fm.i18n(['"'+success.join('", ')+'"', 'complete', fm.i18n('cmdempty')])}); $.each(dirs, function(i, dir) { data.changed[dir.hash] = dir; }); fm.change(data); }) .always(function() { var cwd = fm.cwd().hash; fm.trigger('selectfiles', {files: $.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'](); }; $.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); $.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 $.grep(files, function(file) { return file.read && $.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 = $.Deferred(), cnt = files.length, makedir = opts && opts.makedir ? 1 : 0, i, error, decision; var overwriteAll = false; var omitAll = false; var mkdirAll = 0; var names = $.map(fm.files(hashes), function(file) { return file.name; }); var map = {}; $.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 ($.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 = ($.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 $.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 = $.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 = $.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 ? $.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 = $.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) { $.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 = '', linktpltgt = '', atpl = '
{author}
{work}
', url = /\{url\}/, link = /\{link\}/, author = /\{author\}/, work = /\{work\}/, r = 'replace', prim = 'ui-priority-primary', sec = 'ui-priority-secondary', lic = 'elfinder-help-license', tab = '
  • {title}
  • ', html = ['
    ', '
      '], stpl = '
      {pattern}
      {descrip}
      ', sep = '
      ', selfUrl = $('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 = ''; } return src; }, about = function() { html.push('
      '); html.push('

      elFinder

      '); html.push('
      '+fm.i18n('webfm')+'
      '); html.push('
      '+fm.i18n('ver')+': '+fm.version+'
      '); html.push('
      '+fm.i18n('protocolver')+':
      '); html.push('
      jQuery/jQuery UI: '+$().jquery+'/'+$.ui.version+'
      '); 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('
      '+fm.i18n('team')+'
      '); 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) { $.each(fm.i18[fm.lang].translator.split(', '), function() { html.push(atpl[r](author, $.trim(this))[r](work, fm.i18n('translator')+' ('+fm.i18[fm.lang].language+')')); }); } html.push(getTheme()); html.push(sep); html.push('
      '+fm.i18n('icons')+': Pixelmixer, Fugue, Icons8
      '); html.push(sep); html.push('
      Licence: 3-clauses BSD Licence
      '); html.push('
      Copyright © 2009-2020, Studio 42
      '); html.push('
      „ …'+fm.i18n('dontforget')+' ”
      '); html.push('
      '); }, shortcuts = function() { var sh = fm.shortcuts(); // shortcuts tab html.push('
      '); if (sh.length) { html.push('
      '); $.each(sh, function(i, s) { html.push(stpl.replace(/\{pattern\}/, s[0]).replace(/\{descrip\}/, s[1])); }); html.push('
      '); } else { html.push('
      '+fm.i18n('shortcutsof')+'
      '); } html.push('
      '); }, help = function() { // help tab html.push('
      '); html.push('DON\'T PANIC'); html.push('
      '); // end help }, useInteg = false, integrations = function() { useInteg = true; html.push('
      '); }, useDebug = false, debug = function() { useDebug = true; // debug tab html.push('
      '); html.push('
        '); html.push('
        '); // end debug }, debugRender = function() { var render = function(elm, obj) { $.each(obj, function(k, v) { elm.append($('
        ').text(k)); if (typeof v === 'undefined') { elm.append($('
        ').append($('').text('undfined'))); } else if (typeof v === 'object' && !v) { elm.append($('
        ').append($('').text('null'))); } else if (typeof v === 'object' && ($.isPlainObject(v) || v.length)) { elm.append( $('
        ').append(render($('
        '), v))); } else { elm.append($('
        ').append($('').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 = $('
      • ').html(''+self.debug.debug.cmd+'').prependTo(debugUL); target = $('
        ').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 = $('
        ').append($('').text('debug'), render($('
        '), debug.debug)); target.append(info); } if (debug.options) { info = $('
        ').append($('').text('options'), render($('
        '), 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 = $.inArray('preference', parts)) !== -1) { parts.splice(i, 1); } // debug tab require jQueryUI Tabs Widget if (! $.fn.tabs) { if ((i = $.inArray(parts, 'debug')) !== -1) { parts.splice(i, 1); } } $.each(parts, function(i, title) { html.push(tab[r](/\{id\}/g, title)[r](/\{title\}/, fm.i18n(title))); }); html.push('
      '); $.inArray('about', parts) !== -1 && about(); $.inArray('shortcuts', parts) !== -1 && shortcuts(); if ($.inArray('help', parts) !== -1) { helpSource = fm.i18nBaseUrl + 'help/%s.html.js'; help(); } $.inArray('integrations', parts) !== -1 && integrations(); $.inArray('debug', parts) !== -1 && debug(); html.push('
      '); content = $(html.join('')); content.find('.ui-tabs-nav li') .on('mouseenter mouseleave', function(e) { $(this).toggleClass('ui-state-hover', e.type === 'mouseenter'); }) .on('focus blur', 'a', function(e) { $(e.delegateTarget).toggleClass('ui-state-focus', e.type === 'focusin'); }) .children() .on('click', function(e) { var link = $(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($('
      ').html(fm.i18n('integrationWith'))); fm.bind('helpIntegration', function(e) { var ul = integDIV.children('ul:first'), data, elm, cmdUL, cmdCls; if (e.data) { if ($.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 = $('').attr('href', data.link).attr('target', '_blank').text(data.title); } else { elm = $('').text(data.title); } if (data.banner) { elm = $('').append($('').attr(data.banner), elm); } } } else { elm = $(e.data); elm.filter('a').each(function() { var tgt = $(this); if (!tgt.attr('target')) { tgt.attr('target', '_blank');; } }); } if (elm) { tabInteg.show(); if (!ul.length) { ul = $('
        ').appendTo(integDIV); } if (data && data.cmd) { cmdCls = 'elfinder-help-integration-' + data.cmd; cmdUL = ul.find('ul.' + cmdCls); if (!cmdUL.length) { cmdUL = $('
          '); ul.append($('
        • ').append($('').html(fm.i18n('cmd'+data.cmd))).append(cmdUL)); } elm = cmdUL.append($('
        • ').append(elm)); } else { ul.append($('
        • ').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() { $.ajax({ url: self.options.helpSource? self.options.helpSource : helpSource.replace('%s', fm.lang), dataType: 'html' }).done(function(source) { $('#'+fm.namespace+'-help-help').html(source); }).fail(function() { $.ajax({ url: helpSource.replace('%s', 'en'), dataType: 'html' }).done(function(source) { $('#'+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 $.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 (this.fm.cookieEnabled && cMenuType !== 'cwd' && (sel || this.fm.selected()).length) || hideCnt? 0 : -1; }; this.exec = function(hashes, opts) { var fm = this.fm, dfrd = $.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 (!$.isPlainObject(hideData)) { hideData = {}; } if (!$.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); $.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) { $.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) { $.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) { $.each(this.options.custom, function(name, details) { details.label && this.items.push(details.label); }); } this.tpl = { main : '
          {title}
          {content}
          ', itemTitle : '{name}{kind}', groupTitle : '{items}: {num}', row : '{label} : {value}', spinner : '{text} ' }; this.alwaysEnabled = true; this.updateOnSelect = false; this.shortcuts = [{ pattern : 'ctrl+i' }]; this.init = function() { $.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() { $(this).elfinderdialog('destroy'); if (reqDfrd && reqDfrd.state() === 'pending') { reqDfrd.reject(); } $.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-'+$.map(files, function(f) { return f.hash; }).join('-'), dialog = fm.getUI().find('#'+id), customActions = [], style = '', hashClass = 'elfinder-font-mono elfinder-info-hash', getHashAlgorisms = [], ndialog = fm.ui.notify, size, tmb, file, title, dcnt, rdcnt, path, hideItems, hashProg; if (ndialog.is(':hidden') && ndialog.children('.elfinder-notify').length) { ndialog.elfinderdialog('open').height('auto'); } if (!cnt) { return $.Deferred().reject(); } if (dialog.length) { dialog.elfinderdialog('toTop'); return $.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}', ''+fm.mime2kind(file)+''); 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, '')); } 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, ''+name_esc+'')); } } 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 if (file.size && file.size !== '0') { 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, SHA hashes if (window.ArrayBuffer && (fm.options.cdns.sparkmd5 || fm.options.cdns.jssha) && file.mime !== 'directory' && file.size > 0 && (!o.showHashMaxsize || file.size <= o.showHashMaxsize)) { getHashAlgorisms = []; $.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)); } }); if (getHashAlgorisms.length) { hashProg = $('
          '); reqs.push( fm.getContentsHashes(file.hash, getHashAlgorisms, o.showHashOpts, { progressBar : hashProg }).progress(function(hashes) { $.each(getHashAlgorisms, function(i, n) { if (hashes[n]) { replSpinner(hashes[n], n, hashClass); } }); }).always(function() { $.each(getHashAlgorisms, function(i, n) { replSpinner(msg.unknown, n); }); }) ); } } // Add custom info fields if (o.custom) { $.each(o.custom, function(name, details) { if ( !hideItems[details.label] && (!details.mimes || $.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 = $.grep(files, function(f) { return f.mime == 'directory' ? true : false ; }).length; if (!dcnt) { size = 0; $.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 = $.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'] : $.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 = $.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() { $(this).html(applyZWSP($(this).html(), true)); }); if (getHashAlgorisms.length) { hashProg.appendTo(dialog.find('.'+spclass+'-'+getHashAlgorisms[0]).parent()); } if (fm.UA.Mobile && $.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(){ $(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(''+name_esc+'' || name_esc, 'url'); var rfile = fm.file(file.hash); rfile.url = data.url; } else { replSpinner(name_esc, 'url'); } }); }); } // load thumbnail if (tmb) { $('') .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) { $.each(customActions, function(i, action) { try { action(file, fm, dialog); } catch(e) { fm.debug('error', e); } }); } return $.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 $.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 || $.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.getCommand('edit').getMkfileHides(); self.variants = []; if (fm.mimesCanMakeEmpty) { $.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 $.proxy(fm.res('mixin', 'make'), self)(); } err = ['errMkfile', self.getTypeName(mime, type)]; } return $.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; if (fm.cookieEnabled) { fm.one('open', function() { self.drivers = fm.netDrivers; if (self.drivers.length) { $.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 = $.Deferred(), o = self.options, create = function() { var winFocus = function() { inputs.protocol.trigger('change', 'winfocus'); }, inputs = { protocol : $('') .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); } }) .addClass('ui-corner-all') }, opts = { title : fm.i18n('netMountDialogTitle'), resizable : true, modal : true, destroyOnClose : false, open : function() { $(window).on('focus.'+fm.namespace, winFocus); inputs.protocol.trigger('change'); }, close : function() { dfrd.state() == 'pending' && dfrd.reject(); $(window).off('focus.'+fm.namespace, winFocus); }, buttons : {} }, doMount = function() { var protocol = inputs.protocol.val(), data = {cmd : 'netmount', protocol: protocol}, cur = o[protocol], mnt2res; $.each(content.find('input.elfinder-netmount-inputs-'+protocol), function(name, input) { var val, elm; elm = $(input); if (elm.is(':radio,:checkbox')) { if (elm.is(':checked')) { val = $.trim(elm.val()); } } else { val = $.trim(elm.val()); } if (val) { data[input.name] = val; } }); if (!data.host) { return fm.trigger('error', {error : 'errNetMountHostReq', opts : {modal: true}}); } if (data.mnt2res) { mnt2res = true; } fm.request({data : data, notify : {type : 'netmount', cnt : 1, hideCnt : true}}) .done(function(data) { var pdir; if (data.added && data.added.length) { mnt2res && inputs.protocol.trigger('change', 'reset'); 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 = $('
          ').on('keydown', 'input', function(e) { var comp = true, next; if (e.keyCode === $.ui.keyCode.ENTER) { $.each(form.find('input:visible:not(.elfinder-input-optional)'), function() { if ($(this).val() === '') { comp = false; next = $(this); return false; } }); if (comp) { doMount(); } else { next.trigger('focus'); } } }), hidden = $('
          '), dialog; content = $('
          ') .append($('').append($(''+fm.i18n('protocol')+'')).append($('').append(inputs.protocol))); $.each(self.drivers, function(i, protocol) { if (o[protocol]) { inputs.protocol.append(''); $.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($('').addClass('elfinder-netmount-tr elfinder-netmount-tr-'+protocol).append($(''+fm.i18n(name)+'')).append($('').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(); content.find('.elfinder-netmount-tr-' + self.drivers[0]).show(); 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).ready(function() { inputs.protocol.trigger('change'); dialog.elfinderdialog('posInit'); }); dialogNode = dialog.closest('.ui-dialog'); 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, done = function() { 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'); } }; if (d && d.protocol) { if (d.mode && d.mode === 'redirect') { // To support of third-party cookie blocking (ITP) on CORS // On iOS and iPadOS 13.4 and Safari 13.1 on macOS, the session cannot be continued when redirecting OAuth in CORS mode self.fm.request({ data : {cmd : 'netmount', protocol : d.protocol, host: d.host, user : 'init', pass : 'return', options: d.options}, preventDefault : true }).done(function(data) { d = JSON.parse(data.body); done(); }); } else { done(); } } }); }; 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 = $.Deferred() .fail(function(error) { error && fm.error(error); }), drive = fm.file(hashes[0]), childrenRoots = function(hash) { var roots = [], work; if (fm.leafRoots) { work = []; $.each(fm.leafRoots, function(phash, hashes) { var parents = fm.parents(phash), idx, deep; if ((idx = $.inArray(hash, parents)) !== -1) { idx = parents.length - idx; $.each(hashes, function(i, h) { work.push({i: idx, hash: h}); }); } }); if (work.length) { work.sort(function(a, b) { return a.i < b.i; }); $.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() { $.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']; $.each(roots, function(i, hash) { msgs.push([fm.file(hash).name]); }); return msgs; })(), accept : { label : 'btnUnmount', callback : function() { $.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, self = this; this.alwaysEnabled = true; this.noChangeDirOnRemovedCwd = true; this._handlers = { dblclick : function(e) { var arg = e.data && e.data.file? [ e.data.file ]: void(0); if (self.getstate(arg) === 0) { e.preventDefault(); fm.exec('open', arg); } }, '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) ? ($.grep(sel, function(file) { return file.mime == 'directory' || ! file.read ? false : true;}).length == cnt ? 0 : -1) : -1; }; this.exec = function(hashes, cOpts) { var dfrd = $.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 = $.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 openCB = function(url) { var link = $('').hide().appendTo($('body')); 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) }); $.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; } $(wnd).trigger('focus'); } link.remove(); }, wnd, target, getOnly; try { reg = new RegExp(fm.option('dispInlineRegex'), 'i'); } catch(e) { reg = false; } // open files html5dl = (typeof $('').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)); fm.openUrl(file.hash, !inline, openCB); } 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) { $.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 = $.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 = $.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 = $.Deferred() .fail(function(error) { error && fm.error(error); }) .always(function() { fm.unlockfiles({files : $.map(files, function(f) { return f.hash; })}); }), copy = function(files) { return files.length && fm._commands.duplicate ? fm.exec('duplicate', files) : $.Deferred().resolve(); }, paste = function(files) { var dfrd = $.Deferred(), existed = [], hashes = {}, intersect = function(files, names) { var ret = [], i = files.length; while (i--) { $.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 { $.each(names, function(i, v) { exists[v.name] = v.hash; }); existed = intersect(files, $.map(exists, function(h, n) { return n; })); $.each(files, function(i, file) { if (exists[file.name]) { hashes[exists[file.name]] = file.name; } }); } } } else { existedArr = []; existed = $.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 = $.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 = $.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}, cancel : true, 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 $.each(files, function(i, f) { var phash = f.phash, srcHash = function(name) { var hash; $.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 = []; $.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 $.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(flg) { dfrd.reject(); if (flg === 0) { // canceling fm.sync(); } }) .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 = $.map(files, function(f) { return f.name; }); dst.hash == fm.cwd().hash ? valid($.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, cutDfrd; 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); $.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 ($.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 ($.inArray(dst.hash, fparents) !== -1) { if ($.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; } cutDfrd = $.Deferred(); if (cut && self.options.moveConfirm) { fm.confirm({ title : 'moveFiles', text : fm.i18n('confirmMove', dst.i18 || dst.name), accept : { label : 'btnYes', callback : function() { cutDfrd.resolve(); } }, cancel : { label : 'btnCancel', callback : function() { cutDfrd.reject(); } } }); } else { cutDfrd.resolve(); } cutDfrd.done(function() { $.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([]); }); }).fail(function() { dfrd.reject(); }); 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 $.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 $.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 = '
        • {title}
        • ', base = $('
          '), ul = $('
            '), tabs = $('
            '), sep = '
            ', selfUrl = $('base').length? document.location.href.replace(/#.*$/, '') : '', selectTab = function(tab) { $('#'+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', 'useFullscreen', 'showHidden'], 'dialog' : ['autoFocusDialog'], 'selectionInfo' : ['infoItems', 'hashChecker'], 'reset' : ['clearBrowserData'], 'all' : true }, forms = self.options.prefs || ['language', 'theme', 'toolbarPref', 'iconSize', 'columnPref', 'selectAction', 'makefileTypes', 'useStoredEditor', 'editorMaximized', 'useFullscreen', 'showHidden', 'infoItems', 'hashChecker', 'autoFocusDialog', 'clearBrowserData']; if (!fm.cookieEnabled) { delete cats.language; } forms = fm.arrayFlip(forms, true); if (fm.options.getFileCallback) { delete forms.selectAction; } if (!fm.UA.Fullscreen) { delete forms.useFullscreen; } forms.language && (forms.language = (function() { var langSel = $('').on('change', function() { var lang = $(this).val(); fm.storage('lang', lang); $('#'+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', fr_CA: 'Français (Canada)', 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: '正體中文' }; if (!fm.cookieEnabled) { return $(); } $.each(langs, function(lang, name) { optTags.push(''); }); 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 = $('').on('change', function() { var theme = $(this).val(); fm.changeTheme(theme).storage('theme', theme); }), optTags = [], tpl = { image: '', link: '$2', data: '
            $1
            $2
            ' }, items = ['image', 'description', 'author', 'email', 'license'], render = function(key, data) { }, defBtn = $('').text(fm.i18n('default')).on('click', function(e) { themeSel.val('default').trigger('change'); }), list = $('
            ').on('click', 'button', function() { var val = $(this).data('themeid'); themeSel.val(val).trigger('change'); }); if (!fm.options.themes.default) { themeSel.append(''); } $.each(fm.options.themes, function(id, val) { var opt = $(''), dsc = $('
            '+fm.i18n(id)+'
            '), 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 = $(), 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)); $.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 = $(elm).on('click', function() { themeSel.val(id).trigger('change'); }).attr('title', fm.i18n('select')); } dl.append(elm); } }); val = val.add(dl); val = val.add($('
            ').append($('').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 $('
            ').append(themeSel.val(fm.theme && fm.theme.id? fm.theme.id : 'default'), defBtn, list); })()); forms.toolbarPref && (forms.toolbarPref = (function() { var pnls = $.map(fm.options.uiOptions.toolbar, function(v) { return $.isArray(v)? v : null; }), tags = [], hides = fm.storage('toolbarhides') || {}; $.each(pnls, function() { var cmd = this, name = fm.i18n('cmd'+cmd); if (name === 'cmd'+cmd) { name = fm.i18n(cmd); } tags.push(''); }); return $(tags.join(' ')).on('change', 'input', function() { var v = $(this).val(), o = $(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') || fm.options.uiOptions.cwd.iconsView.size || 0, sld = $('
            ').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') || {}; $.each(cols, function() { var key = this, name = fm.getColumnName(key); tags.push(''); }); return $(tags.join(' ')).on('change', 'input', function() { var v = $(this).val(), o = $(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 = $('').on('change', function() { var act = $(this).val(); fm.storage('selectAction', act === 'default'? null : act); }), optTags = [], acts = self.options.selectActions, defAct = fm.getCommand('open').options.selectAction || 'open'; if ($.inArray(defAct, acts) === -1) { acts.unshift(defAct); } $.each(acts, function(i, act) { var names = $.map(act.split('/'), function(cmd) { var name = fm.i18n('cmd'+cmd); if (name === 'cmd'+cmd) { name = fm.i18n(cmd); } return name; }); optTags.push(''); }); return actSel.append(optTags.join('')).val(fm.storage('selectAction') || defAct); })()); forms.makefileTypes && (forms.makefileTypes = (function() { var hides = fm.getCommand('edit').getMkfileHides(), getTag = function() { var tags = []; // re-assign hides hides = fm.getCommand('edit').getMkfileHides(); $.each(fm.mimesCanMakeEmpty, function(mime, type) { var name = fm.getCommand('mkfile').getTypeName(mime, type); tags.push(''); }); return tags.join(' '); }, elm = $('
            ').on('change', 'input', function() { var v = $(this).val(), o = $(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 = $('
            ').append( $('').on('keydown', function(e) { (e.keyCode === $.ui.keyCode.ENTER) && $(this).next().trigger('click'); }), $('').html(fm.i18n('add')).on('click', function() { var input = $(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()); } }); }), $('').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 $('
            ').append(elm, add); })()); forms.useStoredEditor && (forms.useStoredEditor = $('').prop('checked', (function() { var s = fm.storage('useStoredEditor'); return s? (s > 0) : fm.options.commandsOptions.edit.useStoredEditor; })()).on('change', function(e) { fm.storage('useStoredEditor', $(this).is(':checked')? 1 : -1); })); forms.editorMaximized && (forms.editorMaximized = $('').prop('checked', (function() { var s = fm.storage('editorMaximized'); return s? (s > 0) : fm.options.commandsOptions.edit.editorMaximized; })()).on('change', function(e) { fm.storage('editorMaximized', $(this).is(':checked')? 1 : -1); })); forms.useFullscreen && (forms.useFullscreen = $('').prop('checked', (function() { var s = fm.storage('useFullscreen'); return s? (s > 0) : fm.options.commandsOptions.fullscreen.mode === 'screen'; })()).on('change', function(e) { fm.storage('useFullscreen', $(this).is(':checked')? 1 : -1); })); if (forms.showHidden) { (function() { var setTitle = function() { var s = fm.storage('hide'), t = [], v; if (s && s.items) { $.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, '
            ')).tooltip('close'); }, chk = $('').prop('checked', (function() { var s = fm.storage('hide'); return s && s.show; })()).on('change', function(e) { var o = {}; o[$(this).is(':checked')? 'show' : 'hide'] = true; fm.exec('hide', void(0), o); }), btn = $('').append(fm.i18n('reset')).on('click', function() { fm.exec('hide', void(0), {reset: true}); $(this).parent().find('input:first').prop('checked', false); setTitle(); }), elms = $().add(chk).add(btn), useTooltip; forms.showHidden = $('
            ').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 && $.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); $.each(items, function() { var key = this, name = fm.i18n(key); tags.push(''); }); return $(tags.join(' ')).on('change', 'input', function() { var v = $(this).val(), o = $(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); $.each(fm.hashCheckers, function() { var cmd = this, name = fm.i18n(cmd); tags.push(''); }); return $(tags.join(' ')).on('change', 'input', function() { var v = $(this).val(), o = $(this).is(':checked'); if (o) { enabled[v] = true; } else if (enabled[v]) { delete enabled[v]; } fm.storage('hashchekcer', $.grep(fm.hashCheckers, function(v) { return enabled[v]; })); }); })()); forms.autoFocusDialog && (forms.autoFocusDialog = $('').prop('checked', (function() { var s = fm.storage('autoFocusDialog'); return s? (s > 0) : fm.options.uiOptions.dialog.focusOnMouseOver; })()).on('change', function(e) { fm.storage('autoFocusDialog', $(this).is(':checked')? 1 : -1); })); forms.clearBrowserData && (forms.clearBrowserData = $('').text(fm.i18n('reset')).button().on('click', function(e) { e.preventDefault(); fm.storage(); $('#'+fm.id).elfinder('reload'); })); $.each(cats, function(id, prefs) { var dls, found; if (prefs === true) { found = 1; } else if (prefs) { dls = $(); $.each(prefs, function(i, n) { var f, title, chks = '', cbox; if (f = forms[n]) { found = 2; title = fm.i18n(n); cbox = $(f).filter('input[type="checkbox"]'); if (!cbox.length) { cbox = $(f).find('input[type="checkbox"]'); } if (cbox.length === 1) { if (!cbox.attr('id')) { cbox.attr('id', 'elfinder-preference-'+n+'-checkbox'); } title = ''; } else if (cbox.length > 1) { chks = ' elfinder-preference-checkboxes'; } dls = dls.add($('
            '+title+'
            ')).add($('
            ').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( $('
            ') .hide() .append($('
            ').append(dls)) ); } } }); ul.on('click', 'a', function(e) { var t = $(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(); $(h).show(); } }).on('focus blur', 'a', function(e) { $(this).parent().toggleClass('ui-state-focus', e.type === 'focusin'); }).on('mouseenter mouseleave', 'li', function(e) { $(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 $.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) { $(document).trigger($.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 || fm.options.dialogContained, win = contain? fm.getUI() : $(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 = $.ui.keyCode.LEFT, rightKey = $.ui.keyCode.RIGHT, coverEv = 'mousemove touchstart ' + ('onwheel' in document? 'wheel' : 'onmousewheel' in document? 'mousewheel' : 'DOMMouseScroll'), title = $(''), icon = $('
            '), info = $('
            '),//.hide(), cover = $('
            '), fsicon = $('
            ') .on('click touchstart', function(e) { if (navmove) { return; } var win = self.window, full = win.hasClass(fullscreen), $window = $(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'); $(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; }); } }); } $(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; $.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 : $.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 = $('
            ').on('click touchstart', function(e) { ! navmove && navtrigger(leftKey); return false; }), next = $('
            ').on('click touchstart', function(e) { ! navmove && navtrigger(rightKey); return false; }), navbar = $('
            ') .append(prev) .append(fsicon) .append(next) .append('
            ') .append($('
            ').on('click touchstart', function(e) { ! navmove && self.window.trigger('close'); return false; })) , titleClose = $('').on('mousedown', function(e) { e.stopPropagation(); self.window.trigger('close'); }), titleDock = $('').on('mousedown', function(e) { e.stopPropagation(); if (! self.docked()) { self.window.trigger('navdockin'); } else { self.window.trigger('navdockout'); } }), spinner = '' + fm.i18n('calc') + '' + '', navStyle = '', init = true, dockHeight, getSize, tm4cwd, dockedNode, selectTm; /** * Any flags for each plugin */ this.flags = {}; this.cover = cover; this.evUpdate = evUpdate; (this.navbar = navbar)._show = navShow; this.resize = 'resize.'+fm.namespace; this.info = $('
            ').addClass(infocls) .append(icon) .append(info); this.autoPlay = function() { if (self.opened()) { return !! self.options[self.docked()? 'dockAutoplay' : 'autoplay']; } return false; }; this.preview = $('
            ') // 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 = '
            {value}
            ', 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))) { $('') .hide() .appendTo(self.preview) .on('load', function() { icon.addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); $(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 = $('
            ') .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( $('
            ') .append( $('').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() { preview.off('changesize'); 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 = $.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 $(document).on('keydown.'+fm.namespace, function(e) { e.keyCode == $.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($.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(); } }); $.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()) { $.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 $.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'], getDimSize = ql.fm.returnBytes((ql.options.getDimThreshold || 0)), 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 $.each(navigator.mimeTypes, function(i, o) { var mime = o.type; if (mime.indexOf('image/') === 0 && $.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, prog, m, opDfd; 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 = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); img = $('') .hide() .appendTo(preview) .on('load', function() { hideInfo(); show(); }) .on('error', function() { loading.remove(); }); opDfd = fm.openUrl(file.hash, false, function(url) { img.attr('src', url); }, { progressBar: prog }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); if (file.width && file.height) { show(); } else if (file.size > getDimSize) { 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(); } }); } } }); }, /** * TIFF image preview * * @param object ql elFinder.commands.quicklook */ function(ql) { var fm = ql.fm, mime = 'image/tiff', preview = ql.preview; if (window.Worker && window.Uint8Array) { preview.on(ql.evUpdate, function(e) { var file = e.file, err = function(e) { wk && wk.terminate(); loading.remove(); fm.debug('error', e); }, setdim = function(dim) { var rfile = fm.file(file.hash); rfile.width = dim[0]; rfile.height = dim[1]; }, loading, prog, url, base, wk, opDfd; if (file.mime === mime) { e.stopImmediatePropagation(); loading = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); // stop loading on change file if not loaded yet preview.one('change', function() { wk && wk.terminate(); loading.remove(); }); opDfd = fm.getContents(file.hash, 'arraybuffer', { progressBar: prog }).done(function(data) { if (data) { base = $('
            ').css({width:'100%',height:'100%'}).hide().appendTo(preview); try { wk = fm.getWorker(); wk.onmessage = function(res) { var data = res.data, cv, co, id, prop; wk && wk.terminate(); cv = document.createElement('canvas'); co = cv.getContext('2d'); cv.width = data.width; cv.height = data.height; id = co.createImageData(data.width, data.height); (id).data.set(new Uint8Array(data.image)); co.putImageData(id, 0, 0); base.append(cv).show(); loading.remove(); prop = (data.width/data.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); } $(cv).width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0); }).trigger('changesize'); if (!file.width || !file.height) { setdim([data.width, data.height]); } ql.hideinfo(); }; wk.onerror = err; wk.postMessage({ scripts: [fm.options.cdns.tiff, fm.getWorkerUrl('quicklook.tiff.js')], data: { data: data } }); } catch(e) { err(e); } } else { err(); } }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); } }); } }, /** * 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, prog, m, _define, _require, opDfd; 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 = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); opDfd = fm.openUrl(file.hash, 'sameorigin', function(url) { if (url) { 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); } ); } } }, { progressBar: prog }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); } }); }, /** * 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, prog; if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) { e.stopImmediatePropagation(); loading = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); // 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, progressBar : prog }) .done(function(data) { ql.hideinfo(); var doc = $('').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 = $('').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, prog; 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 = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); // 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, progressBar : prog }) .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 || []) : [], win = ql.window, navi = ql.navbar, setNavi = function() { navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '30px' : ''); }; if (opts.url) { preview.on('update', function(e) { var file = e.file, node, loading, prog, opDfd; if (mimes[file.mime] && (file.mime !== 'application/pdf' || !opts.pdfNative || !ql.flags.pdfNative)) { e.stopImmediatePropagation(); loading = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); opDfd = fm.openUrl(file.hash, 'sameorigin', function(url) { if (url) { node = $('') .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); win.on('viewchange.viewerjs', setNavi); setNavi(); preview.one('change', function() { win.off('viewchange.viewerjs'); loading.remove(); node.off('load').remove(); }); } }, { progressBar: prog }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); } }); } } }, /** * 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 { $.each(navigator.plugins, function(i, plugins) { $.each(plugins, function(i, plugin) { if (plugin.type === mime) { return !(active = true); } }); }); } ql.flags.pdfNative = active; if (active) { if (typeof ql.options.pdfToolbar !== 'undefined' && !ql.options.pdfToolbar) { urlhash = '#toolbar=0'; } preview.on(ql.evUpdate, function(e) { var file = e.file, opDfd; if (active && file.mime === mime && ql.dispInlineRegex.test(file.mime)) { e.stopImmediatePropagation(); opDfd = fm.openUrl(file.hash, false, function(url) { if (url) { ql.hideinfo(); ql.cover.addClass('elfinder-quicklook-coverbg'); $('') .on('error', function(e) { active = false; ql.update(void(0), fm.cwd()); ql.update(void(0), file); }) .appendTo(preview); } }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); } }); } }, /** * Flash preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, mime = 'application/x-shockwave-flash', preview = ql.preview, active = false; $.each(navigator.plugins, function(i, plugins) { $.each(plugins, function(i, plugin) { if (plugin.type === mime) { return !(active = true); } }); }); active && preview.on(ql.evUpdate, function(e) { var file = e.file, node, opDfd; if (file.mime === mime && ql.dispInlineRegex.test(file.mime)) { e.stopImmediatePropagation(); opDfd = fm.openUrl(file.hash, false, function(url) { if (url) { ql.hideinfo(); node = $('') .appendTo(preview); } }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); } }); }, /** * 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 $('') .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 = $.Deferred(), loader = $.Deferred().done(function() { var opDfd; opDfd = fm.getContents(hash, 'arraybuffer', { progressBar: prog }).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(); }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.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; } }, loading, prog; preview.on(ql.evUpdate, function(e) { var file = e.file, type = mimes[file.mime], html5, opDfd; if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime) && ((html5 = ql.support.audio[type]) || (type === 'amr'))) { autoplay = ql.autoPlay(); curHash = file.hash; if (!html5) { if (fm.options.cdns.amr && type === 'amr' && AMR !== false) { e.stopImmediatePropagation(); loading = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); node = getNode('', curHash); amrToWavUrl(file.hash).done(function(url) { loading.remove(); 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(); loading = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); opDfd = fm.openUrl(curHash, false, function(url) { loading.remove(); if (url) { node = getNode(url, curHash); play(node[0]); win.on('viewchange.audio', setNavi); setNavi(); } else { node.remove(); } }, { progressBar: prog }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); } } }).one('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/mpeg' : 'mpeg', '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', 'video/x-msvideo' : 'avi' }, node, win = ql.window, navi = ql.navbar, cHls, cDash, pDash, cFlv, cVideojs, autoplay, tm, loading, prog, 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 = $('') .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(''); } node.appendTo(preview); win.on('viewchange.video', setNavi); setNavi(); }, loadHls = function(file) { var hls, opDfd; opDfd = fm.openUrl(file.hash, false, function(url) { loading.remove(); if (url) { render(file); hls = new cHls(); hls.loadSource(url); hls.attachMedia(node[0]); if (autoplay) { hls.on(cHls.Events.MANIFEST_PARSED, function() { play(node[0]); }); } } }, { progressBar: prog }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); }, loadDash = function(file) { var opDfd; opDfd = fm.openUrl(file.hash, false, function(url) { var debug; loading.remove(); if (url) { render(file); pDash = window.dashjs.MediaPlayer().create(); debug = pDash.getDebug(); if (debug.setLogLevel) { debug.setLogLevel(dashjs.Debug.LOG_LEVEL_FATAL); } else if (debug.setLogToBrowserConsole) { debug.setLogToBrowserConsole(false); } pDash.initialize(node[0], url, autoplay); pDash.on('error', function(e) { reset(true); }); } }, { progressBar: prog }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); }, loadFlv = function(file) { var opDfd if (!cFlv.isSupported()) { cFlv = false; return; } opDfd = fm.openUrl(file.hash, false, function(url) { loading.remove(); if (url) { var player = cFlv.createPlayer({ type: 'flv', url: url }); render(file); player.on(cFlv.Events.ERROR, function() { player.destroy(); reset(true); }); player.attachMediaElement(node[0]); player.load(); play(player); } }, { progressBar: prog }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); }, loadVideojs = function(file) { var opDfd; opDfd = fm.openUrl(file.hash, false, function(url) { loading.remove(); if (url) { render(file); node[0].src = url; cVideojs(node[0], { src: url }); } }, { progressBar: prog }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); }, 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, opDfd; 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(); loading = $('
            '+fm.i18n('nowLoading')+'
            '); prog = $('
            ').appendTo(loading); if (ql.support.video[type] && (type !== 'm3u8' || fm.UA.Safari)) { e.stopImmediatePropagation(); loading.appendTo(ql.info.find('.elfinder-quicklook-info')); opDfd = fm.openUrl(file.hash, false, function(url) { loading.remove(); if (url) { render(file, { src: url }); play(node[0]); } }, { progressBar: prog }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); } else { if (cHls !== false && fm.options.cdns.hls && type === 'm3u8') { e.stopImmediatePropagation(); loading.appendTo(ql.info.find('.elfinder-quicklook-info')); 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(); loading.appendTo(ql.info.find('.elfinder-quicklook-info')); 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(); loading.appendTo(ql.info.find('.elfinder-quicklook-info')); 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; } } ); } } else if (fm.options.cdns.videojs) { e.stopImmediatePropagation(); loading.appendTo(ql.info.find('.elfinder-quicklook-info')); if (cVideojs) { loadVideojs(file); } else { fm.loadScript( [ fm.options.cdns.videojs + '/video.min.js' ], function(res) { cVideojs = res || window.videojs || false; //window.flvjs = stock; cVideojs && loadVideojs(file); }, { tryRequire: true, error : function() { cVideojs = false; } } ).loadCss([fm.options.cdns.videojs + '/video-js.min.css']); } } } } }).one('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; $.each(navigator.plugins, function(i, plugins) { $.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, opDfd, loading, prog, setNavi = function() { navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : ''); }; if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime)) { e.stopImmediatePropagation(); loading = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); opDfd = ql.fm.openUrl(file.hash, false, function(url) { loading.remove(); if (url) { (video = mime.indexOf('video/') === 0) && ql.hideinfo(); node = $('') .appendTo(preview); win.on('viewchange.embed', setNavi); setNavi(); } }, { progressBar: prog }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); } }).one('change', function() { if (node && node.parent().length) { win.off('viewchange.embed'); node.remove(); node= null; } }); }, /** * Archive(zip|gzip|tar|bz2) 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', 'application/x-bzip2']), preview = ql.preview, sizeMax = fm.returnBytes(ql.options.unzipMaxSize || 0), Zlib = (fm.options.cdns.zlibUnzip && fm.options.cdns.zlibGunzip)? true : false, bzip2 = fm.options.cdns.bzip2? true : false; if (window.Worker && window.Uint8Array && window.DataView) { preview.on(ql.evUpdate, function(e) { var file = e.file, isTar = (file.mime === 'application/x-tar'), isBzip2 = (file.mime === 'application/x-bzip2'), isZlib = (file.mime === 'application/zip' || file.mime === 'application/x-gzip'); if (mimes[file.mime] && (!sizeMax || file.size <= sizeMax) && ( isTar || (isBzip2 && bzip2) || (isZlib && Zlib) )) { var jqxhr, wk, loading, prog, url, req = function() { jqxhr = fm.getContents(file.hash, 'arraybuffer', { progressBar: prog }) .fail(function() { loading.remove(); }) .done(function(data) { var unzip, filenames, err = function(e) { wk && wk.terminate(); loading.remove(); if (isZlib) { Zlib = false; } else if (isBzip2) { bzip2 = false; } fm.debug('error', e); }; try { wk = fm.getWorker(); wk.onmessage = function(res) { wk && wk.terminate(); loading.remove(); if (!res.data || res.data.error) { new Error(res.data && res.data.error? res.data.error : ''); } else { makeList(res.data.files); } }; wk.onerror = err; if (file.mime === 'application/x-tar') { wk.postMessage({ scripts: [fm.getWorkerUrl('quicklook.unzip.js')], data: { type: 'tar', bin: data } }); } else if (file.mime === 'application/zip') { wk.postMessage({ scripts: [fm.options.cdns.zlibUnzip, fm.getWorkerUrl('quicklook.unzip.js')], data: { type: 'zip', bin: data } }); } else if (file.mime === 'application/x-gzip') { wk.postMessage({ scripts: [fm.options.cdns.zlibGunzip, fm.getWorkerUrl('quicklook.unzip.js')], data: { type: 'gzip', bin: data } }); } else if (file.mime === 'application/x-bzip2') { wk.postMessage({ scripts: [fm.options.cdns.bzip2, fm.getWorkerUrl('quicklook.unzip.js')], data: { type: 'bzip2', bin: data } }); } } catch (e) { err(e); } }); }, makeList = function(filenames) { var header, list, doc, tsize = 0; if (filenames && filenames.length) { filenames = $.map(filenames, function(str) { return fm.decodeRawString(str); }); filenames.sort(); list = fm.escape(filenames.join("\n").replace(/\{formatSize\((\d+)\)\}/g, function(m, s) { tsize += parseInt(s); return fm.formatSize(s); })); header = ''+fm.escape(file.mime)+' ('+fm.formatSize(file.size)+' / '+fm.formatSize(tsize)+')'+'
            '; doc = $('
            '+header+'
            '+list+'
            ') .on('touchstart', function(e) { if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) { e.originalEvent._preventSwipeX = true; } }) .appendTo(preview); ql.hideinfo(); } loading.remove(); }; // this is our file - stop event propagation e.stopImmediatePropagation(); loading = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); // stop loading on change file if not loaded yet preview.one('change', function() { jqxhr.state() === 'pending' && jqxhr.reject(); wk && wk.terminate(); loading.remove(); }); req(); } }); } }, /** * 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, prog, 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; } $.each(archive.entries, function() { filenames.push(this.path + (this.size? ' (' + fm.formatSize(this.size) + ')' : '')); }); if (filenames.length) { filenames = $.map(filenames, function(str) { return fm.decodeRawString(str); }); filenames.sort(); header = ''+fm.escape(file.mime)+' ('+fm.formatSize(file.size)+')'+'
            '; doc = $('
            '+header+'
            '+fm.escape(filenames.join("\n"))+'
            ') .on('touchstart', function(e) { if ($(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, opDfd; // this is our file - stop event propagation e.stopImmediatePropagation(); loading = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); // stop loading on change file if not loaded yet preview.one('change', function() { archive && (archive.abort = true); loading.remove(); abort = true; }); opDfd = fm.openUrl(file.hash, 'sameorigin', function(url) { if (url) { 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 } ); } } }, { progressBar: prog, temporary: true }); // stop loading on change file if not loaded yet preview.one('change', function() { opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject(); }); } }); } }, /** * 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, prog, url; e.stopImmediatePropagation(); if (file.url == '1') { preview.hide(); $('
            ').appendTo(ql.info.find('.elfinder-quicklook-info')) .on('click', function() { var self = $(this); self.html(''); fm.request({ data : {cmd : 'url', target : file.hash}, preventDefault : true, progressBar : prog }) .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 = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); url = fm.convAbsUrl(fm.url(file.hash)); node = $('') .css('background-color', 'transparent') .appendTo(preview) .on('load', function() { ql.hideinfo(); loading.remove(); ql.preview.after(ql.info); $(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, prog) { var mapsOpts = ql.options.googleMapsOpts.maps; fm.forExternalUrl(file.hash, { progressBar: prog }).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, prog, url, node; e.stopImmediatePropagation(); loading = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); if (getLink) { preview.hide(); $('
            ').appendTo(ql.info.find('.elfinder-quicklook-info')) .on('click', function() { var self = $(this); self.html(''); fm.request({ data : {cmd : 'url', target : file.hash}, preventDefault : true, progressBar : prog }) .always(function() { loading.remove(); 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 = $('
            ').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, prog); }); } else { loadMap(file, node, prog); } } } }); } }, /** * 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, dfd; // 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, prog, url, tm; if (type === 'm') { if ((mLimits[ext] && file.size > mLimits[ext]) || file.size > mLimits.other) { type = 'g'; } } if (getLink) { preview.hide(); $('
            ').appendTo(ql.info.find('.elfinder-quicklook-info')) .on('click', function() { var self = $(this); self.html(''); 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() { dfd && dfd.status && dfd.status() === 'pending' && dfd.reject(); win.off('viewchange.googledocs'); loading.remove(); node.off('load').remove(); node = null; }).addClass('elfinder-overflow-auto'); loading = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); node = $('') .css('background-color', 'transparent') .appendTo(preview); dfd = fm.forExternalUrl(file.hash, { progressBar: prog }).done(function(url) { var load = function() { try { if (node && (!node.attr('src') || node.get(0).contentWindow.document/*maybe HTTP 204*/)) { node.attr('src', 'https://' + urls[type] + encodeURIComponent(url)); // Retry because Google Docs viewer sometimes returns HTTP 204 tm = setTimeout(load, 2000); } } catch(e) {} }; if (url) { if (file.ts) { url += (url.match(/\?/)? '&' : '?') + '_t=' + file.ts; } node.on('load', function() { tm && clearTimeout(tm); ql.hideinfo(); loading.remove(); ql.preview.after(ql.info); $(this).css('background-color', '#fff').show(); }) .on('error', function() { tm && clearTimeout(tm); loading.remove(); ql.preview.after(ql.info); }); load(); } 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) { "use strict"; var fm = ql.fm, preview = ql.preview, textLines = parseInt(ql.options.textInitialLines) || 150, prettifyLines = parseInt(ql.options.prettifyMaxLines) || 500, PR, _PR, error = function() { prettify = function() { return false; }; _PR && (window.PR = _PR); PR = false; }, prettify = function(node) { if (fm.options.cdns.prettify) { prettify = function(node) { setTimeout(function() { PRcheck(node); }, 100); return 'pending'; }; if (window.PR) { _PR = window.PR; } fm.loadScript([fm.options.cdns.prettify + (fm.options.cdns.prettify.match(/\?/)? '&' : '?') + 'autorun=false'], function(wPR) { PR = wPR || window.PR; if (typeof PR === 'object') { prettify = function() { return true; }; if (_PR) { window.PR = _PR; } else { delete window.PR; } exec(node); } else { error(); } }, { tryRequire: true, error : error }); } else { error(); } }, exec = function(node) { if (node && !node.hasClass('prettyprinted')) { node.css('cursor', 'wait'); requestAnimationFrame(function() { PR.prettyPrint && PR.prettyPrint(null, node.get(0)); node.css('cursor', ''); }); } }, PRcheck = function(node) { var status = prettify(node); if (status === true) { exec(node); } }; preview.on(ql.evUpdate, function(e) { var file = e.file, mime = file.mime, jqxhr, loading, prog, encSelect; if (fm.mimeIsText(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax) && PR !== false) { e.stopImmediatePropagation(); loading = $('
            '+fm.i18n('nowLoading')+'
            ').appendTo(ql.info.find('.elfinder-quicklook-info')); prog = $('
            ').appendTo(loading); // stop loading on change file if not loadin yet preview.one('change', function() { jqxhr.state() == 'pending' && jqxhr.reject(); encSelect && encSelect.remove(); }); jqxhr = fm.request({ data : {cmd : 'get', target : file.hash, conv : (file.encoding || 1), _t : file.ts}, options : {type: 'get', cache : true}, preventDefault : true, progressBar : prog }) .done(function(data) { var reg = new RegExp('^(data:'+file.mime.replace(/([.+])/g, '\\$1')+';base64,)', 'i'), text = data.content, part, more, node, lines, m; if (typeof text !== 'string') { return; } ql.hideinfo(); if (window.atob && (m = text.match(reg))) { text = atob(text.substr(m[1].length)); } lines = text.match(/([^\r\n]{1,100}[\r\n]*)/g); more = lines.length - textLines; if (more > 10) { part = lines.splice(0, textLines).join(''); } else { more = 0; } node = $('
            '); if (more) { node.append($('

            ' + fm.i18n('linesLeft', fm.toLocaleString(more)) + '
            ') .on('click', function() { var top = node.scrollTop(); $(this).remove(); node.children('pre').removeClass('prettyprinted').text(text).scrollTop(top); if (lines.length <= prettifyLines) { PRcheck(node); } }) ); } node.children('pre').text(part || text); node.on('touchstart', function(e) { if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) { e.originalEvent._preventSwipeX = true; } }).appendTo(preview); // make toast message if (data.toasts && Array.isArray(data.toasts)) { $.each(data.toasts, function() { this.msg && fm.toast(this); }); } PRcheck(node); }) .always(function(data) { var cmdEdit, sel, head; if (cmdEdit = fm.getCommand('edit')) { head = []; if (data && data.encoding) { head.push({value: data.encoding}); } head.push({value: 'UTF-8'}); sel = cmdEdit.getEncSelect(head); sel.on('change', function() { file.encoding = sel.val(); fm.cache(file, 'change'); preview.trigger({ type: ql.evUpdate, file: file, forceUpdate: true }); }); encSelect = $('
            ').append(sel); ql.window.append(encSelect); } 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: $('') .attr({title: fm.i18n('autoSync')}) .on('click touchstart', function(e){ if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) { return; } e.stopPropagation(); e.preventDefault(); $(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(){ $(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 { $('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) && !file.netkey) { 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', $.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 = '', mkChk = function(node, label) { return $('').prepend(node); }, name = $(''), num = $(tplr), prefix = $(tplr), suffix = $(tplr), extention = $(tplr), checks = $('
            ').append( mkChk(num, 'plusNumber'), mkChk(prefix, 'asPrefix'), mkChk(suffix, 'asSuffix'), mkChk(extention, 'changeExtention') ), preview = $('
            '), node = $('
            ').append( $('
            ').append(name), $('
            ').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 ($.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 && ((fm.cookieEnabled && isRoot) || !sel[0].locked) || (fm.api > 2.1030 && cnt === $.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: $('') .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 = $.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 = $.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 = $(tarea? '' : '') .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 == $.ui.keyCode.ESCAPE) { dfrd.reject(); } else if (e.keyCode == $.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 && $(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 && $.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'))) { $.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 fm = this.fm, 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}; $.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) }); }); $.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 = 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 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 $.Deferred().reject(error); } }; this.exec = function(hashes) { var self = this, files = this.files(hashes), dfrd = $.Deferred(), api2 = (fm.api > 1), options = this.options, dialogWidth = 650, fmnode = fm.getUI(), ctrgrup = $().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, src) { var isJpeg = (file.mime === 'image/jpeg'), dialog = $('
            '), input = '', row = '
            ', label = '
            ', changeTm = null, operate = false, opStart = function() { operate = true; }, opStop = function() { if (operate) { operate = false; control.trigger('change'); } }, control = $('
            ') .on('focus', 'input[type=text],input[type=number]', function() { $(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) { if (blob) { size1 = blob.size; quty.next('span').text(' (' + fm.formatSize(blob.size) + ')'); } }, 'image/jpeg', Math.max(Math.min(quty.val(), 100), 1) / 100); } } }); }) .on('mouseup', 'input', function(e) { $(e.target).trigger('change'); }), preview = $('
            ') .on('touchmove', function(e) { if ($(e.target).hasClass('touch-punch')) { e.stopPropagation(); e.preventDefault(); } }), spinner = $('
            '+fm.i18n('ntfloadimg')+'
            '), rhandle = $('
            '), rhandlec = $('
            '), uiresize = $('
            '), uicrop = $('
            '), uirotate = $('
            '), uideg270 = $('').attr('title',fm.i18n('rotate-cw')).append($('')), uideg90 = $('').attr('title',fm.i18n('rotate-ccw')).append($('')), uiprop = $(''), reset = $('') .on('mouseenter mouseleave', function(e) { $(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 : $.map(data.added, function(f) {return f.hash;})}); }); }); }) ); } else { fm.trigger('selectfiles', {files : $.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 = $.map(fm.files(hash), function(f) { return (f.mime === 'directory' && f.write)? f : null; }); if (! dirs.length) { return $(); } return $('
            ') .on('click', function(e) { e.stopPropagation(); e.preventDefault(); dirs = fm.sortFiles(dirs); var $this = $(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'), '|' ]; $.each(dirs, function(i, f) { raw.push(getRaw(f, 'dir')); }); $this.trigger('blur'); fm.trigger('contextmenu', { raw: raw, x: e.pageX || $(this).offset().left, y: e.pageY || $(this).offset().top, prevNode: base, fitHeight: true }); }).append(''); }, inputButton = function(type, caption) { var button, input = $('') .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 = $('
            ').append(input).on('click', function(e) { e.stopPropagation(); }); return $('
            '+fm.i18n(caption)+'
            ') .append(form) .on('click', function(e) { e.stopPropagation(); e.preventDefault(); input.trigger('click'); }) .on('mouseenter mouseleave', function(e) { $(this).toggleClass(hover, e.type === 'mouseenter'); }); }, dfrd = $.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 && $.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) { $(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 $(v).remove(); } }); if ($(my).find('a,img').length) { type = 'html'; } src = my.innerHTML; my.innerHTML = ''; upload({files : [ src ], type : type}); } }); }; dialog = $('
            ') .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 = $('
            ') .append('') .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 = $('
            ') .on('paste', function(e){ paste(e); }) .on('mousedown click', function(){ $(this).trigger('focus'); }) .on('focus', function(){ this.innerHTML = ''; }) .on('mouseover', function(){ $(this).addClass(hover); }) .on('mouseout', function(){ $(this).removeClass(hover); }) .on('dragenter', function(e) { e.stopPropagation(); e.preventDefault(); $(this).addClass(hover); }) .on('dragleave', function(e) { e.stopPropagation(); e.preventDefault(); $(this).removeClass(hover); }) .on('dragover', function(e) { e.stopPropagation(); e.preventDefault(); e.originalEvent.dataTransfer.dropEffect = 'copy'; $(this).addClass(hover); }) .on('drop', function(e) { dialog.elfinderdialog('close'); targets && (e.originalEvent._target = targets[0]); dropUpload(e.originalEvent); }) .prependTo(dialog) .after('
            '+fm.i18n('or')+'
            ')[0]; } else { pastebox = $('
            '+fm.i18n('dropFilesBrowser')+'
            ') .on('paste drop', function(e){ paste(e); }) .on('mousedown click', function(){ $(this).trigger('focus'); }) .on('focus', function(){ this.innerHTML = ''; }) .on('dragenter mouseover', function(){ $(this).addClass(hover); }) .on('dragleave mouseout', function(){ $(this).removeClass(hover); }) .prependTo(dialog) .after('
            '+fm.i18n('or')+'
            ')[0]; } uidialog = this.fmDialog(dialog, { title : this.title + '' + (targetDir? ' - ' + fm.escape(targetDir.i18 || targetDir.name) : '') + '', 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: $('') .attr({title: fm.i18n('viewtype')}) .on('click touchstart', function(e){ if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) { return; } var node = $(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 = this.value == 'list' ? 'icons' : 'list'; fm.storage('view', value); 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: $('') .attr({title: fm.i18n('cmdview')}) .on('click touchstart', function(e){ if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) { return; } var node = $(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; }));