/*! * elFinder - file manager for web * Version 2.1.67 (2026-04-17) * http://elfinder.org * * Copyright 2009-2026, 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 = ''; /** * Base URL of worker js files * baseUrl + "js/worker/" when empty value * * @type String */ this.workerBaseUrl = ''; /** * 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.workerBaseUrl = (this.options.workerBaseUrl || this.baseUrl + 'js/worker').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'), // the tree option is enabled (for "open" command) isTree = (data.tree === 1), // 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 && !isTree) { 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 Stopper * * @type Boolean */ this.syncStopper = false; /** * Sync content * * @return jQuery.Deferred */ this.sync = function(onlydir, polling) { if (this.syncStopper) { return $.Deferred().reject(); } this.syncStopper = true; 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(), odataRoots, dfrd = $.Deferred().always(function() { !reqFail && self.trigger('sync'); }), tree = (! onlydir && this.ui.tree) ? 1 : 0, opts = [this.request({ data : {cmd : 'open', reload : 1, target : cwd, tree : tree, 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')); } // When tree = 1, the server will return all volumes in response to the open command. // Remove volumes from the tree command that do not exist anymore. if (tree && pdata && pdata.tree) { odataRoots = $.map($.grep(odata.files, function(f) {return f.isroot;}), function(f) {return f.hash;}); pdata.tree = $.grep(pdata.tree, function(f) {return !f.isroot || odataRoots.indexOf(f.hash) >= 0;}); } 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.syncStopper = false; 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 && typeof s.callback === 'function') { 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 (! self.options.noResizeBySelf) { 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 { try { if (typeof res.data !== 'string') { return; } obj = JSON.parse(res.data); if (obj.type !== "io.studio-42.github") { return; } data = obj.data || null; } catch (e2) { return; } 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) { var res = columnNames[key] || self.i18n(key); return typeof res === 'function'? res() : res; }; /** * 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){ if (self._commands.copy) { 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 (typeof cmd === 'function' && !self._commands[name] && (cmd.prototype.forceLoad || $.inArray(name, self.options.commands) !== -1)) { extendsCmd = cmd.prototype.extendsCmd || ''; if (extendsCmd) { if (typeof self.commands[extendsCmd] === 'function') { 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 (typeof lcmd === 'function' && !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 : window.navigator.cookieEnabled, /** * 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', 'image/webp' : 'WEBP', 'application/vnd.ms-fontobject' : 'EOT', 'font/sfnt' : 'SFNT', 'application/font-sfnt' : 'SFNT', 'font/ttf' : 'TTF', 'font/opentype' : 'OTF', 'font/otf' : 'OTF', 'application/x-font-opentype' : 'OTF', 'font/woff' : 'WOFF', 'application/font-woff' : 'WOFF', 'font/woff2' : 'WOFF2', 'application/font-woff2' : 'WOFF2', '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 instanceof File) { pushItem(entry); } else 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){ if (item.kind === 'file') { return (item.getAsEntry? item.getAsEntry() : item.webkitGetAsEntry()) || item.getAsFile(); } else { return null; } }); $.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 && self.options.enableRootRename !== false) { 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 (typeof cancel === 'function' || (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 && typeof opts.optionsCallback === 'function') { 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]; } } else if (this.mimeTypes[mime]) { kind = this.mimeTypes[mime].toUpperCase(); if (!this.messages['kind'+kind]) { kind = null; } } 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 if (mime.indexOf('font') === 0) { kind = 'Font'; } 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 (typeof rawStringDecoder === 'function') { 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, filter = function(files) { var fres = true; return $.grep(files, function(f) { fres = fres && f.read && f.phash === fm.cwd().hash && ! fm.isRoot(f)? true : false; return fres; }); }; return cnt && fm.cwd().write && filter(sel).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() { if (!self.options.confirmUnsavedBeforeClose) { ta.elfinderdialog('close'); } else { 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']; var btnDiscard = { label : 'btnDiscard', callback : function() { close(); } } if (change) { if (typeof change === 'string') { msgs.unshift(change); } fm.confirm({ title : self.title, text : msgs, accept : accept, cancel : { label : 'btnCancel', callback : $.noop }, buttons : onlySaveAs? [btnDiscard] : [{ label : 'btnSaveAs', callback : function() { ta.elfinderdialog('destroy'); requestAnimationFrame(saveAs); } }, btnDiscard] }); } else { 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); } } }, headerBtnCloseAction : self.options.confirmUnsavedBeforeClose ? function() { cancel(); } : undefined, close : function() { if (self.options.confirmUnsavedBeforeClose) { return; } 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, filter = function(files) { var fres = true; return $.grep(files, function(f) { fres = fres && f.read && f.write && f.mime === 'directory' ? true : false; return fres; }); }; cnt = sel.length; return filter(sel).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) { var fres = true; return $.grep(files, function(file) { fres = fres && file.read && $.inArray(file.mime, mimes) !== -1 ? true : false; return fres; }); }; 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, cwdHash, cwdChk; if (!cnt || filter(sel).length != cnt) { return -1; } else if (fm.searchStatus.state > 0) { cwdHash = this.fm.cwd().hash; $.each(sel, function(i, file) { cwdChk = (file.phash === cwdHash); return cwdChk; }); return cwdChk? 0 : -1; } else { return this.fm.cwd().write? 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, overwriteAll = false, omitAll = false, mkdirAll = 0, siblings = fm.files(files[0].phash), names = [], map = {}; $.each(siblings, function(id, file) { map[file.name] = file; names.push(file.name); }); 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) { var full; e.preventDefault(); e.stopPropagation(); if (data && data.fullscreen) { full = (data.fullscreen === 'on'); self.update(void(0), full); self.title = fm.i18n(full ? 'reinstate' : 'cmdfullscreen'); } }; 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, fres = true; files = $.grep(files, function(file) { fres = fres && (file.mime != 'directory' || o.folders) && file.read ? true : false; return fres; }); 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? fm.escape(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 (nao-pon)<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-2024, Studio 42 / nao-pon
      '); 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, filter = function(files) { var fres = true; return $.grep(files, function(f) { fres = fres && f.read && ! f.locked? true : false; return fres; }); }; if (curOrg === 'navbar') { return cnt && sel[0].write && sel[0].read? 0 : -1; } else { return cwd.write && (!cnt || filter(sel).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, filter = function(files) { var fres = true; return $.grep(files, function(file) { fres = fres && file.mime == 'directory' || ! file.read ? false : true; return fres; }); }; 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) { var fres = true; return $.grep(self.files(hashes), function(f) { fres = fres && f.mime == 'directory' ? true : false; return fres; }); }, 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.parse || 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, cssClass = '', canPlay; //reset(); pDash = null; opts = opts || {}; if (opts.cssClass) { cssClass = ' ' + opts.cssClass; } 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, { src: url, cssClass: 'video-js' }); node[0].src = url; cVideojs(node[0], { autoplay: true }); } }, { 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 if (this.fm.options.enableRootRename !== false) { this.alwaysEnabled = true; } this.syncTitleOnChange = true; var self = this, fm = self.fm, enableRootRename = fm.options.enableRootRename !== false, 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 && enableRootRename) { 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 && ((enableRootRename && 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) || !enableRootRename)) { 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 = '', idx, errors; if (trf) { if (trf.types && trf.types.length) { if ((idx = $.inArray('application/x-moz-file', trf.types)) !== -1) { kind = 'file'; } else if ((idx = $.inArray('Files', trf.types)) !== -1) { kind = 'file'; } } else if (trf.items && trf.items.length && trf.items[0].kind) { kind = trf.items[0].kind; } 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[idx].getAsEntry || trf.items[idx].webkitGetAsEntry || trf.items[idx].getAsFile)) { 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; }));/** * עברית translation * @author Yaron Shahrabani * @version 2015-11-02 */ (function(root, factory) { if (typeof define === 'function' && define.amd) { define(['elfinder'], factory); } else if (typeof exports !== 'undefined') { module.exports = factory(require('elfinder')); } else { factory(root.elFinder); } }(this, function(elFinder) { elFinder.prototype.i18.he = { translator : 'Yaron Shahrabani ', language : 'עברית', direction : 'rtl', dateFormat : 'd.m.Y H:i', // Mar 13, 2012 05:27 PM fancyDateFormat : '$1 H:i', // will produce smth like: Today 12:25 PM messages : { /********************************** errors **********************************/ 'error' : 'שגיאה', 'errUnknown' : 'שגיאה בלתי מוכרת.', 'errUnknownCmd' : 'פקודה בלתי מוכרת.', 'errJqui' : 'תצורת ה־jQuery UI שגויה. יש לכלול רכיבים הניתנים לבחירה, גרירה והשלכה.', 'errNode' : 'elFinder דורש יצירה של רכיב DOM.', 'errURL' : 'התצורה של elFinder שגויה! אפשרות הכתובת (URL) לא הוגדרה.', 'errAccess' : 'הגישה נדחית.', 'errConnect' : 'לא ניתן להתחבר למנגנון.', 'errAbort' : 'החיבור בוטל.', 'errTimeout' : 'זמן החיבור פג.', 'errNotFound' : 'לא נמצא מנגנון.', 'errResponse' : 'תגובת המנגנון שגויה.', 'errConf' : 'תצורת המנגנון שגויה.', 'errJSON' : 'המודול PHP JSON לא מותקן.', 'errNoVolumes' : 'אין כוננים זמינים לקריאה.', 'errCmdParams' : 'פרמטרים שגויים לפקודה „$1“.', 'errDataNotJSON' : 'הנתונים אינם JSON.', 'errDataEmpty' : 'הנתונים ריקים.', 'errCmdReq' : 'בקשה למנגנון דורשת שם פקודה.', 'errOpen' : 'לא ניתן לפתוח את „$1“.', 'errNotFolder' : 'הפריט אינו תיקייה.', 'errNotFile' : 'הפריט אינו קובץ.', 'errRead' : 'לא ניתן לקרוא את „$1“.', 'errWrite' : 'לא ניתן לכתוב אל „$1“.', 'errPerm' : 'ההרשאה נדחתה.', 'errLocked' : '„$1“ נעול ואין אפשרות לשנות את שמו, להעבירו או להסירו.', 'errExists' : 'קובץ בשם „$1“ כבר קיים.', 'errInvName' : 'שם הקובץ שגוי.', 'errFolderNotFound' : 'התיקייה לא נמצאה.', 'errFileNotFound' : 'הקובץ לא נמצא.', 'errTrgFolderNotFound' : 'תיקיית היעד „$1“ לא נמצאה.', 'errPopup' : 'הדפדפן מנע פתיחת חלון קובץ. כדי לפתוח קובץ יש לאפשר זאת בהגדרות הדפדפן.', 'errMkdir' : 'לא ניתן ליצור את התיקייה „$1“.', 'errMkfile' : 'לא ניתן ליצור את הקובץ „$1“.', 'errRename' : 'לא ניתן לשנות את השם של „$1“.', 'errCopyFrom' : 'העתקת קבצים מהכונן „$1“ אינה מאופשרת.', 'errCopyTo' : 'העתקת קבצים אל הכונן „$1“ אינה מאופשרת.', 'errUpload' : 'שגיאת העלאה.', // old name - errUploadCommon 'errUploadFile' : 'לא ניתן להעלות את „$1“.', // old name - errUpload 'errUploadNoFiles' : 'לא נמצאו קבצים להעלאה.', 'errUploadTotalSize' : 'הנתונים חורגים מהגודל המרבי המותר.', // old name - errMaxSize 'errUploadFileSize' : 'הקובץ חורג מהגודל המרבי המותר.', // old name - errFileMaxSize 'errUploadMime' : 'סוג הקובץ אינו מורשה.', 'errUploadTransfer' : 'שגיאת העברה „$1“.', 'errNotReplace' : 'הפריט „$1“ כבר קיים במיקום זה ואי אפשר להחליפו בפריט מסוג אחר.', // new 'errReplace' : 'לא ניתן להחליף את „$1“.', 'errSave' : 'לא ניתן לשמור את „$1“.', 'errCopy' : 'לא ניתן להעתיק את „$1“.', 'errMove' : 'לא ניתן להעביר את „$1“.', 'errCopyInItself' : 'לא ניתן להעתיק את „$1“ לתוך עצמו.', 'errRm' : 'לא ניתן להסיר את „$1“.', 'errRmSrc' : 'לא ניתן להסיר את קובצי המקור.', 'errExtract' : 'לא ניתן לחלץ קבצים מהארכיון „$1“.', 'errArchive' : 'לא ניתן ליצור ארכיון.', 'errArcType' : 'סוג הארכיון אינו נתמך.', 'errNoArchive' : 'הקובץ אינו ארכיון או שסוג הקובץ שלו אינו נתמך.', 'errCmdNoSupport' : 'המנגנון אינו תומך בפקודה זו.', 'errReplByChild' : 'לא ניתן להחליף את התיקייה „$1“ בפריט מתוכה.', 'errArcSymlinks' : 'מטעמי אבטחה לא ניתן לחלץ ארכיונים שמכילים קישורים סימבוליים או קבצים עם שמות בלתי מורשים.', // edited 24.06.2012 'errArcMaxSize' : 'הארכיון חורג מהגודל המרבי המותר.', 'errResize' : 'לא ניתן לשנות את הגודל של „$1“.', 'errResizeDegree' : 'מעלות ההיפוך שגויות.', // added 7.3.2013 'errResizeRotate' : 'לא ניתן להפוך את התמונה.', // added 7.3.2013 'errResizeSize' : 'גודל התמונה שגוי.', // added 7.3.2013 'errResizeNoChange' : 'גודל התמונה לא השתנה.', // added 7.3.2013 'errUsupportType' : 'סוג הקובץ אינו נתמך.', 'errNotUTF8Content' : 'הקובץ „$1“ הוא לא בתסדיר UTF-8 ולא ניתן לערוך אותו.', // added 9.11.2011 'errNetMount' : 'לא ניתן לעגן את „$1“.', // added 17.04.2012 'errNetMountNoDriver' : 'פרוטוקול בלתי נתמך.', // added 17.04.2012 'errNetMountFailed' : 'העיגון נכשל.', // added 17.04.2012 'errNetMountHostReq' : 'נדרש מארח.', // added 18.04.2012 'errSessionExpires' : 'ההפעלה שלך פגה עקב חוסר פעילות.', 'errCreatingTempDir' : 'לא ניתן ליצור תיקייה זמנית: „$1“', 'errFtpDownloadFile' : 'לא ניתן להוריד קובץ מ־ FTP: „$1“', 'errFtpUploadFile' : 'לא ניתן להעלות קובץ ל־FTP: „$1“', 'errFtpMkdir' : 'לא ניתן ליצור תיקייה מרוחקת ב־FTP: „$1“', 'errArchiveExec' : 'שמירת הקבצים בארכיון נכשלה: „$1“', 'errExtractExec' : 'חילוץ קבצים נכשל: „$1“', /******************************* commands names ********************************/ 'cmdarchive' : 'יצירת ארכיון', 'cmdback' : 'חזרה', 'cmdcopy' : 'העתקה', 'cmdcut' : 'גזירה', 'cmddownload' : 'הורדה', 'cmdduplicate' : 'שכפול', 'cmdedit' : 'עריכת קובץ', 'cmdextract' : 'חילוץ קבצים מארכיון', 'cmdforward' : 'העברה', 'cmdgetfile' : 'בחירת קבצים', 'cmdhelp' : 'פרטים על התכנית הזו', 'cmdhome' : 'בית', 'cmdinfo' : 'קבלת מידע', 'cmdmkdir' : 'תיקייה חדשה', 'cmdmkfile' : 'קובץ חדש', 'cmdopen' : 'פתיחה', 'cmdpaste' : 'הדבקה', 'cmdquicklook' : 'תצוגה מקדימה', 'cmdreload' : 'רענון', 'cmdrename' : 'שינוי שם', 'cmdrm' : 'מחיקה', 'cmdsearch' : 'חיפוש קבצים', 'cmdup' : 'מעבר לתיקיית ההורה', 'cmdupload' : 'העלאת קבצים', 'cmdview' : 'תצוגה', 'cmdresize' : 'שינוי גודל והיפוך', 'cmdsort' : 'מיון', 'cmdnetmount' : 'עיגון כונן רשת', // added 18.04.2012 /*********************************** buttons ***********************************/ 'btnClose' : 'סגירה', 'btnSave' : 'שמירה', 'btnRm' : 'הסרה', 'btnApply' : 'החלה', 'btnCancel' : 'ביטול', 'btnNo' : 'לא', 'btnYes' : 'כן', 'btnDiscard': 'Discard changes', 'btnMount' : 'עיגון', // added 18.04.2012 /******************************** notifications ********************************/ 'ntfopen' : 'פתיחת תיקייה', 'ntffile' : 'פתיחת קובץ', 'ntfreload' : 'רענון תוכן התיקייה', 'ntfmkdir' : 'תיקייה נוצרת', 'ntfmkfile' : 'קבצים נוצרים', 'ntfrm' : 'קבצים נמחקים', 'ntfcopy' : 'קבצים מועתקים', 'ntfmove' : 'קבצים מועברים', 'ntfprepare' : 'העתקת קבצים בהכנה', 'ntfrename' : 'שמות קבצים משתנים', 'ntfupload' : 'קבצים נשלחים', 'ntfdownload' : 'קבצים מתקבלים', 'ntfsave' : 'שמירת קבצים', 'ntfarchive' : 'ארכיון נוצר', 'ntfextract' : 'מחולצים קבצים מארכיון', 'ntfsearch' : 'קבצים בחיפוש', 'ntfresize' : 'גודל קבצים משתנה', 'ntfsmth' : 'מתבצעת פעולה', 'ntfloadimg' : 'נטענת תמונה', 'ntfnetmount' : 'כונן רשת מעוגן', // added 18.04.2012 'ntfdim' : 'ממדי תמונה מתקבלים', // added 20.05.2013 /************************************ dates **********************************/ 'dateUnknown' : 'לא ידוע', 'Today' : 'היום', 'Yesterday' : 'מחר', 'msJan' : 'ינו׳', 'msFeb' : 'פבר׳', 'msMar' : 'מרץ', 'msApr' : 'אפר׳', 'msMay' : 'מאי', 'msJun' : 'יונ׳', 'msJul' : 'יול׳', 'msAug' : 'אוג׳', 'msSep' : 'ספט׳', 'msOct' : 'אוק׳', 'msNov' : 'נוב׳', 'msDec' : 'דצמ׳', 'January' : 'ינואר', 'February' : 'פברואר', 'March' : 'מרץ', 'April' : 'אפריל', 'May' : 'מאי', 'June' : 'יוני', 'July' : 'יולי', 'August' : 'אוגוסט', 'September' : 'ספטמבר', 'October' : 'אוקטובר', 'November' : 'נובמבר', 'December' : 'דצמבר', 'Sunday' : 'יום ראשון', 'Monday' : 'יום שני', 'Tuesday' : 'יום שלישי', 'Wednesday' : 'יום רביעי', 'Thursday' : 'יום חמישי', 'Friday' : 'יום שישי', 'Saturday' : 'שבת', 'Sun' : 'א׳', 'Mon' : 'ב׳', 'Tue' : 'ג׳', 'Wed' : 'ד׳', 'Thu' : 'ה', 'Fri' : 'ו׳', 'Sat' : 'ש׳', /******************************** sort variants ********************************/ 'sortname' : 'לפי שם', 'sortkind' : 'לפי סוג', 'sortsize' : 'לפי גודל', 'sortdate' : 'לפי תאריך', 'sortFoldersFirst' : 'תיקיות תחילה', /********************************** messages **********************************/ 'confirmReq' : 'נדרש אישור', 'confirmRm' : 'להסיר את הקבצים?
            פעולה זו בלתי הפיכה!', 'confirmRepl' : 'להחליף קובץ ישן בקובץ חדש?', 'apllyAll' : 'להחיל על הכול', 'name' : 'שם', 'size' : 'גודל', 'perms' : 'הרשאות', 'modify' : 'שינוי', 'kind' : 'סוג', 'read' : 'קריאה', 'write' : 'כתיבה', 'noaccess' : 'אין גישה', 'and' : 'וגם', 'unknown' : 'לא ידוע', 'selectall' : 'בחירת כל הקבצים', 'selectfiles' : 'בחירת קובץ אחד ומעלה', 'selectffile' : 'בחירת הקובץ הראשון', 'selectlfile' : 'בחירת הקובץ האחרון', 'viewlist' : 'תצוגת רשימה', 'viewicons' : 'תצוגת סמלים', 'places' : 'מיקומים', 'calc' : 'חישוב', 'path' : 'נתיב', 'aliasfor' : 'כינוי עבור', 'locked' : 'נעול', 'dim' : 'ממדים', 'files' : 'קבצים', 'folders' : 'תיקיות', 'items' : 'פריטים', 'yes' : 'כן', 'no' : 'לא', 'link' : 'קישור', 'searcresult' : 'תוצאות חיפוש', 'selected' : 'קבצים נבחרים', 'about' : 'על אודות', 'shortcuts' : 'קיצורי דרך', 'help' : 'עזרה', 'webfm' : 'מנהל קבצים בדפדפן', 'ver' : 'גרסה', 'protocolver' : 'גרסת פרוטוקול', 'homepage' : 'דף הבית של המיזם', 'docs' : 'תיעוד', 'github' : 'פילוג עותק ב־Github', 'twitter' : 'לעקוב אחרינו בטוויטר', 'facebook' : 'להצטרף אלינו בפייסבוק', 'team' : 'צוות', 'chiefdev' : 'מפתח ראשי', 'developer' : 'מתכנת', 'contributor' : 'תורם', 'maintainer' : 'מתחזק', 'translator' : 'מתרגם', 'icons' : 'סמלים', 'dontforget' : 'לא לשכוח לקחת את המגבת שלך', 'shortcutsof' : 'קיצורי הדרך מנוטרלים', 'dropFiles' : 'ניתן להשליך את הקבצים לכאן', 'or' : 'או', 'selectForUpload' : 'לבחור קבצים להעלאה', 'moveFiles' : 'העברת קבצים', 'copyFiles' : 'העתקת קבצים', 'rmFromPlaces' : 'הסרה ממיקומים', 'aspectRatio' : 'יחס תצוגה', 'scale' : 'מתיחה', 'width' : 'רוחב', 'height' : 'גובה', 'resize' : 'שינוי הגודל', 'crop' : 'חיתוך', 'rotate' : 'היפוך', 'rotate-cw' : 'היפוך ב־90 מעלות נגד השעון', 'rotate-ccw' : 'היפוך ב־90 מעלות עם השעון CCW', 'degree' : '°', 'netMountDialogTitle' : 'עיגון כונן רשת', // added 18.04.2012 'protocol' : 'פרוטוקול', // added 18.04.2012 'host' : 'מארח', // added 18.04.2012 'port' : 'פתחה', // added 18.04.2012 'user' : 'משתמש', // added 18.04.2012 'pass' : 'ססמה', // added 18.04.2012 /********************************** mimetypes **********************************/ 'kindUnknown' : 'בלתי ידוע', 'kindFolder' : 'תיקייה', 'kindAlias' : 'כינוי', 'kindAliasBroken' : 'כינוי שבור', // applications 'kindApp' : 'יישום', 'kindPostscript' : 'מסמך Postscript', 'kindMsOffice' : 'מסמך Microsoft Office', 'kindMsWord' : 'מסמך Microsoft Word', 'kindMsExcel' : 'מסמך Microsoft Excel', 'kindMsPP' : 'מצגת Microsoft Powerpoint', 'kindOO' : 'מסמך Open Office', 'kindAppFlash' : 'יישום Flash', 'kindPDF' : 'Portable Document Format (PDF)', 'kindTorrent' : 'קובץ Bittorrent', 'kind7z' : 'ארכיון 7z', 'kindTAR' : 'ארכיון TAR', 'kindGZIP' : 'ארכיון GZIP', 'kindBZIP' : 'ארכיון BZIP', 'kindXZ' : 'ארכיון XZ', 'kindZIP' : 'ארכיון ZIP', 'kindRAR' : 'ארכיון RAR', 'kindJAR' : 'קובץ JAR של Java', 'kindTTF' : 'גופן True Type', 'kindOTF' : 'גופן Open Type', 'kindRPM' : 'חבילת RPM', // fonts 'kindFont' : 'גופן', 'kindSFNT' : 'גופן SFNT', 'kindEOT' : 'גופן Embedded Open Type', 'kindWOFF' : 'גופן Web Open Font Format', 'kindWOFF2' : 'גופן Web Open Font Format 2', // texts 'kindText' : 'מסמך טקסט', 'kindTextPlain' : 'טקסט פשוט', 'kindPHP' : 'מקור PHP', 'kindCSS' : 'גיליון סגנון מדורג', 'kindHTML' : 'מסמך HTML', 'kindJS' : 'מקור Javascript', 'kindRTF' : 'תבנית טקסט עשיר', 'kindC' : 'מקור C', 'kindCHeader' : 'מקור כותרת C', 'kindCPP' : 'מקור C++', 'kindCPPHeader' : 'מקור כותרת C++', 'kindShell' : 'תסריט מעטפת יוניקס', 'kindPython' : 'מקור Python', 'kindJava' : 'מקור Java', 'kindRuby' : 'מקור Ruby', 'kindPerl' : 'תסריט Perl', 'kindSQL' : 'מקור SQL', 'kindXML' : 'מקור XML', 'kindAWK' : 'מקור AWK', 'kindCSV' : 'ערכים מופרדים בפסיקים', 'kindDOCBOOK' : 'מסמךDocbook XML', // images 'kindImage' : 'תמונה', 'kindBMP' : 'תמונת BMP', 'kindJPEG' : 'תמונת JPEG', 'kindGIF' : 'תמונת GIF', 'kindPNG' : 'תמונת PNG', 'kindTIFF' : 'תמונת TIFF', 'kindTGA' : 'תמונת TGA', 'kindPSD' : 'תמונת Adobe Photoshop', 'kindXBITMAP' : 'תמונת מפת סיביות X', 'kindPXM' : 'תמונת Pixelmator', // media 'kindAudio' : 'מדיה מסוג שמע', 'kindAudioMPEG' : 'שמע MPEG', 'kindAudioMPEG4' : 'שמע MPEG-4', 'kindAudioMIDI' : 'שמע MIDI', 'kindAudioOGG' : 'שמע Ogg Vorbis', 'kindAudioWAV' : 'שמע WAV', 'AudioPlaylist' : 'רשימת נגינה MP3', 'kindVideo' : 'מדיה מסוג וידאו', 'kindVideoDV' : 'סרטון DV', 'kindVideoMPEG' : 'סרטון MPEG', 'kindVideoMPEG4' : 'סרטון MPEG-4', 'kindVideoAVI' : 'סרטון AVI', 'kindVideoMOV' : 'סרטון Quick Time', 'kindVideoWM' : 'סרטון Windows Media', 'kindVideoFlash' : 'סרטון Flash', 'kindVideoMKV' : 'סרטון Matroska', 'kindVideoOGG' : 'סרטון Ogg' } }; })); Page not found – Kashan Foods

            Ooops... Error 404

            We are sorry, but the page you are looking for does not exist.

            Please check entered address and try again or go to homepage

            Open chat
            Get in touch on WhatsApp!