0byt3m1n1
Path:
/
data
/
applications
/
aps
/
tikiwiki
/
14.1-0
/
standard
/
htdocs
/
vendor_extra
/
elfinder
/
js
/
[
Home
]
File: elfinder.full.js
/*! * elFinder - file manager for web * Version 2.x github.com/jonnybradley/elFinder (2015-01-26) * http://elfinder.org * * Copyright 2009-2015, Studio 42 * Licensed under a 3 clauses BSD license */ (function($) { /* * File: /js/elFinder.js */ /** * @class elFinder - file manager for web * * @author Dmitry (dio) Levashov **/ window.elFinder = function(node, opts) { this.time('load'); var self = this, /** * Node on which elfinder creating * * @type jQuery **/ node = $(node), /** * Store node contents. * * @see this.destroy * @type jQuery **/ prevContent = $('<div/>').append(node.contents()), /** * Store node inline styles * * @see this.destroy * @type String **/ prevStyle = node.attr('style'), /** * Instance ID. Required to get/set cookie * * @type String **/ id = node.attr('id') || '', /** * Events namespace * * @type String **/ namespace = 'elfinder-'+(id || Math.random().toString().substr(2, 7)), /** * Mousedown event * * @type String **/ mousedown = 'mousedown.'+namespace, /** * Keydown event * * @type String **/ keydown = 'keydown.'+namespace, /** * Keypress event * * @type String **/ keypress = 'keypress.'+namespace, /** * Is shortcuts/commands enabled * * @type Boolean **/ enabled = true, /** * Store enabled value before ajax requiest * * @type Boolean **/ prevEnabled = true, /** * 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', 'dragstart', 'dragstop'], /** * Rules to validate data from backend * * @type Object **/ rules = {}, /** * Current working directory hash * * @type String **/ cwd = '', /** * Current working directory options * * @type Object **/ cwdOptions = { path : '', url : '', tmbUrl : '', disabled : [], separator : '/', archives : [], extract : [], copyOverwrite : true, tmb : false // old API }, /** * Files/dirs cache * * @type Object **/ files = {}, /** * 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 Array **/ remember = [], /** * Queue for 'open' requests * * @type Array **/ queue = [], /** * Commands prototype * * @type Object **/ base = new self.command(self), /** * elFinder node width * * @type String * @default "auto" **/ width = 'auto', /** * elFinder node height * * @type Number * @default 400 **/ height = 400, beeper = $(document.createElement('audio')).hide().appendTo('body')[0], syncInterval, open = function(data) { if (data.init) { // init - reset cache files = {}; } else { // remove only files from prev cwd for (var i in files) { if (files.hasOwnProperty(i) && files[i].mime != 'directory' && files[i].phash == cwd && $.inArray(i, remember) === -1) { delete files[i]; } } } cwd = data.cwd.hash; cache(data.files); if (!files[cwd]) { cache([data.cwd]); } self.lastDir(cwd); }, /** * Store info about files/dirs in "files" object. * * @param Array files * @return void **/ cache = function(data) { var l = data.length, f; while (l--) { f = data[l]; if (f.name && f.hash && f.mime) { if (!f.phash) { var name = 'volume_'+f.name, i18 = self.i18n(name); if (name != i18) { f.i18 = i18; } } files[f.hash] = f; } } }, /** * Exec shortcut * * @param jQuery.Event keydown/keypress event * @return void */ execShortcut = function(e) { var code = e.keyCode, ctrlKey = !!(e.ctrlKey || e.metaKey); 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 == 9 && !$(e.target).is(':input')) { e.preventDefault(); } } }, date = new Date(), utc, i18n ; /** * 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 = []; /** * User os. Required to bind native shortcuts for open/rename * * @type String **/ this.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 **/ this.UA = (function(){ var webkit = !document.uniqueID && !window.opera && !window.sidebar && window.localStorage && typeof window.orientation == "undefined"; return { // 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", IE:document.uniqueID, Firefox:window.sidebar, Opera:window.opera, Webkit:webkit, Chrome:webkit && window.chrome, Safari:webkit && !window.chrome, Mobile:typeof window.orientation != "undefined" } })(); /** * Configuration options * * @type Object **/ this.options = $.extend(true, {}, this._options, opts||{}); if (opts.ui) { this.options.ui = opts.ui; } if (opts.commands) { this.options.commands = opts.commands; } if (opts.uiOptions && opts.uiOptions.toolbar) { this.options.uiOptions.toolbar = opts.uiOptions.toolbar; } $.extend(this.options.contextmenu, opts.contextmenu); /** * Ajax request type * * @type String * @default "get" **/ this.requestType = /^(get|post)$/i.test(this.options.requestType) ? this.options.requestType.toLowerCase() : 'get', /** * Any data to send across every ajax request * * @type Object * @default {} **/ this.customData = $.isPlainObject(this.options.customData) ? this.options.customData : {}; /** * ID. Required to create unique cookie name * * @type String **/ this.id = id; /** * URL to upload files * * @type String **/ this.uploadURL = opts.urlUpload || opts.url; /** * Events namespace * * @type String **/ this.namespace = namespace; /** * Interface language * * @type String * @default "en" **/ this.lang = this.i18[this.options.lang] && this.i18[this.options.lang].messages ? this.options.lang : 'en'; i18n = this.lang == 'en' ? this.i18['en'] : $.extend(true, {}, this.i18['en'], this.i18[this.lang]); /** * Interface direction * * @type String * @default "ltr" **/ this.direction = i18n.direction; /** * i18 messages * * @type Object **/ this.messages = i18n.messages; /** * Date/time format * * @type String * @default "m.d.Y" **/ this.dateFormat = this.options.dateFormat || i18n.dateFormat; /** * Date format like "Yesterday 10:20:12" * * @type String * @default "{day} {time}" **/ this.fancyFormat = this.options.fancyDateFormat || i18n.fancyDateFormat; /** * 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'; /** * Css classes * * @type String **/ this.cssClass = 'ui-helper-reset ui-helper-clearfix ui-widget ui-widget-content ui-corner-all elfinder elfinder-'+(this.direction == 'rtl' ? 'rtl' : 'ltr')+' '+this.options.cssClass; /** * Method to store/fetch data * * @type Function **/ this.storage = (function() { try { return 'localStorage' in window && window['localStorage'] !== null ? self.localStorage : self.cookie; } catch (e) { return self.cookie; } })(); 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.sortRules = $.extend(true, {}, this._sortRules, this.options.sortsRules); $.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; /** * Base draggable options * * @type Object **/ this.draggable = { appendTo : 'body', addClasses : true, delay : 30, distance : 8, revert : true, refreshPositions : true, cursor : 'move', cursorAt : {left : 50, top : 47}, start : function(e, ui) { var targets = $.map(ui.helper.data('files')||[], function(h) { return h || null ;}), cnt, h; cnt = targets.length; while (cnt--) { h = targets[cnt]; if (files[h].locked) { ui.helper.addClass('elfinder-drag-helper-plus').data('locked', true); break; } } }, stop : function() { self.trigger('focus').trigger('dragstop'); }, helper : function(e, ui) { var element = this.id ? $(this) : $(this).parents('[id]:first'), helper = $('<div class="elfinder-drag-helper"><span class="elfinder-drag-helper-icon-plus"/></div>'), icon = function(mime) { return '<div class="elfinder-cwd-icon '+self.mime2class(mime)+' ui-corner-all"/>'; }, hashes, l; self.trigger('dragstart', {target : element[0], originalEvent : e}); hashes = element.is('.'+self.res('class', 'cwdfile')) ? self.selected() : [self.navId2Hash(element.attr('id'))]; helper.append(icon(files[hashes[0]].mime)).data('files', hashes).data('locked', false); if ((l = hashes.length) > 1) { helper.append(icon(files[hashes[l-1]].mime) + '<span class="elfinder-drag-num">'+l+'</span>'); } $(document).bind(keydown + ' keyup.' + namespace, function(e){ if (helper.is(':visible') && ! helper.data('locked')) { helper.toggleClass('elfinder-drag-helper-plus', e.shiftKey||e.ctrlKey||e.metaKey); } }); return helper; } }; /** * Base droppable options * * @type Object **/ this.droppable = { // greedy : true, tolerance : 'pointer', accept : '.elfinder-cwd-file-wrapper,.elfinder-navbar-dir,.elfinder-cwd-file', hoverClass : this.res('class', 'adroppable'), drop : function(e, ui) { var dst = $(this), targets = $.map(ui.helper.data('files')||[], function(h) { return h || null }), result = [], c = 'class', cnt, hash, i, h; if (dst.is('.'+self.res(c, 'cwd'))) { hash = cwd; } else if (dst.is('.'+self.res(c, 'cwdfile'))) { hash = dst.attr('id'); } else if (dst.is('.'+self.res(c, 'navdir'))) { hash = self.navId2Hash(dst.attr('id')); } cnt = targets.length; while (cnt--) { h = targets[cnt]; // ignore drop into itself or in own location h != hash && files[h].phash != hash && result.push(h); } if (result.length) { ui.helper.hide(); self.clipboard(result, !(e.ctrlKey||e.shiftKey||e.metaKey||ui.helper.data('locked'))); self.exec('paste', hash); self.trigger('drop', {files : targets}); } } }; /** * Return true if filemanager is active * * @return Boolean **/ this.enabled = function() { return node.is(':visible') && enabled; } /** * Return true if filemanager is visible * * @return Boolean **/ this.visible = function() { return node.is(':visible'); } /** * Return root dir hash for current working directory * * @return String */ this.root = function(hash) { var dir = files[hash || cwd], i; while (dir && dir.phash) { dir = files[dir.phash] } if (dir) { return dir.hash; } while (i in files && files.hasOwnProperty(i)) { dir = files[i] if (!dir.phash && !dir.mime == 'directory' && 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 * @return mixed */ this.option = function(name) { return cwdOptions[name]||''; } /** * Return file data from current dir or tree by it's hash * * @param String file hash * @return Object */ this.file = function(hash) { return files[hash]; }; /** * Return all cached files * * @return Array */ this.files = function() { return $.extend(true, {}, files); } /** * Return list of file parents hashes include file hash * * @param String file hash * @return Array */ this.parents = function(hash) { var parents = [], dir; while ((dir = this.file(hash))) { parents.unshift(dir.hash); hash = dir.phash; } return parents; } this.path2array = function(hash, i18) { var file, path = []; while (hash && (file = files[hash]) && file.hash) { path.unshift(i18 && file.i18 ? file.i18 : file.name); hash = file.phash; } return path; } /** * Return file path * * @param Object file * @return String */ this.path = function(hash, i18) { return files[hash] && files[hash].path ? files[hash].path : this.path2array(hash, i18).join(cwdOptions.separator); } /** * Return file url if set * * @param Object file * @return String */ this.url = function(hash) { var file = files[hash]; if (!file || !file.read) { return ''; } if (file.url) { return file.url; } if (cwdOptions.url) { return cwdOptions.url + $.map(this.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/') } var params = $.extend({}, this.customData, { cmd: 'file', target: file.hash }); if (this.oldAPI) { params.cmd = 'open'; params.current = file.phash; } return this.options.url + (this.options.url.indexOf('?') === -1 ? '?' : '&') + $.param(params, true); } /** * Return thumbnail url * * @param String file hash * @return String */ this.tmb = function(hash) { var file = files[hash], url = file && file.tmb && file.tmb != 1 ? cwdOptions['tmbUrl'] + file.tmb : ''; if (url && (this.UA.Opera || this.UA.IE)) { url += '?_=' + new Date().getTime(); } return url; } /** * 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] ? $.extend({}, 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); } /** * Proccess ajax request. * Fired events : * @todo * @example * @todo * @return $.Deferred */ this.request = function(options) { var self = this, o = this.options, dfrd = $.Deferred(), // request data data = $.extend({}, o.customData, {mimes : o.onlyMimes}, options.data || options), // command name cmd = data.cmd, // call default fail callback (display error dialog) ? deffail = !(options.preventDefault || options.preventFail), // call default success callback ? defdone = !(options.preventDefault || options.preventDone), // options for notify dialog notify = $.extend({}, options.notify), // do not normalize data - return as is raw = !!options.raw, // sync files on request fail syncOnFail = options.syncOnFail, // open notify dialog timeout timeout, // request options options = $.extend({ url : o.url, async : true, type : this.requestType, dataType : 'json', cache : false, // timeout : 100, data : data }, options.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); cmd == 'open' && open($.extend(true, {}, data)); // 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); // fire event with command name self.trigger(cmd, data); // 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; switch (status) { case 'abort': error = xhr.quiet ? '' : ['errConnect', 'errAbort']; break; case 'timeout': error = ['errConnect', 'errTimeout']; break; case 'parsererror': error = ['errResponse', 'errDataNotJSON']; break; default: if (xhr.status == 403) { error = ['errConnect', 'errAccess']; } else if (xhr.status == 404) { error = ['errConnect', 'errNotFound']; } else { error = 'errConnect'; } } dfrd.reject(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) { if (raw) { return dfrd.resolve(response); } if (!response) { return dfrd.reject(['errResponse', 'errDataEmpty'], xhr); } else if (!$.isPlainObject(response)) { return dfrd.reject(['errResponse', 'errDataNotJSON'], xhr); } else if (response.error) { return dfrd.reject(response.error, xhr); } else if (!self.validResponse(cmd, response)) { return dfrd.reject('errResponse', xhr); } response = self.normalize(response); if (!self.api) { self.api = response.api || 1; self.newAPI = self.api >= 2; self.oldAPI = !self.newAPI; } if (response.options) { cwdOptions = $.extend({}, cwdOptions, response.options); } if (response.netDrivers) { self.netDrivers = response.netDrivers; } dfrd.resolve(response); response.debug && self.debug('backend-debug', response.debug); }, xhr, _xhr ; defdone && dfrd.done(done); dfrd.fail(function(error) { if (error) { deffail ? self.error(error) : self.debug('error', self.i18n(error)); } }) if (!cmd) { return dfrd.reject('errCmdReq'); } if (syncOnFail) { dfrd.fail(function(error) { error && self.sync(); }); } if (notify.type && notify.cnt) { timeout = setTimeout(function() { self.notify(notify); dfrd.always(function() { notify.cnt = -(parseInt(notify.cnt)||0); self.notify(notify); }) }, self.notifyDelay) dfrd.always(function() { clearTimeout(timeout); }); } // quiet abort not completed "open" requests if (cmd == 'open') { while ((_xhr = queue.pop())) { if (_xhr.state() == 'pending') { _xhr.quiet = true; _xhr.abort(); } } } delete options.preventFail xhr = this.transport.send(options).fail(error).done(success); // this.transport.send(options) // add "open" xhr into queue if (cmd == 'open') { queue.unshift(xhr); dfrd.always(function() { var ndx = $.inArray(xhr, queue); ndx !== -1 && queue.splice(ndx, 1); }); } return dfrd; }; /** * Compare current files cache with new files and return diff * * @param Array new files * @return Object */ this.diff = function(incoming) { var raw = {}, added = [], removed = [], changed = [], 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; }); // find removed $.each(files, function(hash, f) { !raw[hash] && removed.push(hash); }); // compare files $.each(raw, function(hash, file) { var origin = files[hash]; if (!origin) { added.push(file); } else { $.each(file, function(prop) { if (file[prop] != origin[prop]) { changed.push(file) return false; } }); } }); // parents of removed dirs mark as changed (required for tree correct work) $.each(removed, function(i, hash) { var file = files[hash], phash = file.phash; if (phash && file.mime == 'directory' && $.inArray(phash, removed) === -1 && raw[phash] && !isChanged(phash)) { changed.push(raw[phash]); } }); return { added : added, removed : removed, changed : changed }; } /** * Sync content * * @return jQuery.Deferred */ this.sync = function() { var self = this, dfrd = $.Deferred().done(function() { self.trigger('sync'); }), opts1 = { data : {cmd : 'open', init : 1, target : cwd, tree : this.ui.tree ? 1 : 0}, preventDefault : true }, opts2 = { data : {cmd : 'tree', target : (cwd == this.root())? cwd : this.file(cwd).phash}, preventDefault : true }; $.when( this.request(opts1), this.request(opts2) ) .fail(function(error) { dfrd.reject(error); error && self.request({ data : {cmd : 'open', target : self.lastDir(''), tree : 1, init : 1}, notify : {type : 'open', cnt : 1, hideCnt : true}, preventDefault : true }); }) .done(function(odata, pdata) { var diff = self.diff(odata.files.concat(pdata && pdata.tree ? pdata.tree : [])); diff.added.push(odata.cwd) diff.removed.length && self.remove(diff); diff.added.length && self.add(diff); diff.changed.length && self.change(diff); return dfrd.resolve(diff); }); return dfrd; } this.upload = function(files) { return this.transport.upload(files, this); } /** * 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 * @return elFinder */ this.bind = function(event, callback) { var i; if (typeof(callback) == 'function') { event = ('' + event).toLowerCase().split(/\s+/); for (i = 0; i < event.length; i++) { if (listeners[event[i]] === void(0)) { listeners[event[i]] = []; } listeners[event[i]].push(callback); } } return this; }; /** * Remove event listener if exists * * @param String event name * @param Function callback * @return elFinder */ this.unbind = function(event, callback) { var l = listeners[('' + event).toLowerCase()] || [], i = l.indexOf(callback); i > -1 && l.splice(i, 1); //delete callback; // need this? callback = null return this; }; /** * Fire event - send notification to all event listeners * * @param String event type * @param Object data to send across event * @return elFinder */ this.trigger = function(event, data) { var event = event.toLowerCase(), handlers = listeners[event] || [], i, j; this.debug('event-'+event, data) if (handlers.length) { event = $.Event(event); for (i = 0; i < handlers.length; i++) { // to avoid data modifications. remember about "sharing" passing arguments in js :) event.data = $.extend(true, {}, data); try { if (handlers[i](event, this) === false || event.isDefaultPrevented()) { this.debug('event-stoped', event.type); break; } } catch (ex) { window.console && window.console.log && window.console.log(ex); } } } return this; } /** * Bind keybord shortcut to keydown event * * @example * elfinder.shortcut({ * pattern : 'ctrl+a', * description : 'Select all files', * callback : function(e) { ... }, * keypress : true|false (bind to keypress instead of keydown) * }) * * @param Object shortcut config * @return elFinder */ this.shortcut = function(s) { var patterns, pattern, code, i, parts; if (this.options.allowShortcuts && s.pattern && $.isFunction(s.callback)) { patterns = s.pattern.toUpperCase().split(/\s+/); for (i= 0; i < patterns.length; i++) { pattern = patterns[i] parts = pattern.split('+'); code = (code = parts.pop()).length == 1 ? code > 0 ? code : code.charCodeAt(0) : $.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.push(hash); 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 * @return Boolean */ this.isCommandEnabled = function(name) { return this._commands[name] ? $.inArray(name, cwdOptions.disabled) === -1 : false; } /** * Exec command and return result; * * @param String command name * @param String|Array usualy files hashes * @param String|Array command options * @return $.Deferred */ this.exec = function(cmd, files, opts) { return this._commands[cmd] && this.isCommandEnabled(cmd) ? this._commands[cmd].exec(files, opts) : $.Deferred().reject('No such command'); } /** * Create and return dialog. * * @param String|DOMElement dialog content * @param Object dialog options * @return jQuery */ this.dialog = function(content, options) { return $('<div/>').append(content).appendTo(node).elfinderdialog(options); } /** * Return UI widget or node * * @param String ui name * @return jQuery */ this.getUI = function(ui) { return this.ui[ui] || node; } this.command = function(name) { return name === void(0) ? this._commands : this._commands[name]; } /** * Resize elfinder node * * @param String|Number width * @param Number height * @return void */ this.resize = function(w, h) { node.css('width', w).height(h).trigger('resize'); this.trigger('resize', {width : node.width(), height : node.height()}); } /** * 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() { this.disable().trigger('hide'); node.hide(); } /** * Destroy this elFinder instance * * @return void **/ this.destroy = function() { if (node && node[0].elfinder) { this.trigger('destroy').disable(); listeners = {}; shortcuts = {}; $(document).add(node).unbind('.'+this.namespace); self.trigger = function() { } node.children().remove(); node.append(prevContent.contents()).removeClass(this.cssClass).attr('style', prevStyle); node[0].elfinder = null; if (syncInterval) { clearInterval(syncInterval); } } } /************* init stuffs ****************/ // check jquery ui if (!($.fn.selectable && $.fn.draggable && $.fn.droppable)) { return alert(this.i18n('errJqui')); } // check node if (!node.length) { return alert(this.i18n('errNode')); } // check connector url if (!this.options.url) { return alert(this.i18n('errURL')); } $.extend($.ui.keyCode, { 'F1' : 112, 'F2' : 113, 'F3' : 114, 'F4' : 115, 'F5' : 116, 'F6' : 117, 'F7' : 118, 'F8' : 119, 'F9' : 120 }); 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) { 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); } /** * Alias for this.trigger('error', {error : 'message'}) * * @param String error message * @return elFinder **/ this.error = function() { var arg = arguments[0]; return arguments.length == 1 && typeof(arg) == 'function' ? self.bind('error', arg) : self.trigger('error', {error : arg}); } // create bind/trigger aliases for build-in events $.each(['enable', 'disable', 'load', 'open', 'reload', 'select', 'add', 'remove', 'change', 'dblclick', 'getfile', 'lockfiles', 'unlockfiles', 'dragstart', 'dragstop', 'search', 'searchend', 'viewchange'], 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.visible() && self.ui.overlay.is(':hidden')) { enabled = true; $('textarea:focus,input:focus,button').blur(); node.removeClass('elfinder-disabled'); } }) .disable(function() { prevEnabled = enabled; enabled = false; node.addClass('elfinder-disabled'); }) .open(function() { selected = []; }) .select(function(e) { selected = $.map(e.data.selected || e.data.value|| [], function(hash) { return files[hash] ? hash : null; }); }) .error(function(e) { var opts = { cssClass : 'elfinder-dialog-error', title : self.i18n(self.i18n('error')), resizable : false, destroyOnClose : true, buttons : {} }; opts.buttons[self.i18n(self.i18n('btnClose'))] = function() { $(this).elfinderdialog('close'); }; self.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-error"/>'+self.i18n(e.data.error), opts); }) .bind('tree parents', function(e) { cache(e.data.tree || []); }) .bind('tmb', function(e) { $.each(e.data.images||[], function(hash, tmb) { if (files[hash]) { files[hash].tmb = tmb; } }) }) .add(function(e) { cache(e.data.added||[]); }) .change(function(e) { $.each(e.data.changed||[], function(i, file) { var hash = file.hash; if ((files[hash].width && !file.width) || (files[hash].height && !file.height)) { files[hash].width = undefined; files[hash].height = undefined; } files[hash] = files[hash] ? $.extend(files[hash], file) : file; }); }) .remove(function(e) { var removed = e.data.removed||[], l = removed.length, rm = function(hash) { var file = files[hash]; if (file) { if (file.mime == 'directory' && file.dirs) { $.each(files, function(h, f) { f.phash == hash && rm(h); }); } delete files[hash]; } }; while (l--) { rm(removed[l]); } }) .bind('search', function(e) { cache(e.data.files); }) .bind('rm', function(e) { var play = beeper.canPlayType && beeper.canPlayType('audio/wav; codecs="1"'); play && play != '' && play != 'no' && $(beeper).html('<source src="./sounds/rm.wav" type="audio/wav">')[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); // in getFileCallback set - change default actions on double click/enter/ctrl+enter if (typeof(this.options.getFileCallback) == 'function' && this.commands.getfile) { this.bind('dblclick', function(e) { e.preventDefault(); self.exec('getfile').fail(function() { self.exec('open'); }); }); this.shortcut({ pattern : 'enter', description : this.i18n('cmdgetfile'), callback : function() { self.exec('getfile').fail(function() { self.exec(self.OS == 'mac' ? 'rename' : 'open') }) } }) .shortcut({ pattern : 'ctrl+enter', description : this.i18n(this.OS == 'mac' ? 'cmdrename' : 'cmdopen'), callback : function() { self.exec(self.OS == 'mac' ? 'rename' : 'open') } }); } /** * Loaded commands * * @type Object **/ this._commands = {}; if (!$.isArray(this.options.commands)) { this.options.commands = []; } // check required commands $.each(['open', 'reload', 'back', 'forward', 'up', 'home', 'info', 'quicklook', 'getfile', 'help'], function(i, cmd) { $.inArray(cmd, self.options.commands) === -1 && self.options.commands.push(cmd); }); // load commands $.each(this.options.commands, function(i, name) { var cmd = self.commands[name]; if ($.isFunction(cmd) && !self._commands[name]) { cmd.prototype = base; self._commands[name] = new cmd(); self._commands[name].setup(name, self.options.commandsOptions[name]||{}); } }); // prepare node node.addClass(this.cssClass) .bind(mousedown, function() { !enabled && self.enable(); }); /** * UI nodes * * @type Object **/ this.ui = { // container for nav panel and current folder container workzone : $('<div/>').appendTo(node).elfinderworkzone(this), // container for folders tree / places navbar : $('<div/>').appendTo(node).elfindernavbar(this, this.options.uiOptions.navbar || {}), // contextmenu contextmenu : $('<div/>').appendTo(node).elfindercontextmenu(this), // overlay overlay : $('<div/>').appendTo(node).elfinderoverlay({ show : function() { self.disable(); }, hide : function() { prevEnabled && self.enable(); } }), // current folder container cwd : $('<div/>').appendTo(node).elfindercwd(this, this.options.uiOptions.cwd || {}), // notification dialog window notify : this.dialog('', { cssClass : 'elfinder-dialog-notify', position : {top : '12px', right : '12px'}, resizable : false, autoOpen : false, title : ' ', width : 280 }), statusbar : $('<div class="ui-widget-header ui-helper-clearfix ui-corner-bottom elfinder-statusbar"/>').hide().appendTo(node) } // load required ui $.each(this.options.ui || [], function(i, ui) { var name = 'elfinder'+ui, opts = self.options.uiOptions[ui] || {}; if (!self.ui[ui] && $.fn[name]) { self.ui[ui] = $('<'+(opts.tag || 'div')+'/>').appendTo(node)[name](self, opts); } }); // store instance in node node[0].elfinder = this; // make node resizable this.options.resizable && $.fn.resizable && node.resizable({ handles : 'se', minWidth : 300, minHeight : 200 }); if (this.options.width) { width = this.options.width; } if (this.options.height) { height = parseInt(this.options.height); } // update size self.resize(width, height); // attach events to document $(document) // disable elfinder on click outside elfinder .bind('click.'+this.namespace, function(e) { enabled && !$(e.target).closest(node).length && self.disable(); }) // exec shortcuts .bind(keydown+' '+keypress, execShortcut); // attach events to window self.options.useBrowserHistory && $(window) .on('popstate', function(ev) { var target = ev.originalEvent.state && ev.originalEvent.state.thash; target && !$.isEmptyObject(self.files()) && self.request({ data : {cmd : 'open', target : target, onhistory : 1}, notify : {type : 'open', cnt : 1, hideCnt : true}, syncOnFail : true }); }); // send initial request and start to pray >_< this.trigger('init') .request({ data : {cmd : 'open', target : self.startDir(), init : 1, tree : this.ui.tree ? 1 : 0}, preventDone : true, notify : {type : 'open', cnt : 1, hideCnt : true}, freeze : true }) .fail(function() { self.trigger('fail').disable().lastDir(''); listeners = {}; shortcuts = {}; $(document).add(node).unbind('.'+this.namespace); self.trigger = function() { }; }) .done(function(data) { self.load().debug('api', self.api); data = $.extend(true, {}, data); open(data); self.trigger('open', data); }); // update ui's size after init this.one('load', function() { node.trigger('resize'); if (self.options.sync > 1000) { syncInterval = setInterval(function() { self.sync(); }, self.options.sync) } }); // self.timeEnd('load'); } /** * Prototype * * @type Object */ elFinder.prototype = { res : function(type, id) { return this.resources[type] && this.resources[type][id]; }, /** * Internationalization object * * @type Object */ i18 : { en : { translator : '', language : 'English', direction : 'ltr', dateFormat : 'd.m.Y H:i', fancyDateFormat : '$1 H:i', 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', 'symlink' : 'Alias', 'symlink-broken' : 'AliasBroken', 'application/x-empty' : 'TextPlain', 'application/postscript' : 'Postscript', 'application/vnd.ms-office' : 'MsOffice', '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.openxmlformats-officedocument.spreadsheetml.sheet' : 'MsWord', 'application/vnd.ms-excel' : '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/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', 'image/x-ms-bmp' : 'BMP', 'image/jpeg' : 'JPEG', 'image/gif' : 'GIF', 'image/png' : 'PNG', 'image/tiff' : 'TIFF', 'image/x-targa' : 'TGA', 'image/vnd.adobe.photoshop' : 'PSD', 'image/xbm' : 'XBITMAP', 'image/pxm' : 'PXM', 'audio/mpeg' : 'AudioMPEG', 'audio/midi' : 'AudioMIDI', 'audio/ogg' : 'AudioOGG', 'audio/mp4' : 'AudioMPEG4', 'audio/x-m4a' : 'AudioMPEG4', 'audio/wav' : 'AudioWAV', 'audio/x-mp3-playlist' : 'AudioPlaylist', 'video/x-dv' : 'VideoDV', 'video/mp4' : 'VideoMPEG4', 'video/mpeg' : 'VideoMPEG', 'video/x-msvideo' : 'VideoAVI', 'video/quicktime' : 'VideoMOV', 'video/x-ms-wmv' : 'VideoWM', 'video/x-flv' : 'VideoFlash', 'video/x-matroska' : 'VideoMKV', 'video/ogg' : 'VideoOGG' }, /** * Ajax request data validation rules * * @type Object */ rules : { defaults : function(data) { if (!data || (data.added && !$.isArray(data.added)) || (data.removed && !$.isArray(data.removed)) || (data.changed && !$.isArray(data.changed))) { return false; } return true; }, open : function(data) { return data && data.cwd && data.files && $.isPlainObject(data.cwd) && $.isArray(data.files); }, tree : function(data) { return data && data.tree && $.isArray(data.tree); }, parents : function(data) { return data && data.tree && $.isArray(data.tree); }, tmb : function(data) { return data && data.images && ($.isPlainObject(data.images) || $.isArray(data.images)); }, upload : function(data) { return data && ($.isPlainObject(data.added) || $.isArray(data.added));}, search : function(data) { return data && data.files && $.isArray(data.files)} }, /** * Commands costructors * * @type Object */ commands : {}, parseUploadData : function(text) { var data; if (!$.trim(text)) { return {error : ['errResponse', 'errDataEmpty']}; } try { data = $.parseJSON(text); } catch (e) { return {error : ['errResponse', 'errDataNotJSON']} } if (!this.validResponse('upload', data)) { return {error : ['errResponse']}; } data = this.normalize(data); data.removed = $.map(data.added||[], function(f) { return f.hash; }) return data; }, iframeCnt : 0, uploads : { // check droped contents checkFile : function(data) { if (data.type == 'files') { return data.files; } else { var ret = []; var regex; var str = data.files[0]; if (data.type == 'html') { regex = /<img[^>]+src=["']?([^"'> ]+)/ig; var m = []; var url = ''; var links; while (m = regex.exec(str)) { url = m[1].replace(/&/g, '&'); if (url.match(/^http/) && $.inArray(url, ret) == -1) ret.push(url); } links = str.match(/<\/a>/i); if (links && links.length == 1) { regex = /<a[^>]+href=["']?([^"'> ]+)((?:.|\s)+)<\/a>/i; if (m = regex.exec(str)) { if (! m[2].match(/<img/i)) { url = m[1].replace(/&/g, '&'); if (url.match(/^http/) && $.inArray(url, ret) == -1) ret.push(url); } } } } else { regex = /(http[^<>"{}|\\^\[\]`\s]+)/ig; while (m = regex.exec(str)) { url = m[1].replace(/&/g, '&'); if ($.inArray(url, ret) == -1) ret.push(url); } } return ret; } }, // 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) : false, dfrd = $.Deferred() .fail(function(error) { error && self.error(error); }) .done(function(data) { data.warning && self.error(data.warning); data.removed && self.remove(data); data.added && self.add(data); data.changed && self.change(data); self.trigger('upload', data); data.sync && self.sync(); }), name = 'iframe-'+self.namespace+(++self.iframeCnt), form = $('<form action="'+self.uploadURL+'" method="post" enctype="multipart/form-data" encoding="multipart/form-data" target="'+name+'" style="display:none"><input type="hidden" name="cmd" value="upload" /></form>'), msie = this.UA.IE, // clear timeouts, close notification dialog, remove form/iframe onload = function() { abortto && clearTimeout(abortto); notifyto && clearTimeout(notifyto); notify && self.notify({type : 'upload', cnt : -cnt}); setTimeout(function() { msie && $('<iframe src="javascript:false;"/>').appendTo(form); form.remove(); iframe.remove(); }, 100); }, iframe = $('<iframe src="'+(msie ? 'javascript:false;' : 'about:blank')+'" name="'+name+'" style="position:absolute;left:-1000px;top:-1000px" />') .bind('load', function() { iframe.unbind('load') .bind('load', function() { var data = self.parseUploadData(iframe.contents().text()); onload(); data.error ? dfrd.reject(data.error) : dfrd.resolve(data); }); // 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([errors.connect, errors.timeout]); }, self.options.iframeTimeout); } form.submit(); }), cnt, notify, notifyto, abortto ; if (files && files.length) { $.each(files, function(i, val) { form.append('<input type="hidden" name="upload[]" value="'+val+'"/>'); }); cnt = 1; } else if (input && $(input).is(':file') && $(input).val()) { form.append(input); cnt = input.files ? input.files.length : 1; } else { return dfrd.reject(); } form.append('<input type="hidden" name="'+(self.newAPI ? 'target' : 'current')+'" value="'+self.cwd().hash+'"/>') .append('<input type="hidden" name="html" value="1"/>') .append($(input).attr('name', 'upload[]')); $.each(self.options.onlyMimes||[], function(i, mime) { form.append('<input type="hidden" name="mimes[]" value="'+mime+'"/>'); }); $.each(self.options.customData, function(key, val) { form.append('<input type="hidden" name="'+key+'" value="'+val+'"/>'); }); form.appendTo('body'); iframe.appendTo('body'); return dfrd; }, // upload transport using XMLHttpRequest xhr : function(data, fm) { var self = fm ? fm : this, dfrd = $.Deferred() .fail(function(error) { error && self.error(error); }) .done(function(data) { data.warning && self.error(data.warning); data.removed && self.remove(data); data.added && self.add(data); data.changed && self.change(data); self.trigger('upload', data); data.sync && self.sync(); }) .always(function() { notifyto && clearTimeout(notifyto); notify && self.notify({type : 'upload', cnt : -cnt, progress : 100*cnt}); }), xhr = new XMLHttpRequest(), formData = new FormData(), files = data.input ? data.input.files : self.uploads.checkFile(data), cnt = files.length, loaded = 5, notify = false, startNotify = function() { return setTimeout(function() { notify = true; self.notify({type : 'upload', cnt : cnt, progress : loaded*cnt}); }, self.options.notifyDelay); }, notifyto; if (!cnt) { return dfrd.reject(); } xhr.addEventListener('error', function() { dfrd.reject('errConnect'); }, false); xhr.addEventListener('abort', function() { dfrd.reject(['errConnect', 'errAbort']); }, false); xhr.addEventListener('load', function() { var status = xhr.status, data; if (status > 500) { return dfrd.reject('errResponse'); } if (status != 200) { return dfrd.reject('errConnect'); } if (xhr.readyState != 4) { return dfrd.reject(['errConnect', 'errTimeout']); // am i right? } if (!xhr.responseText) { return dfrd.reject(['errResponse', 'errDataEmpty']); } data = self.parseUploadData(xhr.responseText); data.error ? dfrd.reject(data.error) : dfrd.resolve(data); }, false); xhr.upload.addEventListener('progress', function(e) { var prev = loaded, curr; if (e.lengthComputable) { curr = parseInt(e.loaded*100 / e.total); // 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 (curr > 0 && !notifyto) { notifyto = startNotify(); } if (curr - prev > 4) { loaded = curr; notify && self.notify({type : 'upload', cnt : 0, progress : (loaded - prev)*cnt}); } } }, false); xhr.open('POST', self.uploadURL, true); formData.append('cmd', 'upload'); formData.append(self.newAPI ? 'target' : 'current', self.cwd().hash); $.each(self.options.customData, function(key, val) { formData.append(key, val); }); $.each(self.options.onlyMimes, function(i, mime) { formData.append('mimes['+i+']', mime); }); $.each(files, function(i, file) { formData.append('upload[]', file); }); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 0) { // ff bug while send zero sized file // for safari - send directory dfrd.reject(['errConnect', 'errAbort']); } } xhr.send(formData); if (!this.UA.Safari || !data.files) { notifyto = startNotify(); } 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 * @return elFinder */ one : function(event, callback) { var self = this, h = $.proxy(callback, function(event) { setTimeout(function() {self.unbind(event.type, h);}, 3); return callback.apply(this, arguments); }); return this.bind(event, h); }, /** * Set/get data into/from localStorage * * @param String key * @param String|void value * @return String */ localStorage : function(key, val) { var s = window.localStorage; key = 'elfinder-'+key+this.id; if (val === null) { console.log('remove', key) return s.removeItem(key); } if (val !== void(0)) { try { s.setItem(key, val); } catch (e) { s.clear(); s.setItem(key, val); } } return s.getItem(key); }, /** * Get/set cookie * * @param String cookie name * @param String|void cookie value * @return String */ cookie : function(name, value) { var d, o, c, i; name = 'elfinder-'+name+this.id; if (value === void(0)) { if (document.cookie && document.cookie != '') { c = document.cookie.split(';'); name += '='; for (i=0; i<c.length; i++) { c[i] = $.trim(c[i]); if (c[i].substring(0, name.length) == name) { return decodeURIComponent(c[i].substring(name.length)); } } } return ''; } o = $.extend({}, this.options.cookie); if (value === null) { value = ''; o.expires = -1; } if (typeof(o.expires) == 'number') { d = new Date(); d.setTime(d.getTime()+(o.expires * 86400000)); o.expires = d; } document.cookie = name+'='+encodeURIComponent(value)+'; expires='+o.expires.toUTCString()+(o.path ? '; path='+o.path : '')+(o.domain ? '; domain='+o.domain : '')+(o.secure ? '; secure' : ''); return value; }, /** * Get start directory (by location.hash or last opened directory) * * @return String */ startDir : function() { var locHash = window.location.hash; if (locHash && locHash.match(/^#elf_/)) { return locHash.replace(/^#elf_/, ''); } else { return this.lastDir(); } }, /** * Get/set last opened directory * * @param String|undefined dir hash * @return String */ lastDir : function(hash) { return this.options.rememberLastDir ? this.storage('lastdir', hash) : ''; }, /** * Node for escape html entities in texts * * @type jQuery */ _node : $('<span/>'), /** * Replace not html-safe symbols to html entities * * @param String text to escape * @return String */ escape : function(name) { return this._node.text(name).html(); }, /** * 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 filter = function(file) { if (file && file.hash && file.name && file.mime) { if (file.mime == 'application/x-empty') { file.mime = 'text/plain'; } return file; } return null; return file && file.hash && file.name && file.mime ? file : null; }; if (data.files) { data.files = $.map(data.files, filter); } if (data.tree) { data.tree = $.map(data.tree, filter); } if (data.added) { data.added = $.map(data.added, filter); } if (data.changed) { data.changed = $.map(data.changed, filter); } if (data.api) { data.init = true; } return data; }, /** * Update sort options * * @param {String} sort type * @param {String} sort order * @param {Boolean} show folder first */ setSort : function(type, order, stickFolders) { 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.trigger('sortchange'); }, _sortRules : { name : function(file1, file2) { var self = elFinder.prototype._sortRules.name; 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 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; } } var n1 = file1.name.toLowerCase(), n2 = file2.name.toLowerCase(), e1 = '', e2 = '', m, ret; if (m = n1.match(/^(.+)(\.[0-9a-z.]+)$/)) { n1 = m[1]; e1 = m[2]; } if (m = n2.match(/^(.+)(\.[0-9a-z.]+)$/)) { n2 = m[1]; e2 = m[2]; } ret = self.sort(n1, n2); if (ret == 0 && (e1 || e2) && e1 != e2) { ret = self.sort(e1, e2); } return ret; }, 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 file1.mime.localeCompare(file2.mime); }, date : function(file1, file2) { var date1 = file1.ts || file1.date, date2 = file2.ts || file2.date; return date1 == date2 ? 0 : date1 > date2 ? 1 : -1 } }, /** * 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) * }) * @return elFinder */ notify : function(opts) { var type = opts.type, msg = this.messages['ntf'+type] ? this.i18n('ntf'+type) : this.i18n('ntfsmth'), ndialog = this.ui.notify, notify = ndialog.children('.elfinder-notify-'+type), ntpl = '<div class="elfinder-notify elfinder-notify-{type}"><span class="elfinder-dialog-icon elfinder-dialog-icon-{type}"/><span class="elfinder-notify-msg">{msg}</span> <span class="elfinder-notify-cnt"/><div class="elfinder-notify-progressbar"><div class="elfinder-notify-progress"/></div></div>', delta = opts.cnt, progress = opts.progress >= 0 && opts.progress <= 100 ? opts.progress : 0, cnt, total, prc; if (!type) { return this; } if (!notify.length) { notify = $(ntpl.replace(/\{type\}/g, type).replace(/\{msg\}/g, msg)) .appendTo(ndialog) .data('cnt', 0); if (progress) { notify.data({progress : 0, total : 0}); } } cnt = delta + parseInt(notify.data('cnt')); if (cnt > 0) { !opts.hideCnt && notify.children('.elfinder-notify-cnt').text('('+cnt+')'); ndialog.is(':hidden') && ndialog.elfinderdialog('open'); notify.data('cnt', cnt); if (progress < 100 && (total = notify.data('total')) >= 0 && (prc = notify.data('progress')) >= 0) { total = delta + parseInt(notify.data('total')); prc = progress + prc; progress = parseInt(prc/total); notify.data({progress : prc, total : total}); ndialog.find('.elfinder-notify-progress') .animate({ width : (progress < 100 ? progress : 100)+'%' }, 20); } } else { notify.remove(); !ndialog.children().length && ndialog.elfinderdialog('close'); } return this; }, /** * Open confirmation dialog * * @param Object options * @example * this.confirm({ * 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')} * }, * all : true // display checkbox "Apply to all" * }) * @return elFinder */ confirm : function(opts) { var 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; 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.accept.label)] = function() { opts.accept.callback(!!(checkbox && checkbox.prop('checked'))) complete = true; $(this).elfinderdialog('close') }; options.buttons[this.i18n(opts.cancel.label)] = function() { $(this).elfinderdialog('close') }; if (opts.all) { if (opts.reject) { options.width = 370; } options.create = function() { checkbox = $('<input type="checkbox" />'); $(this).next().children().before($('<label>'+apply+'</label>').prepend(checkbox)); } options.open = function() { var pane = $(this).next(), width = parseInt(pane.children(':first').outerWidth() + pane.children(':last').outerWidth()); if (width > parseInt(pane.width())) { $(this).closest('.elfinder-dialog').width(width+30); } } } return this.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-confirm"/>' + this.i18n(opts.text), options); }, /** * Create unique file name in required dir * * @param String file name * @param String parent dir hash * @return String */ uniqueName : function(prefix, phash) { var i = 0, ext = '', p, name; prefix = this.i18n(prefix); phash = phash || this.cwd().hash; if ((p = prefix.indexOf('.txt')) != -1) { ext = '.txt'; prefix = prefix.substr(0, p); } name = prefix+ext; if (!this.fileByName(name, phash)) { return name; } while (i < 10000) { name = prefix + ' ' + (++i) + ext; if (!this.fileByName(name, phash)) { return name; } } return prefix + Math.random() + ext; }, /** * Return message translated onto current language * * @param String|Array message[s] * @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; for (i = 0; i< arguments.length; i++) { m = arguments[i]; if (typeof m == 'string') { input.push(message(m)); } else if ($.isArray(m)) { for (j = 0; j < m.length; j++) { if (typeof m[j] == 'string') { input.push(message(m[j])); } } } } for (i = 0; i < input.length; i++) { // dont translate placeholders if ($.inArray(i, ignore) !== -1) { continue; } m = input[i]; // translate message m = messages[m] || m; // replace placeholders in message m = m.replace(/\$(\d+)/g, function(match, placeholder) { placeholder = i + parseInt(placeholder); if (placeholder > 0 && input[placeholder]) { ignore.push(placeholder) } return input[placeholder] || ''; }); input[i] = m; } return $.map(input, function(m, i) { return $.inArray(i, ignore) === -1 ? m : null; }).join('<br>'); }, /** * Convert mimetype into css classes * * @param String file mimetype * @return String */ mime2class : function(mime) { var prefix = 'elfinder-cwd-icon-'; mime = mime.split('/'); return prefix+mime[0]+(mime[0] != 'image' && 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 mime = typeof(f) == 'object' ? f.mime : f, kind; if (f.alias) { kind = 'Alias'; } else if (this.kinds[mime]) { kind = this.kinds[mime]; } else { if (mime.indexOf('text') === 0) { kind = 'Text'; } else if (mime.indexOf('image') === 0) { kind = 'Image'; } else if (mime.indexOf('audio') === 0) { kind = 'Audio'; } else if (mime.indexOf('video') === 0) { kind = 'Video'; } else if (mime.indexOf('application') === 0) { kind = 'App'; } else { kind = mime; } } return this.messages['kind'+kind] ? this.i18n('kind'+kind) : mime; var mime = typeof(f) == 'object' ? f.mime : f, kind = this.kinds[mime]||'unknown'; if (f.alias) { kind = 'Alias'; } else if (kind == 'unknown') { 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 = 'Application'; } } return this.i18n(kind); }, /** * Return localized date * * @param Object file object * @return String */ formatDate : function(file, ts) { var self = this, ts = ts || 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); 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](); format = ts >= this.yesterday ? this.fancyFormat : this.dateFormat; 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(i18.daysShort[dw]); case 'l': return self.i18n(i18.days[dw]); case 'm': return m > 9 ? m : '0'+m; case 'n': return m; case 'M': return self.i18n(i18.monthsShort[m-1]); case 'F': return self.i18n(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 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 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'; } 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; }, navHash2Id : function(hash) { return 'nav-'+hash; }, navId2Hash : function(id) { return typeof(id) == 'string' ? id.substr(4) : false; }, log : function(m) { window.console && window.console.log && window.console.log(m); return this; }, debug : function(type, m) { var d = this.options.debug; if (d == 'all' || d === true || ($.isArray(d) && $.inArray(type, d) != -1)) { window.console && window.console.log && window.console.log('elfinder debug: ['+type+'] ['+this.id+']', m); } return this; }, time : function(l) { window.console && window.console.time && window.console.time(l); }, timeEnd : function(l) { window.console && window.console.timeEnd && window.console.timeEnd(l); } } /* * File: /js/elFinder.version.js */ /** * Application version * * @type String **/ elFinder.prototype.version = '2.x github.com/jonnybradley/elFinder'; /* * File: /js/jquery.elfinder.js */ $.fn.elfinder = function(o) { if (o == 'instance') { return this.getElFinder(); } return this.each(function() { var cmd = typeof(o) == 'string' ? o : ''; if (!this.elfinder) { new elFinder(this, typeof(o) == 'object' ? o : {}) } switch(cmd) { case 'close': case 'hide': this.elfinder.hide(); break; case 'open': case 'show': this.elfinder.show(); break; case'destroy': this.elfinder.destroy(); break; } }) } $.fn.getElFinder = function() { var instance; this.each(function() { if (this.elfinder) { instance = this.elfinder; return false; } }); return instance; } /* * File: /js/elFinder.options.js */ /** * Default elFinder config * * @type Object * @autor Dmitry (dio) Levashov */ elFinder.prototype._options = { /** * Connector url. Required! * * @type String */ url : '', /** * Ajax request type. * * @type String * @default "get" */ requestType : 'get', /** * Transport to send request to backend. * Required for future extensions using websockets/webdav etc. * Must be an object with "send" method. * transport.send must return $.Deferred() object * * @type Object * @default null * @example * transport : { * init : function(elfinderInstance) { }, * send : function(options) { * var dfrd = $.Deferred(); * // connect to backend ... * return dfrd; * }, * upload : function(data) { * var dfrd = $.Deferred(); * // upload ... * return dfrd; * } * * } **/ transport : {}, /** * URL to upload file to. * If not set - connector URL will be used * * @type String * @default '' */ urlUpload : '', /** * Allow to drag and drop to upload files * * @type Boolean|String * @default 'auto' */ dragUploadAllow : 'auto', /** * Timeout for upload using iframe * * @type Number * @default 0 - no timeout */ iframeTimeout : 0, /** * Data to append to all requests and to upload files * * @type Object * @default {} */ customData : {}, /** * Event listeners to bind on elFinder init * * @type Object * @default {} */ handlers : {}, /** * Interface language * * @type String * @default "en" */ lang : 'en', /** * Additional css class for filemanager node. * * @type String */ cssClass : '', /** * Active commands list * If some required commands will be missed here, elFinder will add its * * @type Array */ commands : [ 'open', 'reload', 'home', 'up', 'back', 'forward', 'getfile', 'quicklook', 'download', 'rm', 'duplicate', 'rename', 'mkdir', 'mkfile', 'upload', 'copy', 'cut', 'paste', 'edit', 'extract', 'archive', 'search', 'info', 'view', 'help', 'resize', 'sort', 'netmount' ], /** * Commands options. * * @type Object **/ commandsOptions : { // "getfile" command options. getfile : { onlyURL : false, // allow to return multiple files info multiple : false, // allow to return filers info folders : false, // action after callback (""/"close"/"destroy") oncomplete : '' }, // "upload" command options. upload : { ui : 'uploadbutton' }, // "quicklook" command options. quicklook : { autoplay : true, jplayer : 'extensions/jplayer' }, // "quicklook" command options. edit : { // list of allowed mimetypes to edit // if empty - any text files can be edited mimes : [], // edit files in wysisyg's editors : [ // { // /** // * files mimetypes allowed to edit in current wysisyg // * @type Array // */ // mimes : ['text/html'], // /** // * Called when "edit" dialog loaded. // * Place to init wysisyg. // * Can return wysisyg instance // * // * @param DOMElement textarea node // * @return Object // */ // load : function(textarea) { }, // /** // * Called before "edit" dialog closed. // * Place to destroy wysisyg instance. // * // * @param DOMElement textarea node // * @param Object wysisyg instance (if was returned by "load" callback) // * @return void // */ // close : function(textarea, instance) { }, // /** // * Called before file content send to backend. // * Place to update textarea content if needed. // * // * @param DOMElement textarea node // * @param Object wysisyg instance (if was returned by "load" callback) // * @return void // */ // save : function(textarea, editor) {} // // } ] }, // "info" command options. info : {nullUrlDirLinkSelf : true}, help : {view : ['about', 'shortcuts', 'help']} }, /** * Callback for "getfile" commands. * Required to use elFinder with WYSIWYG editors etc.. * * @type Function * @default null (command not active) */ getFileCallback : null, /** * Default directory view. icons/list * * @type String * @default "icons" */ defaultView : 'icons', /** * UI plugins to load. * Current dir ui and dialogs loads always. * Here set not required plugins as folders tree/toolbar/statusbar etc. * * @type Array * @default ['toolbar', 'tree', 'path', 'stat'] * @full ['toolbar', 'places', 'tree', 'path', 'stat'] */ ui : ['toolbar', 'tree', 'path', 'stat'], /** * Some UI plugins options. * @type Object */ uiOptions : { // toolbar configuration toolbar : [ ['back', 'forward'], ['netmount'], // ['reload'], // ['home', 'up'], ['mkdir', 'mkfile', 'upload'], ['open', 'download', 'getfile'], ['info'], ['quicklook'], ['copy', 'cut', 'paste'], ['rm'], ['duplicate', 'rename', 'edit', 'resize'], ['extract', 'archive'], ['search'], ['view', 'sort'], ['help'] ], // directories tree options tree : { // expand current root on init openRootOnLoad : true, // auto load current dir parents syncTree : true }, // navbar options navbar : { minWidth : 150, maxWidth : 500 }, cwd : { // display parent folder with ".." name :) oldSchool : false } }, /** * Display only required files by types * * @type Array * @default [] * @example * onlyMimes : ["image"] - display all images * onlyMimes : ["image/png", "application/x-shockwave-flash"] - display png and flash */ onlyMimes : [], /** * Custom files sort rules. * All default rules (name/size/kind/date) set in elFinder._sortRules * * @type {Object} * @example * sortRules : { * name : function(file1, file2) { return file1.name.toLowerCase().localeCompare(file2.name.toLowerCase()); } * } */ sortRules : {}, /** * Default sort type. * * @type {String} */ sortType : 'name', /** * Default sort order. * * @type {String} * @default "asc" */ sortOrder : 'asc', /** * Display folders first? * * @type {Boolean} * @default true */ sortStickFolders : true, /** * If true - elFinder will formating dates itself, * otherwise - backend date will be used. * * @type Boolean */ clientFormatDate : true, /** * Show UTC dates. * Required set clientFormatDate to true * * @type Boolean */ UTCDate : false, /** * File modification datetime format. * Value from selected language data is used by default. * Set format here to overwrite it. * * @type String * @default "" */ dateFormat : '', /** * File modification datetime format in form "Yesterday 12:23:01". * Value from selected language data is used by default. * Set format here to overwrite it. * Use $1 for "Today"/"Yesterday" placeholder * * @type String * @default "" * @example "$1 H:m:i" */ fancyDateFormat : '', /** * elFinder width * * @type String|Number * @default "auto" */ width : 'auto', /** * elFinder height * * @type Number * @default "auto" */ height : 400, /** * Make elFinder resizable if jquery ui resizable available * * @type Boolean * @default true */ resizable : true, /** * Timeout before open notifications dialogs * * @type Number * @default 500 (.5 sec) */ notifyDelay : 500, /** * Allow shortcuts * * @type Boolean * @default true */ allowShortcuts : true, /** * Remeber last opened dir to open it after reload or in next session * * @type Boolean * @default true */ rememberLastDir : true, /** * Use browser native history with supported browsers * * @type Boolean * @default true */ useBrowserHistory : true, /** * Lazy load config. * How many files display at once? * * @type Number * @default 50 */ showFiles : 30, /** * Lazy load config. * Distance in px to cwd bottom edge to start display files * * @type Number * @default 50 */ showThreshold : 50, /** * Additional rule to valid new file name. * By default not allowed empty names or '..' * * @type false|RegExp|function * @default false * @example * disable names with spaces: * validName : /^[^\s]$/ */ validName : false, /** * Sync content interval * @todo - fix in elFinder * @type Number * @default 0 (do not sync) */ sync : 0, /** * How many thumbnails create in one request * * @type Number * @default 5 */ loadTmbs : 5, /** * Cookie option for browsersdoes not suppot localStorage * * @type Object */ cookie : { expires : 30, domain : '', path : '/', secure : false }, /** * Contextmenu config * * @type Object */ contextmenu : { // navbarfolder menu navbar : ['open', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', '|', 'info'], // current directory menu cwd : ['reload', 'back', '|', 'upload', 'mkdir', 'mkfile', 'paste', '|', 'sort', '|', 'info'], // current directory file menu files : ['getfile', '|','open', 'quicklook', '|', 'download', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', '|', 'edit', 'rename', 'resize', '|', 'archive', 'extract', '|', 'info'] }, /** * Debug config * * @type Array|Boolean */ // debug : true debug : ['error', 'warning', 'event-destroy'] } /* * File: /js/elFinder.history.js */ /** * @class elFinder.history * Store visited folders * and provide "back" and "forward" methods * * @author Dmitry (dio) Levashov */ elFinder.prototype.history = function(fm) { var self = this, /** * Update history on "open" event? * * @type Boolean */ update = true, /** * Directories hashes storage * * @type Array */ history = [], /** * Current directory index in history * * @type Number */ current, /** * Clear history * * @return void */ reset = function() { history = [fm.cwd().hash]; current = 0; update = true; }, /** * Browser native history object */ nativeHistory = (fm.options.useBrowserHistory && window.history && window.history.pushState)? window.history : null, /** * Open prev/next folder * * @Boolen open next folder? * @return jQuery.Deferred */ go = function(fwd) { if ((fwd && self.canForward()) || (!fwd && self.canBack())) { update = false; return fm.exec('open', history[fwd ? ++current : --current]).fail(reset); } return $.Deferred().reject(); }; /** * Return true if there is previous visited directories * * @return Boolen */ this.canBack = function() { return current > 0; } /** * Return true if can go forward * * @return Boolen */ this.canForward = function() { return current < history.length - 1; } /** * Go back * * @return void */ this.back = go; /** * Go forward * * @return void */ this.forward = function() { return go(true); } // bind to elfinder events fm.open(function(e) { var l = history.length, cwd = fm.cwd().hash; if (update) { current >= 0 && l > current + 1 && history.splice(current+1); history[history.length-1] != cwd && history.push(cwd); current = history.length - 1; } update = true; if (nativeHistory) { if (! nativeHistory.state) { nativeHistory.replaceState({thash: cwd}, null, location.pathname + location.search + '#elf_' + cwd); } else { nativeHistory.state.thash != cwd && nativeHistory.pushState({thash: cwd}, null, location.pathname + location.search + '#elf_' + cwd); } } }) .reload(reset); } /* * File: /js/elFinder.command.js */ /** * elFinder command prototype * * @type elFinder.command * @author Dmitry (dio) Levashov */ elFinder.prototype.command = function(fm) { /** * elFinder instance * * @type elFinder */ this.fm = fm; /** * Command name, same as class name * * @type String */ this.name = ''; /** * Short command description * * @type String */ this.title = ''; /** * Current command state * * @example * this.state = -1; // command disabled * this.state = 0; // command enabled * this.state = 1; // command active (for example "fullscreen" command while elfinder in fullscreen mode) * @default -1 * @type Number */ this.state = -1; /** * If true, command can not be disabled by connector. * @see this.update() * * @type Boolen */ this.alwaysEnabled = false; /** * If true, this means command was disabled by connector. * @see this.update() * * @type Boolen */ this._disabled = false; this.disableOnSearch = false; this.updateOnSelect = true; /** * elFinder events defaults handlers. * Inside handlers "this" is current command object * * @type Object */ this._handlers = { enable : function() { this.update(void(0), this.value); }, disable : function() { this.update(-1, this.value); }, 'open reload load' : function(e) { this._disabled = !(this.alwaysEnabled || this.fm.isCommandEnabled(this.name)); this.update(void(0), this.value) this.change(); } }; /** * elFinder events handlers. * Inside handlers "this" is current command object * * @type Object */ this.handlers = {} /** * Shortcuts * * @type Array */ this.shortcuts = []; /** * Command options * * @type Object */ this.options = {ui : 'button'}; /** * Prepare object - * bind events and shortcuts * * @return void */ this.setup = function(name, opts) { var self = this, fm = this.fm, i, s; this.name = name; this.title = fm.messages['cmd'+name] ? fm.i18n('cmd'+name) : name, this.options = $.extend({}, this.options, opts); this.listeners = []; if (this.updateOnSelect) { this._handlers.select = function() { this.update(void(0), this.value); } } $.each($.extend({}, self._handlers, self.handlers), function(cmd, handler) { fm.bind(cmd, $.proxy(handler, self)); }); for (i = 0; i < this.shortcuts.length; i++) { s = this.shortcuts[i]; s.callback = $.proxy(s.callback || function() { this.exec() }, this); !s.description && (s.description = this.title); fm.shortcut(s); } if (this.disableOnSearch) { fm.bind('search searchend', function(e) { self._disabled = e.type == 'search'; self.update(void(0), self.value); }); } this.init(); } /** * Command specific init stuffs * * @return void */ this.init = function() { } /** * Exec command * * @param Array target files hashes * @param Array|Object command value * @return $.Deferred */ this.exec = function(files, opts) { return $.Deferred().reject(); } /** * Return true if command disabled. * * @return Boolen */ this.disabled = function() { return this.state < 0; } /** * Return true if command enabled. * * @return Boolen */ this.enabled = function() { return this.state > -1; } /** * Return true if command active. * * @return Boolen */ this.active = function() { return this.state > 0; } /** * Return current command state. * Must be overloaded in most commands * * @return Number */ this.getstate = function() { return -1; } /** * Update command state/value * and rize 'change' event if smth changed * * @param Number new state or undefined to auto update state * @param mixed new value * @return void */ this.update = function(s, v) { var state = this.state, value = this.value; if (this._disabled) { this.state = -1; } else { this.state = s !== void(0) ? s : this.getstate(); } this.value = v; if (state != this.state || value != this.value) { this.change(); } } /** * Bind handler / fire 'change' event. * * @param Function|undefined event callback * @return void */ this.change = function(c) { var cmd, i; if (typeof(c) === 'function') { this.listeners.push(c); } else { for (i = 0; i < this.listeners.length; i++) { cmd = this.listeners[i]; try { cmd(this.state, this.value); } catch (e) { this.fm.debug('error', e) } } } return this; } /** * With argument check given files hashes and return list of existed files hashes. * Without argument return selected files hashes. * * @param Array|String|void hashes * @return Array */ this.hashes = function(hashes) { return hashes ? $.map($.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) ? hash : null; }) : fm.selected(); } /** * Return only existed files from given fils hashes | selected files * * @param Array|String|void hashes * @return Array */ this.files = function(hashes) { var fm = this.fm; return hashes ? $.map($.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) || null }) : fm.selectedFiles(); } } /* * File: /js/elFinder.resources.js */ /** * elFinder resources registry. * Store shared data * * @type Object * @author Dmitry (dio) Levashov **/ elFinder.prototype.resources = { 'class' : { hover : 'ui-state-hover', active : 'ui-state-active', disabled : 'ui-state-disabled', draggable : 'ui-draggable', droppable : 'ui-droppable', adroppable : 'elfinder-droppable-active', cwdfile : 'elfinder-cwd-file', cwd : 'elfinder-cwd', tree : 'elfinder-tree', treeroot : 'elfinder-navbar-root', navdir : 'elfinder-navbar-dir', navdirwrap : 'elfinder-navbar-dir-wrapper', navarrow : 'elfinder-navbar-arrow', navsubtree : 'elfinder-navbar-subtree', navcollapse : 'elfinder-navbar-collapsed', navexpand : 'elfinder-navbar-expanded', treedir : 'elfinder-tree-dir', placedir : 'elfinder-place-dir', searchbtn : 'elfinder-button-search' }, tpl : { perms : '<span class="elfinder-perms"/>', symlink : '<span class="elfinder-symlink"/>', navicon : '<span class="elfinder-nav-icon"/>', navspinner : '<span class="elfinder-navbar-spinner"/>', navdir : '<div class="elfinder-navbar-wrapper"><span id="{id}" class="ui-corner-all elfinder-navbar-dir {cssclass}"><span class="elfinder-navbar-arrow"/><span class="elfinder-navbar-icon"/>{symlink}{permissions}{name}</span><div class="elfinder-navbar-subtree"/></div>' }, mimes : { text : [ 'application/x-empty', 'application/javascript', 'application/xhtml+xml', 'audio/x-mp3-playlist', 'application/x-web-config', 'application/docbook+xml', 'application/x-php', 'application/x-perl', 'application/x-awk', 'application/x-config', 'application/x-csh', 'application/xml' ] }, mixin : { make : function() { var fm = this.fm, cmd = this.name, cwd = fm.getUI('cwd'), dfrd = $.Deferred() .fail(function(error) { cwd.trigger('unselectall'); error && fm.error(error); }) .always(function() { input.remove(); node.remove(); fm.enable(); }), id = 'tmp_'+parseInt(Math.random()*100000), phash = fm.cwd().hash, date = new Date(), file = { hash : id, name : fm.uniqueName(this.prefix), mime : this.mime, read : true, write : true, date : 'Today '+date.getHours()+':'+date.getMinutes() }, node = cwd.trigger('create.'+fm.namespace, file).find('#'+id), input = $('<input type="text"/>') .keydown(function(e) { e.stopImmediatePropagation(); if (e.keyCode == $.ui.keyCode.ESCAPE) { dfrd.reject(); } else if (e.keyCode == $.ui.keyCode.ENTER) { input.blur(); } }) .mousedown(function(e) { e.stopPropagation(); }) .blur(function() { var name = $.trim(input.val()), parent = input.parent(); if (parent.length) { if (!name) { return dfrd.reject('errInvName'); } if (fm.fileByName(name, phash)) { return dfrd.reject(['errExists', name]); } parent.html(fm.escape(name)); fm.lockfiles({files : [id]}); fm.request({ data : {cmd : cmd, name : name, target : phash}, notify : {type : cmd, cnt : 1}, preventFail : true, syncOnFail : true }) .fail(function(error) { dfrd.reject(error); }) .done(function(data) { dfrd.resolve(data); }); } }); if (this.disabled() || !node.length) { return dfrd.reject(); } fm.disable(); node.find('.elfinder-cwd-filename').empty('').append(input.val(file.name)); input.select().focus(); input[0].setSelectionRange && input[0].setSelectionRange(0, file.name.replace(/\..+$/, '').length); return dfrd; } } } /* * File: /js/jquery.dialogelfinder.js */ /** * @class dialogelfinder - open elFinder in dialog window * * @param Object elFinder options with dialog options * @example * $(selector).dialogelfinder({ * // some elfinder options * title : 'My files', // dialog title, default = "Files" * width : 850, // dialog width, default 840 * autoOpen : false, // if false - dialog will not be opened after init, default = true * destroyOnClose : true // destroy elFinder on close dialog, default = false * }) * @author Dmitry (dio) Levashov **/ $.fn.dialogelfinder = function(opts) { var position = 'elfinderPosition', destroy = 'elfinderDestroyOnClose'; this.not('.elfinder').each(function() { var doc = $(document), toolbar = $('<div class="ui-widget-header dialogelfinder-drag ui-corner-top">'+(opts.title || 'Files')+'</div>'), button = $('<a href="#" class="dialogelfinder-drag-close ui-corner-all"><span class="ui-icon ui-icon-closethick"/></a>') .appendTo(toolbar) .click(function(e) { e.preventDefault(); node.dialogelfinder('close'); }), node = $(this).addClass('dialogelfinder') .css('position', 'absolute') .hide() .appendTo('body') .draggable({ handle : '.dialogelfinder-drag', containment : 'window' }) .elfinder(opts) .prepend(toolbar), elfinder = node.elfinder('instance'); node.width(parseInt(node.width()) || 840) // fix width if set to "auto" .data(destroy, !!opts.destroyOnClose) .find('.elfinder-toolbar').removeClass('ui-corner-top'); opts.position && node.data(position, opts.position); opts.autoOpen !== false && $(this).dialogelfinder('open'); }); if (opts == 'open') { var node = $(this), pos = node.data(position) || { top : parseInt($(document).scrollTop() + ($(window).height() < node.height() ? 2 : ($(window).height() - node.height())/2)), left : parseInt($(document).scrollLeft() + ($(window).width() < node.width() ? 2 : ($(window).width() - node.width())/2)) }, zindex = 100; if (node.is(':hidden')) { $('body').find(':visible').each(function() { var $this = $(this), z; if (this !== node[0] && $this.css('position') == 'absolute' && (z = parseInt($this.zIndex())) > zindex) { zindex = z + 1; } }); node.zIndex(zindex).css(pos).show().trigger('resize') setTimeout(function() { // fix resize icon position and make elfinder active node.trigger('resize').mousedown(); }, 200); } } else if (opts == 'close') { var node = $(this); if (node.is(':visible')) { !!node.data(destroy) ? node.elfinder('destroy').remove() : node.elfinder('close'); } } else if (opts == 'instance') { return $(this).getElFinder(); } return this; } /* * File: /js/i18n/elfinder.en.js */ /** * English translation * @author Troex Nevelin <troex@fury.scancode.ru> * @version 2014-12-19 */ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object') { elFinder.prototype.i18.en = { translator : 'Troex Nevelin <troex@fury.scancode.ru>', language : 'English', direction : 'ltr', dateFormat : 'M d, Y h:i A', // Mar 13, 2012 05:27 PM fancyDateFormat : '$1 h:i A', // will produce smth like: Today 12:25 PM messages : { /********************************** errors **********************************/ 'error' : 'Error', 'errUnknown' : 'Unknown error.', 'errUnknownCmd' : 'Unknown command.', 'errJqui' : 'Invalid jQuery UI configuration. Selectable, draggable and droppable components must be included.', 'errNode' : 'elFinder requires DOM Element to be created.', 'errURL' : 'Invalid elFinder configuration! URL option is not set.', 'errAccess' : 'Access denied.', 'errConnect' : 'Unable to connect to backend.', 'errAbort' : 'Connection aborted.', 'errTimeout' : 'Connection timeout.', 'errNotFound' : 'Backend not found.', 'errResponse' : 'Invalid backend response.', 'errConf' : 'Invalid backend configuration.', 'errJSON' : 'PHP JSON module not installed.', 'errNoVolumes' : 'Readable volumes not available.', 'errCmdParams' : 'Invalid parameters for command "$1".', 'errDataNotJSON' : 'Data is not JSON.', 'errDataEmpty' : 'Data is empty.', 'errCmdReq' : 'Backend request requires command name.', 'errOpen' : 'Unable to open "$1".', 'errNotFolder' : 'Object is not a folder.', 'errNotFile' : 'Object is not a file.', 'errRead' : 'Unable to read "$1".', 'errWrite' : 'Unable to write into "$1".', 'errPerm' : 'Permission denied.', 'errLocked' : '"$1" is locked and can not be renamed, moved or removed.', 'errExists' : 'File named "$1" already exists.', 'errInvName' : 'Invalid file name.', 'errFolderNotFound' : 'Folder not found.', 'errFileNotFound' : 'File not found.', 'errTrgFolderNotFound' : 'Target folder "$1" not found.', 'errPopup' : 'Browser prevented opening popup window. To open file enable it in browser options.', 'errMkdir' : 'Unable to create folder "$1".', 'errMkfile' : 'Unable to create file "$1".', 'errRename' : 'Unable to rename "$1".', 'errCopyFrom' : 'Copying files from volume "$1" not allowed.', 'errCopyTo' : 'Copying files to volume "$1" not allowed.', 'errUpload' : 'Upload error.', // old name - errUploadCommon 'errUploadFile' : 'Unable to upload "$1".', // old name - errUpload 'errUploadNoFiles' : 'No files found for upload.', 'errUploadTotalSize' : 'Data exceeds the maximum allowed size.', // old name - errMaxSize 'errUploadFileSize' : 'File exceeds maximum allowed size.', // old name - errFileMaxSize 'errUploadMime' : 'File type not allowed.', 'errUploadTransfer' : '"$1" transfer error.', 'errNotReplace' : 'Object "$1" already exists at this location and can not be replaced by object with another type.', // new 'errReplace' : 'Unable to replace "$1".', 'errSave' : 'Unable to save "$1".', 'errCopy' : 'Unable to copy "$1".', 'errMove' : 'Unable to move "$1".', 'errCopyInItself' : 'Unable to copy "$1" into itself.', 'errRm' : 'Unable to remove "$1".', 'errRmSrc' : 'Unable remove source file(s).', 'errExtract' : 'Unable to extract files from "$1".', 'errArchive' : 'Unable to create archive.', 'errArcType' : 'Unsupported archive type.', 'errNoArchive' : 'File is not archive or has unsupported archive type.', 'errCmdNoSupport' : 'Backend does not support this command.', 'errReplByChild' : 'The folder “$1” can’t be replaced by an item it contains.', 'errArcSymlinks' : 'For security reason denied to unpack archives contains symlinks or files with not allowed names.', // edited 24.06.2012 'errArcMaxSize' : 'Archive files exceeds maximum allowed size.', 'errResize' : 'Unable to resize "$1".', 'errResizeDegree' : 'Invalid rotate degree.', // added 7.3.2013 'errResizeRotate' : 'Image dose not rotated.', // added 7.3.2013 'errResizeSize' : 'Invalid image size.', // added 7.3.2013 'errResizeNoChange' : 'Image size not changed.', // added 7.3.2013 'errUsupportType' : 'Unsupported file type.', 'errNotUTF8Content' : 'File "$1" is not in UTF-8 and cannot be edited.', // added 9.11.2011 'errNetMount' : 'Unable to mount "$1".', // added 17.04.2012 'errNetMountNoDriver' : 'Unsupported protocol.', // added 17.04.2012 'errNetMountFailed' : 'Mount failed.', // added 17.04.2012 'errNetMountHostReq' : 'Host required.', // added 18.04.2012 'errSessionExpires' : 'Your session has expired due to inactivity.', 'errCreatingTempDir' : 'Unable to create temporary directory: "$1"', 'errFtpDownloadFile' : 'Unable to download file from FTP: "$1"', 'errFtpUploadFile' : 'Unable to upload file to FTP: "$1"', 'errFtpMkdir' : 'Unable to create remote directory on FTP: "$1"', 'errArchiveExec' : 'Error while archiving files: "$1"', 'errExtractExec' : 'Error while extracting files: "$1"', /******************************* commands names ********************************/ 'cmdarchive' : 'Create archive', 'cmdback' : 'Back', 'cmdcopy' : 'Copy', 'cmdcut' : 'Cut', 'cmddownload' : 'Download', 'cmdduplicate' : 'Duplicate', 'cmdedit' : 'Edit file', 'cmdextract' : 'Extract files from archive', 'cmdforward' : 'Forward', 'cmdgetfile' : 'Select files', 'cmdhelp' : 'About this software', 'cmdhome' : 'Home', 'cmdinfo' : 'Get info', 'cmdmkdir' : 'New folder', 'cmdmkfile' : 'New text file', 'cmdopen' : 'Open', 'cmdpaste' : 'Paste', 'cmdquicklook' : 'Preview', 'cmdreload' : 'Reload', 'cmdrename' : 'Rename', 'cmdrm' : 'Delete', 'cmdsearch' : 'Find files', 'cmdup' : 'Go to parent directory', 'cmdupload' : 'Upload files', 'cmdview' : 'View', 'cmdresize' : 'Resize & Rotate', 'cmdsort' : 'Sort', 'cmdnetmount' : 'Mount network volume', // added 18.04.2012 /*********************************** buttons ***********************************/ 'btnClose' : 'Close', 'btnSave' : 'Save', 'btnRm' : 'Remove', 'btnApply' : 'Apply', 'btnCancel' : 'Cancel', 'btnNo' : 'No', 'btnYes' : 'Yes', 'btnMount' : 'Mount', // added 18.04.2012 /******************************** notifications ********************************/ 'ntfopen' : 'Open folder', 'ntffile' : 'Open file', 'ntfreload' : 'Reload folder content', 'ntfmkdir' : 'Creating directory', 'ntfmkfile' : 'Creating files', 'ntfrm' : 'Delete files', 'ntfcopy' : 'Copy files', 'ntfmove' : 'Move files', 'ntfprepare' : 'Prepare to copy files', 'ntfrename' : 'Rename files', 'ntfupload' : 'Uploading files', 'ntfdownload' : 'Downloading files', 'ntfsave' : 'Save files', 'ntfarchive' : 'Creating archive', 'ntfextract' : 'Extracting files from archive', 'ntfsearch' : 'Searching files', 'ntfresize' : 'Resizing images', 'ntfsmth' : 'Doing something', 'ntfloadimg' : 'Loading image', 'ntfnetmount' : 'Mounting network volume', // added 18.04.2012 'ntfdim' : 'Acquiring image dimension', // added 20.05.2013 /************************************ dates **********************************/ 'dateUnknown' : 'unknown', 'Today' : 'Today', 'Yesterday' : 'Yesterday', 'msJan' : 'Jan', 'msFeb' : 'Feb', 'msMar' : 'Mar', 'msApr' : 'Apr', 'msMay' : 'May', 'msJun' : 'Jun', 'msJul' : 'Jul', 'msAug' : 'Aug', 'msSep' : 'Sep', 'msOct' : 'Oct', 'msNov' : 'Nov', 'msDec' : 'Dec', 'January' : 'January', 'February' : 'February', 'March' : 'March', 'April' : 'April', 'May' : 'May', 'June' : 'June', 'July' : 'July', 'August' : 'August', 'September' : 'September', 'October' : 'October', 'November' : 'November', 'December' : 'December', 'Sunday' : 'Sunday', 'Monday' : 'Monday', 'Tuesday' : 'Tuesday', 'Wednesday' : 'Wednesday', 'Thursday' : 'Thursday', 'Friday' : 'Friday', 'Saturday' : 'Saturday', 'Sun' : 'Sun', 'Mon' : 'Mon', 'Tue' : 'Tue', 'Wed' : 'Wed', 'Thu' : 'Thu', 'Fri' : 'Fri', 'Sat' : 'Sat', /******************************** sort variants ********************************/ 'sortname' : 'by name', 'sortkind' : 'by kind', 'sortsize' : 'by size', 'sortdate' : 'by date', 'sortFoldersFirst' : 'Folders first', /********************************** messages **********************************/ 'confirmReq' : 'Confirmation required', 'confirmRm' : 'Are you sure you want to remove files?<br/>This cannot be undone!', 'confirmRepl' : 'Replace old file with new one?', 'apllyAll' : 'Apply to all', 'name' : 'Name', 'size' : 'Size', 'perms' : 'Permissions', 'modify' : 'Modified', 'kind' : 'Kind', 'read' : 'read', 'write' : 'write', 'noaccess' : 'no access', 'and' : 'and', 'unknown' : 'unknown', 'selectall' : 'Select all files', 'selectfiles' : 'Select file(s)', 'selectffile' : 'Select first file', 'selectlfile' : 'Select last file', 'viewlist' : 'List view', 'viewicons' : 'Icons view', 'places' : 'Places', 'calc' : 'Calculate', 'path' : 'Path', 'aliasfor' : 'Alias for', 'locked' : 'Locked', 'dim' : 'Dimensions', 'files' : 'Files', 'folders' : 'Folders', 'items' : 'Items', 'yes' : 'yes', 'no' : 'no', 'link' : 'Link', 'searcresult' : 'Search results', 'selected' : 'selected items', 'about' : 'About', 'shortcuts' : 'Shortcuts', 'help' : 'Help', 'webfm' : 'Web file manager', 'ver' : 'Version', 'protocolver' : 'protocol version', 'homepage' : 'Project home', 'docs' : 'Documentation', 'github' : 'Fork us on Github', 'twitter' : 'Follow us on twitter', 'facebook' : 'Join us on facebook', 'team' : 'Team', 'chiefdev' : 'chief developer', 'developer' : 'developer', 'contributor' : 'contributor', 'maintainer' : 'maintainer', 'translator' : 'translator', 'icons' : 'Icons', 'dontforget' : 'and don\'t forget to take your towel', 'shortcutsof' : 'Shortcuts disabled', 'dropFiles' : 'Drop files here', 'dropFilesBrowser': 'Drop or paste files from browser', // added 30.05.2012 'or' : 'or', 'selectForUpload' : 'Select files to upload', 'moveFiles' : 'Move files', 'copyFiles' : 'Copy files', 'rmFromPlaces' : 'Remove from places', 'aspectRatio' : 'Aspect ratio', 'scale' : 'Scale', 'width' : 'Width', 'height' : 'Height', 'resize' : 'Resize', 'crop' : 'Crop', 'rotate' : 'Rotate', 'rotate-cw' : 'Rotate 90 degrees CW', 'rotate-ccw' : 'Rotate 90 degrees CCW', 'degree' : '°', 'netMountDialogTitle' : 'Mount network volume', // added 18.04.2012 'protocol' : 'Protocol', // added 18.04.2012 'host' : 'Host', // added 18.04.2012 'port' : 'Port', // added 18.04.2012 'user' : 'User', // added 18.04.2012 'pass' : 'Password', // added 18.04.2012 /********************************** mimetypes **********************************/ 'kindUnknown' : 'Unknown', 'kindFolder' : 'Folder', 'kindAlias' : 'Alias', 'kindAliasBroken' : 'Broken alias', // applications 'kindApp' : 'Application', 'kindPostscript' : 'Postscript document', 'kindMsOffice' : 'Microsoft Office document', 'kindMsWord' : 'Microsoft Word document', 'kindMsExcel' : 'Microsoft Excel document', 'kindMsPP' : 'Microsoft Powerpoint presentation', 'kindOO' : 'Open Office document', 'kindAppFlash' : 'Flash application', 'kindPDF' : 'Portable Document Format (PDF)', 'kindTorrent' : 'Bittorrent file', 'kind7z' : '7z archive', 'kindTAR' : 'TAR archive', 'kindGZIP' : 'GZIP archive', 'kindBZIP' : 'BZIP archive', 'kindZIP' : 'ZIP archive', 'kindRAR' : 'RAR archive', 'kindJAR' : 'Java JAR file', 'kindTTF' : 'True Type font', 'kindOTF' : 'Open Type font', 'kindRPM' : 'RPM package', // texts 'kindText' : 'Text document', 'kindTextPlain' : 'Plain text', 'kindPHP' : 'PHP source', 'kindCSS' : 'Cascading style sheet', 'kindHTML' : 'HTML document', 'kindJS' : 'Javascript source', 'kindRTF' : 'Rich Text Format', 'kindC' : 'C source', 'kindCHeader' : 'C header source', 'kindCPP' : 'C++ source', 'kindCPPHeader' : 'C++ header source', 'kindShell' : 'Unix shell script', 'kindPython' : 'Python source', 'kindJava' : 'Java source', 'kindRuby' : 'Ruby source', 'kindPerl' : 'Perl script', 'kindSQL' : 'SQL source', 'kindXML' : 'XML document', 'kindAWK' : 'AWK source', 'kindCSV' : 'Comma separated values', 'kindDOCBOOK' : 'Docbook XML document', // images 'kindImage' : 'Image', 'kindBMP' : 'BMP image', 'kindJPEG' : 'JPEG image', 'kindGIF' : 'GIF Image', 'kindPNG' : 'PNG Image', 'kindTIFF' : 'TIFF image', 'kindTGA' : 'TGA image', 'kindPSD' : 'Adobe Photoshop image', 'kindXBITMAP' : 'X bitmap image', 'kindPXM' : 'Pixelmator image', // media 'kindAudio' : 'Audio media', 'kindAudioMPEG' : 'MPEG audio', 'kindAudioMPEG4' : 'MPEG-4 audio', 'kindAudioMIDI' : 'MIDI audio', 'kindAudioOGG' : 'Ogg Vorbis audio', 'kindAudioWAV' : 'WAV audio', 'AudioPlaylist' : 'MP3 playlist', 'kindVideo' : 'Video media', 'kindVideoDV' : 'DV movie', 'kindVideoMPEG' : 'MPEG movie', 'kindVideoMPEG4' : 'MPEG-4 movie', 'kindVideoAVI' : 'AVI movie', 'kindVideoMOV' : 'Quick Time movie', 'kindVideoWM' : 'Windows Media movie', 'kindVideoFlash' : 'Flash movie', 'kindVideoMKV' : 'Matroska movie', 'kindVideoOGG' : 'Ogg movie' } }; } /* * File: /js/ui/button.js */ /** * @class elFinder toolbar button widget. * If command has variants - create menu * * @author Dmitry (dio) Levashov **/ $.fn.elfinderbutton = function(cmd) { return this.each(function() { var c = 'class', fm = cmd.fm, disabled = fm.res(c, 'disabled'), active = fm.res(c, 'active'), hover = fm.res(c, 'hover'), item = 'elfinder-button-menu-item', selected = 'elfinder-button-menu-item-selected', menu, button = $(this).addClass('ui-state-default elfinder-button') .attr('title', cmd.title) .append('<span class="elfinder-button-icon elfinder-button-icon-'+cmd.name+'"/>') .hover(function(e) { !button.is('.'+disabled) && button[e.type == 'mouseleave' ? 'removeClass' : 'addClass'](hover) /**button.toggleClass(hover);*/ }) .click(function(e) { if (!button.is('.'+disabled)) { if (menu && cmd.variants.length > 1) { // close other menus menu.is(':hidden') && cmd.fm.getUI().click(); e.stopPropagation(); menu.slideToggle(100); } else { cmd.exec(); } } }), hideMenu = function() { menu.hide(); }; // if command has variants create menu if ($.isArray(cmd.variants)) { button.addClass('elfinder-menubutton'); menu = $('<div class="ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>') .hide() .appendTo(button) .zIndex(12+button.zIndex()) .delegate('.'+item, 'hover', function() { $(this).toggleClass(hover) }) .delegate('.'+item, 'click', function(e) { e.preventDefault(); e.stopPropagation(); button.removeClass(hover); cmd.exec(cmd.fm.selected(), $(this).data('value')); }); cmd.fm.bind('disable select', hideMenu).getUI().click(hideMenu); cmd.change(function() { menu.html(''); $.each(cmd.variants, function(i, variant) { menu.append($('<div class="'+item+'">'+variant[1]+'</div>').data('value', variant[0]).addClass(variant[0] == cmd.value ? selected : '')); }); }); } cmd.change(function() { if (cmd.disabled()) { button.removeClass(active+' '+hover).addClass(disabled); } else { button.removeClass(disabled); button[cmd.active() ? 'addClass' : 'removeClass'](active); } }) .change(); }); } /* * File: /js/ui/contextmenu.js */ /** * @class elFinder contextmenu * * @author Dmitry (dio) Levashov **/ $.fn.elfindercontextmenu = function(fm) { return this.each(function() { var menu = $(this).addClass('ui-helper-reset ui-widget ui-state-default ui-corner-all elfinder-contextmenu elfinder-contextmenu-'+fm.direction) .hide() .appendTo('body') .delegate('.elfinder-contextmenu-item', 'mouseenter mouseleave', function() { $(this).toggleClass('ui-state-hover') }), subpos = fm.direction == 'ltr' ? 'left' : 'right', types = $.extend({}, fm.options.contextmenu), tpl = '<div class="elfinder-contextmenu-item"><span class="elfinder-button-icon {icon} elfinder-contextmenu-icon"/><span>{label}</span></div>', item = function(label, icon, callback) { return $(tpl.replace('{icon}', icon ? 'elfinder-button-icon-'+icon : '').replace('{label}', label)) .click(function(e) { e.stopPropagation(); e.stopPropagation(); callback(); }) }, open = function(x, y) { var win = $(window), width = menu.outerWidth(), height = menu.outerHeight(), wwidth = win.width(), wheight = win.height(), scrolltop = win.scrollTop(), scrollleft = win.scrollLeft(), m = fm.UA.Touch? 10 : 0, css = { top : (y + m + height < wheight ? y + m : y - m - height > 0 ? y - m - height : y + m) + scrolltop, left : (x + m + width < wwidth ? x + m : x - m - width) + scrollleft, 'z-index' : 100 + fm.getUI('workzone').zIndex() }; menu.css(css) .show(); css = {'z-index' : css['z-index']+10}; css[subpos] = parseInt(menu.width()); menu.find('.elfinder-contextmenu-sub').css(css); }, close = function() { menu.hide().empty(); }, create = function(type, targets) { var sep = false; $.each(types[type]||[], function(i, name) { var cmd, node, submenu, hover; if (name == '|' && sep) { menu.append('<div class="elfinder-contextmenu-separator"/>'); sep = false; return; } cmd = fm.command(name); if (cmd && cmd.getstate(targets) != -1) { if (cmd.variants) { if (!cmd.variants.length) { return; } node = item(cmd.title, cmd.name, function() {}); submenu = $('<div class="ui-corner-all elfinder-contextmenu-sub"/>') .appendTo(node.append('<span class="elfinder-contextmenu-arrow"/>')); hover = function(){ var win = $(window), baseleft = $(node).offset().left, basetop = $(node).offset().top, basewidth = $(node).outerWidth(), width = submenu.outerWidth(), height = submenu.outerHeight(), wwidth = win.scrollLeft() + win.width(), wheight = win.scrollTop() + win.height(), margin = 5, x, y, over; over = (baseleft + basewidth + width + margin) - wwidth; x = (over > 0)? basewidth - over : basewidth; over = (basetop + 5 + height + margin) - wheight; y = (over > 0)? 5 - over : 5; var css = { left : x, top : y }; submenu.css(css).toggle(); }; node.addClass('elfinder-contextmenu-group') .hover(function() { hover(); }) .on('touchstart', function(e){ if (node.hasClass('ui-state-hover')) { return true; } node.addClass('ui-state-hover'); e.preventDefault(); hover(); return false; }); $.each(cmd.variants, function(i, variant) { submenu.append( $('<div class="elfinder-contextmenu-item"><span>'+variant[1]+'</span></div>') .on('click touchstart', function(e) { e.stopPropagation(); close(); cmd.exec(targets, variant[0]); }) ); }); } else { node = item(cmd.title, cmd.name, function() { close(); cmd.exec(targets); }) } menu.append(node) sep = true; } }) }, createFromRaw = function(raw) { $.each(raw, function(i, data) { var node; if (data.label && typeof data.callback == 'function') { node = item(data.label, data.icon, function() { close(); data.callback(); }); menu.append(node); } }) }; fm.one('load', function() { fm.bind('contextmenu', function(e) { var data = e.data; close(); if (data.type && data.targets) { create(data.type, data.targets); } else if (data.raw) { createFromRaw(data.raw); } menu.children().length && open(data.x, data.y); }) .one('destroy', function() { menu.remove(); }) .bind('disable select', close) .getUI().click(close); }); }); } /* * File: /js/ui/cwd.js */ /** * elFinder current working directory ui. * * @author Dmitry (dio) Levashov **/ $.fn.elfindercwd = function(fm, options) { this.not('.elfinder-cwd').each(function() { // fm.time('cwdLoad'); var list = fm.viewType == 'list', undef = 'undefined', /** * Select event full name * * @type String **/ evtSelect = 'select.'+fm.namespace, /** * Unselect event full name * * @type String **/ evtUnselect = 'unselect.'+fm.namespace, /** * Disable event full name * * @type String **/ evtDisable = 'disable.'+fm.namespace, /** * Disable event full name * * @type String **/ evtEnable = 'enable.'+fm.namespace, c = 'class', /** * File css class * * @type String **/ clFile = fm.res(c, 'cwdfile'), /** * Selected css class * * @type String **/ fileSelector = '.'+clFile, /** * Selected css class * * @type String **/ clSelected = 'ui-selected', /** * Disabled css class * * @type String **/ clDisabled = fm.res(c, 'disabled'), /** * Draggable css class * * @type String **/ clDraggable = fm.res(c, 'draggable'), /** * Droppable css class * * @type String **/ clDroppable = fm.res(c, 'droppable'), /** * Hover css class * * @type String **/ clHover = fm.res(c, 'hover'), /** * Hover css class * * @type String **/ clDropActive = fm.res(c, 'adroppable'), /** * Css class for temporary nodes (for mkdir/mkfile) commands * * @type String **/ clTmp = clFile+'-tmp', /** * Number of thumbnails to load in one request (new api only) * * @type Number **/ tmbNum = fm.options.loadTmbs > 0 ? fm.options.loadTmbs : 5, /** * Current search query. * * @type String */ query = '', lastSearch = [], /** * File templates * * @type Object **/ templates = { icon : '<div id="{hash}" class="'+clFile+' {permsclass} {dirclass} ui-corner-all" title="{tooltip}"><div class="elfinder-cwd-file-wrapper ui-corner-all"><div class="elfinder-cwd-icon {mime} ui-corner-all" unselectable="on" {style}/>{marker}</div><div class="elfinder-cwd-filename" title="{name}">{name}</div></div>', row : '<tr id="{hash}" class="'+clFile+' {permsclass} {dirclass}" title="{tooltip}"><td><div class="elfinder-cwd-file-wrapper"><span class="elfinder-cwd-icon {mime}"/>{marker}<span class="elfinder-cwd-filename">{name}</span></div></td><td>{perms}</td><td>{date}</td><td>{size}</td><td>{kind}</td></tr>' }, permsTpl = fm.res('tpl', 'perms'), symlinkTpl = fm.res('tpl', 'symlink'), /** * Template placeholders replacement rules * * @type Object **/ replacement = { permsclass : function(f) { return fm.perms2class(f); }, perms : function(f) { return fm.formatPermissions(f); }, dirclass : function(f) { return f.mime == 'directory' ? 'directory' : ''; }, mime : function(f) { return fm.mime2class(f.mime); }, size : function(f) { return fm.formatSize(f.size); }, date : function(f) { return fm.formatDate(f); }, kind : function(f) { return fm.mime2kind(f); }, marker : function(f) { return (f.alias || f.mime == 'symlink-broken' ? symlinkTpl : '')+(!f.read || !f.write ? permsTpl : ''); }, tooltip : function(f) { var title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''); return f.tooltip? fm.escape(f.tooltip).replace(/"/g, '"').replace(/\r/g, ' ') + ' ' + title : title; } }, /** * Return file html * * @param Object file info * @return String **/ itemhtml = function(f) { f.name = fm.escape(f.name); return templates[list ? 'row' : 'icon'] .replace(/\{([a-z]+)\}/g, function(s, e) { return replacement[e] ? replacement[e](f) : (f[e] ? f[e] : ''); }); }, /** * Flag. Required for msie to avoid unselect files on dragstart * * @type Boolean **/ selectLock = false, /** * Move selection to prev/next file * * @param String move direction * @param Boolean append to current selection * @return void * @rise select */ select = function(keyCode, append) { var code = $.ui.keyCode, prev = keyCode == code.LEFT || keyCode == code.UP, sel = cwd.find('[id].'+clSelected), selector = prev ? 'first:' : 'last', s, n, sib, top, left; function sibling(n, direction) { return n[direction+'All']('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):first'); } if (sel.length) { s = sel.filter(prev ? ':first' : ':last'); sib = sibling(s, prev ? 'prev' : 'next'); if (!sib.length) { // there is no sibling on required side - do not move selection n = s; } else if (list || keyCode == code.LEFT || keyCode == code.RIGHT) { // find real prevoius file n = sib; } else { // find up/down side file in icons view top = s.position().top; left = s.position().left; n = s; if (prev) { do { n = n.prev('[id]'); } while (n.length && !(n.position().top < top && n.position().left <= left)); if (n.is('.'+clDisabled)) { n = sibling(n, 'next'); } } else { do { n = n.next('[id]'); } while (n.length && !(n.position().top > top && n.position().left >= left)); if (n.is('.'+clDisabled)) { n = sibling(n, 'prev'); } // there is row before last one - select last file if (!n.length) { sib = cwd.find('[id]:not(.'+clDisabled+'):last'); if (sib.position().top > top) { n = sib; } } } } // !append && unselectAll(); } else { // there are no selected file - select first/last one n = cwd.find('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):'+(prev ? 'last' : 'first')); } if (n && n.length && !n.is('.elfinder-cwd-parent')) { if (append) { // append new files to selected n = s.add(s[prev ? 'prevUntil' : 'nextUntil']('#'+n.attr('id'))).add(n); } else { // unselect selected files sel.trigger(evtUnselect); } // select file(s) n.trigger(evtSelect); // set its visible scrollToView(n.filter(prev ? ':first' : ':last')); // update cache/view trigger(); } }, selectedFiles = [], selectFile = function(hash) { cwd.find('#'+hash).trigger(evtSelect); }, selectAll = function() { var phash = fm.cwd().hash; cwd.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').trigger(evtSelect); selectedFiles = $.map(fm.files(), function(f) { return f.phash == phash ? f.hash : null ;}); trigger(); }, /** * Unselect all files * * @return void */ unselectAll = function() { selectedFiles = []; cwd.find('[id].'+clSelected).trigger(evtUnselect); trigger(); }, /** * Return selected files hashes list * * @return Array */ selected = function() { return selectedFiles; }, /** * Fire elfinder "select" event and pass selected files to it * * @return void */ trigger = function() { fm.trigger('select', {selected : selectedFiles}); }, /** * Scroll file to set it visible * * @param DOMElement file/dir node * @return void */ scrollToView = function(o) { var ftop = o.position().top, fheight = o.outerHeight(true), wtop = wrapper.scrollTop(), wheight = wrapper.innerHeight(); if (ftop + fheight > wtop + wheight) { wrapper.scrollTop(parseInt(ftop + fheight - wheight)); } else if (ftop < wtop) { wrapper.scrollTop(ftop); } }, /** * Files we get from server but not show yet * * @type Array **/ buffer = [], /** * Return index of elements with required hash in buffer * * @param String file hash * @return Number */ index = function(hash) { var l = buffer.length; while (l--) { if (buffer[l].hash == hash) { return l; } } return -1; }, /** * Scroll event name * * @type String **/ scrollEvent = 'scroll.'+fm.namespace, /** * Cwd scroll event handler. * Lazy load - append to cwd not shown files * * @return void */ render = function() { var html = [], dirs = false, ltmb = [], atmb = {}, last = cwd.find('[id]:last'), top = !last.length, place = list ? cwd.children('table').children('tbody') : cwd, files; if (!buffer.length) { return wrapper.unbind(scrollEvent); } while ((!last.length || last.position().top <= wrapper.height() + wrapper.scrollTop() + fm.options.showThreshold) && (files = buffer.splice(0, fm.options.showFiles)).length) { html = $.map(files, function(f) { if (f.hash && f.name) { if (f.mime == 'directory') { dirs = true; } if (f.tmb) { f.tmb === 1 ? ltmb.push(f.hash) : (atmb[f.hash] = f.tmb); } return itemhtml(f); } return null; }); place.append(html.join('')); last = cwd.find('[id]:last'); // scroll top on dir load to avoid scroll after page reload top && cwd.scrollTop(0); } // load/attach thumbnails attachThumbnails(atmb); ltmb.length && loadThumbnails(ltmb); // make directory droppable dirs && makeDroppable(); if (selectedFiles.length) { place.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').each(function() { var id = this.id; $.inArray(id, selectedFiles) !== -1 && $(this).trigger(evtSelect); }); } }, /** * Droppable options for cwd. * Do not add class on childs file over * * @type Object */ droppable = $.extend({}, fm.droppable, { over : function(e, ui) { var hash = fm.cwd().hash; $.each(ui.helper.data('files'), function(i, h) { if (fm.file(h).phash == hash) { cwd.removeClass(clDropActive); return false; } }); } }), /** * Make directory droppable * * @return void */ makeDroppable = function() { setTimeout(function() { cwd.find('.directory:not(.'+clDroppable+',.elfinder-na,.elfinder-ro)').droppable(fm.droppable); }, 20); }, /** * Preload required thumbnails and on load add css to files. * Return false if required file is not visible yet (in buffer) - * required for old api to stop loading thumbnails. * * @param Object file hash -> thumbnail map * @return Boolean */ attachThumbnails = function(images) { var url = fm.option('tmbUrl'), ret = true, ndx; $.each(images, function(hash, tmb) { var node = cwd.find('#'+hash); if (node.length) { (function(node, tmb) { $('<img/>') .load(function() { node.find('.elfinder-cwd-icon').css('background', "url('"+tmb+"') center center no-repeat"); }) .attr('src', tmb); })(node, url+tmb); } else { ret = false; if ((ndx = index(hash)) != -1) { buffer[ndx].tmb = tmb; } } }); return ret; }, /** * Load thumbnails from backend. * * @param Array|Boolean files hashes list for new api | true for old api * @return void */ loadThumbnails = function(files) { var tmbs = []; if (fm.oldAPI) { fm.request({ data : {cmd : 'tmb', current : fm.cwd().hash}, preventFail : true }) .done(function(data) { if (attachThumbnails(data.images||[]) && data.tmb) { loadThumbnails(); } }); return; } tmbs = tmbs = files.splice(0, tmbNum); if (tmbs.length) { fm.request({ data : {cmd : 'tmb', targets : tmbs}, preventFail : true }) .done(function(data) { if (attachThumbnails(data.images||[])) { loadThumbnails(files); } }); } }, /** * Add new files to cwd/buffer * * @param Array new files * @return void */ add = function(files) { var place = list ? cwd.find('tbody') : cwd, l = files.length, ltmb = [], atmb = {}, dirs = false, findNode = function(file) { var pointer = cwd.find('[id]:first'), file2; while (pointer.length) { file2 = fm.file(pointer.attr('id')); if (!pointer.is('.elfinder-cwd-parent') && file2 && fm.compare(file, file2) < 0) { return pointer; } pointer = pointer.next('[id]'); } }, findIndex = function(file) { var l = buffer.length, i; for (i =0; i < l; i++) { if (fm.compare(file, buffer[i]) < 0) { return i; } } return l || -1; }, file, hash, node, ndx; while (l--) { file = files[l]; hash = file.hash; if (cwd.find('#'+hash).length) { continue; } if ((node = findNode(file)) && node.length) { node.before(itemhtml(file)); } else if ((ndx = findIndex(file)) >= 0) { buffer.splice(ndx, 0, file); } else { place.append(itemhtml(file)); } if (cwd.find('#'+hash).length) { if (file.mime == 'directory') { dirs = true; } else if (file.tmb) { file.tmb === 1 ? ltmb.push(hash) : (atmb[hash] = file.tmb); } } } attachThumbnails(atmb); ltmb.length && loadThumbnails(ltmb); dirs && makeDroppable(); }, /** * Remove files from cwd/buffer * * @param Array files hashes * @return void */ remove = function(files) { var l = files.length, hash, n, ndx; while (l--) { hash = files[l]; if ((n = cwd.find('#'+hash)).length) { try { n.detach(); } catch(e) { fm.debug('error', e); } } else if ((ndx = index(hash)) != -1) { buffer.splice(ndx, 1); } } }, msg = { name : fm.i18n('name'), perm : fm.i18n('perms'), mod : fm.i18n('modify'), size : fm.i18n('size'), kind : fm.i18n('kind') }, /** * Update directory content * * @param Array files * @return void */ content = function(files, any) { var phash = fm.cwd().hash; // console.log(files) unselectAll(); try { // to avoid problem with draggable cwd.children('table,'+fileSelector).remove(); } catch (e) { cwd.html(''); } cwd.removeClass('elfinder-cwd-view-icons elfinder-cwd-view-list') .addClass('elfinder-cwd-view-'+(list ? 'list' :'icons')); wrapper[list ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-list'); list && cwd.html('<table><thead><tr class="ui-state-default"><td class="elfinder-cwd-view-th-name">'+msg.name+'</td><td class="elfinder-cwd-view-th-perm">'+msg.perm+'</td><td class="elfinder-cwd-view-th-date">'+msg.mod+'</td><td class="elfinder-cwd-view-th-size">'+msg.size+'</td><td class="elfinder-cwd-view-th-kind">'+msg.kind+'</td></tr></thead><tbody/></table>'); buffer = $.map(files, function(f) { return any || f.phash == phash ? f : null; }); buffer = fm.sortFiles(buffer); wrapper.bind(scrollEvent, render).trigger(scrollEvent); phash = fm.cwd().phash; if (options.oldSchool && phash && !query) { var parent = $.extend(true, {}, fm.file(phash), {name : '..', mime : 'directory'}); parent = $(itemhtml(parent)) .addClass('elfinder-cwd-parent') .bind('mousedown click mouseup touchstart touchmove touchend dblclick mouseenter', function(e) { //.bind('mousedown click mouseup dblclick mouseenter', function(e) { e.preventDefault(); e.stopPropagation(); }) .dblclick(function() { fm.exec('open', this.id); }); (list ? cwd.find('tbody') : cwd).prepend(parent); } }, /** * CWD node itself * * @type JQuery **/ cwd = $(this) .addClass('ui-helper-clearfix elfinder-cwd') .attr('unselectable', 'on') // fix ui.selectable bugs and add shift+click support .delegate(fileSelector, 'click.'+fm.namespace, function(e) { var p = this.id ? $(this) : $(this).parents('[id]:first'), prev = p.prevAll('.'+clSelected+':first'), next = p.nextAll('.'+clSelected+':first'), pl = prev.length, nl = next.length, sib; if (cwd.data('longtap')) { e.stopPropagation(); return; } e.stopImmediatePropagation(); if (e.shiftKey && (pl || nl)) { sib = pl ? p.prevUntil('#'+prev.attr('id')) : p.nextUntil('#'+next.attr('id')); sib.add(p).trigger(evtSelect); } else if (e.ctrlKey || e.metaKey) { p.trigger(p.is('.'+clSelected) ? evtUnselect : evtSelect); } else { if (p.data('touching') && p.is('.'+clSelected)) { p.data('touching', null); fm.dblclick({file : this.id}); unselectAll(); return; } else { unselectAll(); p.trigger(evtSelect); } } trigger(); }) // call fm.open() .delegate(fileSelector, 'dblclick.'+fm.namespace, function(e) { fm.dblclick({file : this.id}); }) // for touch device .delegate(fileSelector, 'touchstart.'+fm.namespace, function(e) { e.stopPropagation(); var p = this.id ? $(this) : $(this).parents('[id]:first'), sel = p.prevAll('.'+clSelected+':first').length + p.nextAll('.'+clSelected+':first').length; cwd.data('longtap', null); p.data('touching', true); p.data('tmlongtap', setTimeout(function(){ // long tap cwd.data('longtap', true); if (p.is('.'+clSelected) && sel > 0) { p.trigger(evtUnselect); trigger(); } else { if (e.target.nodeName != 'TD' || fm.selected().length > 0) { p.trigger(evtSelect); trigger(); p.trigger(fm.trigger('contextmenu', { 'type' : 'files', 'targets' : fm.selected(), 'x' : e.originalEvent.touches[0].clientX, 'y' : e.originalEvent.touches[0].clientY })); } } }, 500)); }) .delegate(fileSelector, 'touchmove.'+fm.namespace+' touchend.'+fm.namespace, function(e) { var p = this.id ? $(this) : $(this).parents('[id]:first'); e.stopPropagation(); clearTimeout(p.data('tmlongtap')); }) // attach draggable .delegate(fileSelector, 'mouseenter.'+fm.namespace, function(e) { var $this = $(this), target = list ? $this : $this.children(); if (!$this.is('.'+clTmp) && !target.is('.'+clDraggable+',.'+clDisabled)) { target.draggable(fm.draggable); } }) // add hover class to selected file .delegate(fileSelector, evtSelect, function(e) { var $this = $(this), id = $this.attr('id'); if (!selectLock && !$this.is('.'+clDisabled)) { $this.addClass(clSelected).children().addClass(clHover); if ($.inArray(id, selectedFiles) === -1) { selectedFiles.push(id); } } }) // remove hover class from unselected file .delegate(fileSelector, evtUnselect, function(e) { var $this = $(this), id = $this.attr('id'), ndx; if (!selectLock) { $(this).removeClass(clSelected).children().removeClass(clHover); ndx = $.inArray(id, selectedFiles); if (ndx !== -1) { selectedFiles.splice(ndx, 1); } } }) // disable files wich removing or moving .delegate(fileSelector, evtDisable, function() { var $this = $(this).removeClass(clSelected).addClass(clDisabled), target = (list ? $this : $this.children()).removeClass(clHover); $this.is('.'+clDroppable) && $this.droppable('disable'); target.is('.'+clDraggable) && target.draggable('disable'); !list && target.removeClass(clDisabled); }) // if any files was not removed/moved - unlock its .delegate(fileSelector, evtEnable, function() { var $this = $(this).removeClass(clDisabled), target = list ? $this : $this.children(); $this.is('.'+clDroppable) && $this.droppable('enable'); target.is('.'+clDraggable) && target.draggable('enable'); }) .delegate(fileSelector, 'scrolltoview', function() { scrollToView($(this)); }) .delegate(fileSelector, 'mouseenter.'+fm.namespace+' mouseleave.'+fm.namespace, function(e) { fm.trigger('hover', {hash : $(this).attr('id'), type : e.type}); $(this).toggleClass('ui-state-hover'); }) .bind('contextmenu.'+fm.namespace, function(e) { var file = $(e.target).closest('.'+clFile); if (file.length && (e.target.nodeName != 'TD' || $.inArray(file.get(0).id, fm.selected()) > -1)) { e.stopPropagation(); e.preventDefault(); if (!file.is('.'+clDisabled) && !file.data('touching')) { if (!file.is('.'+clSelected)) { // cwd.trigger('unselectall'); unselectAll(); file.trigger(evtSelect); trigger(); } fm.trigger('contextmenu', { 'type' : 'files', 'targets' : fm.selected(), 'x' : e.clientX, 'y' : e.clientY }); } } // e.preventDefault(); }) // unselect all on cwd click .bind('click.'+fm.namespace, function(e) { if (cwd.data('longtap')) { e.stopPropagation(); return; } !e.shiftKey && !e.ctrlKey && !e.metaKey && unselectAll(); }) // make files selectable .selectable({ filter : fileSelector, stop : trigger, delay : 250, selected : function(e, ui) { $(ui.selected).trigger(evtSelect); }, unselected : function(e, ui) { $(ui.unselected).trigger(evtUnselect); } }) // make cwd itself droppable for folders from nav panel .droppable(droppable) // prepend fake file/dir .bind('create.'+fm.namespace, function(e, file) { var parent = list ? cwd.find('tbody') : cwd, p = parent.find('.elfinder-cwd-parent'), file = $(itemhtml(file)).addClass(clTmp); unselectAll(); if (p.length) { p.after(file); } else { parent.prepend(file); } cwd.scrollTop(0); }) // unselect all selected files .bind('unselectall', unselectAll) .bind('selectfile', function(e, id) { cwd.find('#'+id).trigger(evtSelect); trigger(); }), wrapper = $('<div class="elfinder-cwd-wrapper"/>') .bind('contextmenu', function(e) { e.preventDefault(); fm.trigger('contextmenu', { 'type' : 'cwd', 'targets' : [fm.cwd().hash], 'x' : e.clientX, 'y' : e.clientY }); }) // for touch device .bind('touchstart.'+fm.namespace, function(e) { var p = $(this); cwd.data('longtap', null); p.data('touching', true); p.data('tmlongtap', setTimeout(function(){ // long tap cwd.data('longtap', true); fm.trigger('contextmenu', { 'type' : 'cwd', 'targets' : [fm.cwd().hash], 'x' : e.originalEvent.touches[0].clientX, 'y' : e.originalEvent.touches[0].clientY }); }, 500)); }) .bind('touchmove.'+fm.namespace+' touchend.'+fm.namespace, function(e) { clearTimeout($(this).data('tmlongtap')); }), resize = function() { var h = 0; wrapper.siblings('.elfinder-panel:visible').each(function() { h += $(this).outerHeight(true); }); wrapper.height(wz.height() - h); }, // elfinder node parent = $(this).parent().resize(resize), // workzone node wz = parent.children('.elfinder-workzone').append(wrapper.append(this)) ; // for iOS5 bug $('body').on('touchstart touchmove touchend', function(e){}); if (fm.dragUpload) { wrapper[0].addEventListener('dragenter', function(e) { e.preventDefault(); e.stopPropagation(); wrapper.addClass(clDropActive); }, false); wrapper[0].addEventListener('dragleave', function(e) { e.preventDefault(); e.stopPropagation(); e.target == cwd[0] && wrapper.removeClass(clDropActive); }, false); wrapper[0].addEventListener('dragover', function(e) { e.preventDefault(); e.stopPropagation(); }, false); wrapper[0].addEventListener('drop', function(e) { e.preventDefault(); wrapper.removeClass(clDropActive); var file = false; var type = ''; if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length) { file = e.dataTransfer.files; type = 'files'; } else if (e.dataTransfer.getData('text/html')) { file = [ e.dataTransfer.getData('text/html') ]; type = 'html'; } else if (e.dataTransfer.getData('text')) { file = [ e.dataTransfer.getData('text') ]; type = 'text'; } if (file) { fm.exec('upload', {files : file, type : type}); } }, false); } fm .bind('open', function(e) { content(e.data.files); }) .bind('search', function(e) { lastSearch = e.data.files; content(lastSearch, true); }) .bind('searchend', function() { lastSearch = []; if (query) { query = ''; content(fm.files()); } }) .bind('searchstart', function(e) { query = e.data.query; }) .bind('sortchange', function() { content(query ? lastSearch : fm.files(), !!query); }) .bind('viewchange', function() { var sel = fm.selected(), l = fm.storage('view') == 'list'; if (l != list) { list = l; content(query ? lastSearch : fm.files(), !!query); $.each(sel, function(i, h) { selectFile(h); }); trigger(); } resize(); }) .add(function(e) { var phash = fm.cwd().hash, files = query ? $.map(e.data.added || [], function(f) { return f.name.indexOf(query) === -1 ? null : f ;}) : $.map(e.data.added || [], function(f) { return f.phash == phash ? f : null; }) ; add(files); }) .change(function(e) { var phash = fm.cwd().hash, sel = fm.selected(), files; if (query) { $.each(e.data.changed || [], function(i, file) { remove([file.hash]); if (file.name.indexOf(query) !== -1) { add([file]); $.inArray(file.hash, sel) !== -1 && selectFile(file.hash); } }); } else { $.each($.map(e.data.changed || [], function(f) { return f.phash == phash ? f : null; }), function(i, file) { remove([file.hash]); add([file]); $.inArray(file.hash, sel) !== -1 && selectFile(file.hash); }); } trigger(); }) .remove(function(e) { remove(e.data.removed || []); trigger(); }) // fix cwd height if it less then wrapper .bind('open add search searchend', function() { cwd.css('height', 'auto'); if (cwd.outerHeight(true) < wrapper.height()) { cwd.height(wrapper.height() - (cwd.outerHeight(true) - cwd.height()) - 2); } }) // select dragged file if no selected, disable selectable .dragstart(function(e) { var target = $(e.data.target), oe = e.data.originalEvent; if (target.is(fileSelector)) { if (!target.is('.'+clSelected)) { !(oe.ctrlKey || oe.metaKey || oe.shiftKey) && unselectAll(); target.trigger(evtSelect); trigger(); } cwd.droppable('disable'); } cwd.selectable('disable').removeClass(clDisabled); selectLock = true; }) // enable selectable .dragstop(function() { cwd.selectable('enable').droppable('enable'); selectLock = false; }) .bind('lockfiles unlockfiles', function(e) { var event = e.type == 'lockfiles' ? evtDisable : evtEnable, files = e.data.files || [], l = files.length; while (l--) { cwd.find('#'+files[l]).trigger(event); } trigger(); }) // select new files after some actions .bind('mkdir mkfile duplicate upload rename archive extract', function(e) { var phash = fm.cwd().hash, files; unselectAll(); $.each(e.data.added || [], function(i, file) { file && file.phash == phash && selectFile(file.hash); }); trigger(); }) .shortcut({ pattern :'ctrl+a', description : 'selectall', callback : selectAll }) .shortcut({ pattern : 'left right up down shift+left shift+right shift+up shift+down', description : 'selectfiles', type : 'keydown' , //fm.UA.Firefox || fm.UA.Opera ? 'keypress' : 'keydown', callback : function(e) { select(e.keyCode, e.shiftKey); } }) .shortcut({ pattern : 'home', description : 'selectffile', callback : function(e) { unselectAll(); scrollToView(cwd.find('[id]:first').trigger(evtSelect)); trigger(); } }) .shortcut({ pattern : 'end', description : 'selectlfile', callback : function(e) { unselectAll(); scrollToView(cwd.find('[id]:last').trigger(evtSelect)) ; trigger(); } }); }); // fm.timeEnd('cwdLoad') return this; } /* * File: /js/ui/dialog.js */ /** * @class elFinder dialog * * @author Dmitry (dio) Levashov **/ $.fn.elfinderdialog = function(opts) { var dialog; if (typeof(opts) == 'string' && (dialog = this.closest('.ui-dialog')).length) { if (opts == 'open') { dialog.css('display') == 'none' && dialog.fadeIn(120, function() { dialog.trigger('open'); }); } else if (opts == 'close') { dialog.css('display') != 'none' && dialog.hide().trigger('close'); } else if (opts == 'destroy') { dialog.hide().remove(); } else if (opts == 'toTop') { dialog.trigger('totop'); } } opts = $.extend({}, $.fn.elfinderdialog.defaults, opts); this.filter(':not(.ui-dialog-content)').each(function() { var self = $(this).addClass('ui-dialog-content ui-widget-content'), parent = self.parent(), clactive = 'elfinder-dialog-active', cldialog = 'elfinder-dialog', clnotify = 'elfinder-dialog-notify', clhover = 'ui-state-hover', id = parseInt(Math.random()*1000000), overlay = parent.children('.elfinder-overlay'), buttonset = $('<div class="ui-dialog-buttonset"/>'), buttonpane = $('<div class=" ui-helper-clearfix ui-dialog-buttonpane ui-widget-content"/>') .append(buttonset), dialog = $('<div class="ui-dialog ui-widget ui-widget-content ui-corner-all ui-draggable std42-dialog '+cldialog+' '+opts.cssClass+'"/>') .hide() .append(self) .appendTo(parent) .draggable({ handle : '.ui-dialog-titlebar', containment : 'document' }) .css({ width : opts.width, height : opts.height }) .mousedown(function(e) { e.stopPropagation(); $(document).mousedown(); if (!dialog.is('.'+clactive)) { parent.find('.'+cldialog+':visible').removeClass(clactive); dialog.addClass(clactive).zIndex(maxZIndex() + 1); } }) .bind('open', function() { dialog.trigger('totop'); typeof(opts.open) == 'function' && $.proxy(opts.open, self[0])(); if (!dialog.is('.'+clnotify)) { parent.find('.'+cldialog+':visible').not('.'+clnotify).each(function() { var d = $(this), top = parseInt(d.css('top')), left = parseInt(d.css('left')), _top = parseInt(dialog.css('top')), _left = parseInt(dialog.css('left')) ; if (d[0] != dialog[0] && (top == _top || left == _left)) { dialog.css({ top : (top+10)+'px', left : (left+10)+'px' }); } }); } }) .bind('close', function() { var dialogs = parent.find('.elfinder-dialog:visible'), z = maxZIndex(); $(this).data('modal') && overlay.elfinderoverlay('hide'); // get focus to next dialog if (dialogs.length) { dialogs.each(function() { var d = $(this); if (d.zIndex() >= z) { d.trigger('totop'); return false; } }) } else { // return focus to parent setTimeout(function() { parent.mousedown().click(); }, 10); } if (typeof(opts.close) == 'function') { $.proxy(opts.close, self[0])(); } else if (opts.destroyOnClose) { dialog.hide().remove(); } }) .bind('totop', function() { $(this).mousedown().find('.ui-button:first').focus().end().find(':text:first').focus(); $(this).data('modal') && overlay.elfinderoverlay('show'); overlay.zIndex($(this).zIndex()); }) .data({modal: opts.modal}), maxZIndex = function() { var z = parent.zIndex() + 10; parent.find('.'+cldialog+':visible').each(function() { var _z; if (this != dialog[0]) { _z = $(this).zIndex(); if (_z > z) { z = _z; } } }) return z; }, top ; if (!opts.position) { top = parseInt((parent.height() - dialog.outerHeight())/2 - 42); opts.position = { top : (top > 0 ? top : 0)+'px', left : parseInt((parent.width() - dialog.outerWidth())/2)+'px' } } dialog.css(opts.position); if (opts.closeOnEscape) { $(document).bind('keyup.'+id, function(e) { if (e.keyCode == $.ui.keyCode.ESCAPE && dialog.is('.'+clactive)) { self.elfinderdialog('close'); $(document).unbind('keyup.'+id); } }) } dialog.prepend( $('<div class="ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix">'+opts.title+'</div>') .prepend($('<a href="#" class="ui-dialog-titlebar-close ui-corner-all"><span class="ui-icon ui-icon-closethick"/></a>') .mousedown(function(e) { e.preventDefault(); self.elfinderdialog('close'); })) ); $.each(opts.buttons, function(name, cb) { var button = $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">'+name+'</span></button>') .click($.proxy(cb, self[0])) .hover(function(e) { $(this)[e.type == 'mouseenter' ? 'focus' : 'blur']() }) .focus(function() { $(this).addClass(clhover) }) .blur(function() { $(this).removeClass(clhover) }) .keydown(function(e) { var next; if (e.keyCode == $.ui.keyCode.ENTER) { $(this).click(); } else if (e.keyCode == $.ui.keyCode.TAB) { next = $(this).next('.ui-button'); next.length ? next.focus() : $(this).parent().children('.ui-button:first').focus() } }) buttonset.append(button); }) buttonset.children().length && dialog.append(buttonpane); if (opts.resizable && $.fn.resizable) { dialog.resizable({ minWidth : opts.minWidth, minHeight : opts.minHeight, alsoResize : this }); } typeof(opts.create) == 'function' && $.proxy(opts.create, this)(); opts.autoOpen && self.elfinderdialog('open'); }); return this; } $.fn.elfinderdialog.defaults = { cssClass : '', title : '', modal : false, resizable : true, autoOpen : true, closeOnEscape : true, destroyOnClose : false, buttons : {}, position : null, width : 320, height : 'auto', minWidth : 200, minHeight : 110 } /* * File: /js/ui/navbar.js */ /** * @class elfindernav - elFinder container for diretories tree and places * * @author Dmitry (dio) Levashov **/ $.fn.elfindernavbar = function(fm, opts) { this.not('.elfinder-navbar').each(function() { var nav = $(this).addClass('ui-state-default elfinder-navbar'), parent = nav.parent() .resize(function() { nav.height(wz.height() - delta); }), wz = parent.children('.elfinder-workzone').append(nav), delta = nav.outerHeight() - nav.height(), ltr = fm.direction == 'ltr', handle; if ($.fn.resizable) { handle = nav.resizable({ handles : ltr ? 'e' : 'w', minWidth : opts.minWidth || 150, maxWidth : opts.maxWidth || 500 }) .bind('resize scroll', function() { var offset = (fm.UA.Opera && nav.scrollLeft())? 20 : 2; handle.css({ top : parseInt(nav.scrollTop())+'px', left : ltr ? 'auto' : parseInt(nav.scrollLeft() + offset), right: ltr ? parseInt(nav.scrollLeft() - offset) * -1 : 'auto' }); }) .find('.ui-resizable-handle').zIndex(nav.zIndex() + 10); if (!ltr) { nav.resize(function() { nav.css('left', null).css('right', 0); }); } fm.one('open', function() { setTimeout(function() { nav.trigger('resize'); }, 150); }); } }); return this; }; /* * File: /js/ui/overlay.js */ $.fn.elfinderoverlay = function(opts) { this.filter(':not(.elfinder-overlay)').each(function() { opts = $.extend({}, opts); $(this).addClass('ui-widget-overlay elfinder-overlay') .hide() .mousedown(function(e) { e.preventDefault(); e.stopPropagation(); }) .data({ cnt : 0, show : typeof(opts.show) == 'function' ? opts.show : function() { }, hide : typeof(opts.hide) == 'function' ? opts.hide : function() { } }); }); if (opts == 'show') { var o = this.eq(0), cnt = o.data('cnt') + 1, show = o.data('show'); o.data('cnt', cnt); if (o.is(':hidden')) { o.zIndex(o.parent().zIndex()+1); o.show(); show(); } } if (opts == 'hide') { var o = this.eq(0), cnt = o.data('cnt') - 1, hide = o.data('hide'); o.data('cnt', cnt); if (cnt == 0 && o.is(':visible')) { o.hide(); hide(); } } return this; } /* * File: /js/ui/panel.js */ $.fn.elfinderpanel = function(fm) { return this.each(function() { var panel = $(this).addClass('elfinder-panel ui-state-default ui-corner-all'), margin = 'margin-'+(fm.direction == 'ltr' ? 'left' : 'right'); fm.one('load', function(e) { var navbar = fm.getUI('navbar'); panel.css(margin, parseInt(navbar.outerWidth(true))); navbar.bind('resize', function() { panel.is(':visible') && panel.css(margin, parseInt(navbar.outerWidth(true))) }) }) }) } /* * File: /js/ui/path.js */ /** * @class elFinder ui * Display current folder path in statusbar. * Click on folder name in path - open folder * * @author Dmitry (dio) Levashov **/ $.fn.elfinderpath = function(fm) { return this.each(function() { var path = $(this).addClass('elfinder-path').html(' ') .delegate('a', 'click', function(e) { var hash = $(this).attr('href').substr(1); e.preventDefault(); hash != fm.cwd().hash && fm.exec('open', hash); }) .prependTo(fm.getUI('statusbar').show()) fm.bind('open searchend', function() { var dirs = []; $.each(fm.parents(fm.cwd().hash), function(i, hash) { dirs.push('<a href="#'+hash+'">'+fm.escape(fm.file(hash).name)+'</a>'); }); path.html(dirs.join(fm.option('separator'))); }) .bind('search', function() { path.html(fm.i18n('searcresult')); }); }); } /* * File: /js/ui/places.js */ /** * @class elFinder places/favorites ui * * @author Dmitry (dio) Levashov **/ $.fn.elfinderplaces = function(fm, opts) { return this.each(function() { var dirs = [], c = 'class', navdir = fm.res(c, 'navdir'), collapsed = fm.res(c, 'navcollapse'), expanded = fm.res(c, 'navexpand'), hover = fm.res(c, 'hover'), clroot = fm.res(c, 'treeroot'), tpl = fm.res('tpl', 'navdir'), ptpl = fm.res('tpl', 'perms'), spinner = $(fm.res('tpl', 'navspinner')), /** * Convert places dir node into dir hash * * @param String directory id * @return String **/ id2hash = function(id) { return id.substr(6); }, /** * Convert places dir node into dir hash * * @param String directory id * @return String **/ hash2id = function(hash) { return 'place-'+hash; }, /** * Save current places state * * @return void **/ save = function() { fm.storage('places', dirs.join(',')); }, /** * Return node for given dir object * * @param Object directory object * @return jQuery **/ create = function(dir) { return $(tpl.replace(/\{id\}/, hash2id(dir.hash)) .replace(/\{name\}/, fm.escape(dir.name)) .replace(/\{cssclass\}/, fm.perms2class(dir)) .replace(/\{permissions\}/, !dir.read || !dir.write ? ptpl : '') .replace(/\{symlink\}/, '')); }, /** * Add new node into places * * @param Object directory object * @return void **/ add = function(dir) { if (!fm.files().hasOwnProperty(dir.hash)) { // update cache fm.trigger('tree', {tree: [dir]}); } var node = create(dir); if (subtree.children().length) { $.each(subtree.children(), function() { var current = $(this); if (dir.name.localeCompare(current.children('.'+navdir).text()) < 0) { return !node.insertBefore(current); } }); } dirs.push(dir.hash); !node.parent().length && subtree.append(node); root.addClass(collapsed); node.draggable({ appendTo : 'body', revert : false, helper : function() { var dir = $(this); dir.children().removeClass('ui-state-hover'); return $('<div class="elfinder-place-drag elfinder-'+fm.direction+'"/>') .append(dir.clone()) .data('hash', id2hash(dir.children(':first').attr('id'))); }, start : function() { $(this).hide(); }, stop : function(e, ui) { var top = places.offset().top, left = places.offset().left, width = places.width(), height = places.height(), x = e.clientX, y = e.clientY; if (x > left && x < left+width && y > top && y < y+height) { $(this).show(); } else { remove(ui.helper.data('hash')); save(); } } }); }, /** * Remove dir from places * * @param String directory id * @return void **/ remove = function(hash) { var ndx = $.inArray(hash, dirs); if (ndx !== -1) { dirs.splice(ndx, 1); subtree.find('#'+hash2id(hash)).parent().remove(); !subtree.children().length && root.removeClass(collapsed+' '+expanded); } }, /** * Remove all dir from places * * @return void **/ clear = function() { subtree.empty(); root.removeClass(collapsed+' '+expanded); }, /** * Node - wrapper for places root * * @type jQuery **/ wrapper = create({ hash : 'root-'+fm.namespace, name : fm.i18n(opts.name, 'places'), read : true, write : true }), /** * Places root node * * @type jQuery **/ root = wrapper.children('.'+navdir) .addClass(clroot) .click(function() { if (root.is('.'+collapsed)) { places.toggleClass(expanded); subtree.slideToggle(); fm.storage('placesState', places.is('.'+expanded)? 1 : 0); } }), /** * Container for dirs * * @type jQuery **/ subtree = wrapper.children('.'+fm.res(c, 'navsubtree')), /** * Main places container * * @type jQuery **/ places = $(this).addClass(fm.res(c, 'tree')+' elfinder-places ui-corner-all') .hide() .append(wrapper) .appendTo(fm.getUI('navbar')) .delegate('.'+navdir, 'hover', function() { $(this).toggleClass('ui-state-hover'); }) .delegate('.'+navdir, 'click', function(e) { fm.exec('open', $(this).attr('id').substr(6)); }) .delegate('.'+navdir+':not(.'+clroot+')', 'contextmenu', function(e) { var hash = $(this).attr('id').substr(6); e.preventDefault(); fm.trigger('contextmenu', { raw : [{ label : fm.i18n('rmFromPlaces'), icon : 'rm', callback : function() { remove(hash); save(); } }], 'x' : e.clientX, 'y' : e.clientY }) }) .droppable({ tolerance : 'pointer', accept : '.elfinder-cwd-file-wrapper,.elfinder-tree-dir,.elfinder-cwd-file', hoverClass : fm.res('class', 'adroppable'), drop : function(e, ui) { var resolve = true; $.each(ui.helper.data('files'), function(i, hash) { var dir = fm.file(hash); if (dir && dir.mime == 'directory' && $.inArray(dir.hash, dirs) === -1) { add(dir); } else { resolve = false; } }) save(); resolve && ui.helper.hide(); } }); // on fm load - show places and load files from backend fm.one('load', function() { if (fm.oldAPI) { return; } places.show().parent().show(); dirs = $.map((fm.storage('places') || '').split(','), function(hash) { return hash || null;}); if (dirs.length) { root.prepend(spinner); fm.request({ data : {cmd : 'info', targets : dirs}, preventDefault : true }) .done(function(data) { dirs = []; $.each(data.files, function(i, file) { file.mime == 'directory' && add(file); }); save(); if (fm.storage('placesState') > 0) { root.click(); } }) .always(function() { spinner.remove(); }) } fm.remove(function(e) { $.each(e.data.removed, function(i, hash) { remove(hash); }); save(); }) .change(function(e) { $.each(e.data.changed, function(i, file) { if ($.inArray(file.hash, dirs) !== -1) { remove(file.hash); file.mime == 'directory' && add(file); } }); save(); }) .bind('sync', function() { if (dirs.length) { root.prepend(spinner); fm.request({ data : {cmd : 'info', targets : dirs}, preventDefault : true }) .done(function(data) { $.each(data.files || [], function(i, file) { if ($.inArray(file.hash, dirs) === -1) { remove(file.hash); } }); save(); }) .always(function() { spinner.remove(); }); } }) }) }); } /* * File: /js/ui/searchbutton.js */ /** * @class elFinder toolbar search button widget. * * @author Dmitry (dio) Levashov **/ $.fn.elfindersearchbutton = function(cmd) { return this.each(function() { var result = false, button = $(this).hide().addClass('ui-widget-content elfinder-button '+cmd.fm.res('class', 'searchbtn')+''), search = function() { var val = $.trim(input.val()); if (val) { cmd.exec(val).done(function() { result = true; input.focus(); }); } else { cmd.fm.trigger('searchend'); } }, abort = function() { input.val(''); if (result) { result = false; cmd.fm.trigger('searchend'); } }, input = $('<input type="text" size="42"/>') .appendTo(button) // to avoid fm shortcuts on arrows .keypress(function(e) { e.stopPropagation(); }) .keydown(function(e) { e.stopPropagation(); e.keyCode == 13 && search(); if (e.keyCode== 27) { e.preventDefault(); abort(); } }); $('<span class="ui-icon ui-icon-search" title="'+cmd.title+'"/>') .appendTo(button) .click(search); $('<span class="ui-icon ui-icon-close"/>') .appendTo(button) .click(abort); // wait when button will be added to DOM setTimeout(function() { button.parent().detach(); cmd.fm.getUI('toolbar').prepend(button.show()); // position icons for ie7 if (cmd.fm.UA.ltIE7) { var icon = button.children(cmd.fm.direction == 'ltr' ? '.ui-icon-close' : '.ui-icon-search'); icon.css({ right : '', left : parseInt(button.width())-icon.outerWidth(true) }); } }, 200); cmd.fm .select(function() { input.blur(); }) .bind('searchend', function() { input.val(''); }) .shortcut({ pattern : 'ctrl+f f3', description : cmd.title, callback : function() { input.select().focus(); } }); }); }; /* * File: /js/ui/sortbutton.js */ /** * @class elFinder toolbar button menu with sort variants. * * @author Dmitry (dio) Levashov **/ $.fn.elfindersortbutton = function(cmd) { return this.each(function() { var fm = cmd.fm, name = cmd.name, c = 'class', disabled = fm.res(c, 'disabled'), hover = fm.res(c, 'hover'), item = 'elfinder-button-menu-item', selected = item+'-selected', asc = selected+'-asc', desc = selected+'-desc', button = $(this).addClass('ui-state-default elfinder-button elfinder-menubutton elfiner-button-'+name) .attr('title', cmd.title) .append('<span class="elfinder-button-icon elfinder-button-icon-'+name+'"/>') .hover(function(e) { !button.is('.'+disabled) && button.toggleClass(hover); }) .click(function(e) { if (!button.is('.'+disabled)) { e.stopPropagation(); menu.is(':hidden') && cmd.fm.getUI().click(); menu.slideToggle(100); } }), menu = $('<div class="ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>') .hide() .appendTo(button) .zIndex(12+button.zIndex()) .delegate('.'+item, 'hover', function() { $(this).toggleClass(hover) }) .delegate('.'+item, 'click', function(e) { e.preventDefault(); e.stopPropagation(); hide(); }), update = function() { menu.children(':not(:last)').removeClass(selected+' '+asc+' '+desc) .filter('[rel="'+fm.sortType+'"]') .addClass(selected+' '+(fm.sortOrder == 'asc' ? asc : desc)); menu.children(':last').toggleClass(selected, fm.sortStickFolders); }, hide = function() { menu.hide(); }; $.each(fm.sortRules, function(name, value) { menu.append($('<div class="'+item+'" rel="'+name+'"><span class="ui-icon ui-icon-arrowthick-1-n"/><span class="ui-icon ui-icon-arrowthick-1-s"/>'+fm.i18n('sort'+name)+'</div>').data('type', name)); }); menu.children().click(function(e) { var type = $(this).attr('rel'); cmd.exec([], { type : type, order : type == fm.sortType ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder, stick : fm.sortStickFolders }); }) $('<div class="'+item+' '+item+'-separated"><span class="ui-icon ui-icon-check"/>'+fm.i18n('sortFoldersFirst')+'</div>') .appendTo(menu) .click(function() { cmd.exec([], {type : fm.sortType, order : fm.sortOrder, stick : !fm.sortStickFolders}); }); fm.bind('disable select', hide).getUI().click(hide); fm.bind('sortchange', update) if (menu.children().length > 1) { cmd.change(function() { button.toggleClass(disabled, cmd.disabled()); update(); }) .change(); } else { button.addClass(disabled); } }); } /* * File: /js/ui/stat.js */ /** * @class elFinder ui * Display number of files/selected files and its size in statusbar * * @author Dmitry (dio) Levashov **/ $.fn.elfinderstat = function(fm) { return this.each(function() { var size = $(this).addClass('elfinder-stat-size'), sel = $('<div class="elfinder-stat-selected"/>'), titlesize = fm.i18n('size').toLowerCase(), titleitems = fm.i18n('items').toLowerCase(), titlesel = fm.i18n('selected'), setstat = function(files, cwd) { var c = 0, s = 0; $.each(files, function(i, file) { if (!cwd || file.phash == cwd) { c++; s += parseInt(file.size)||0; } }) size.html(titleitems+': '+c+', '+titlesize+': '+fm.formatSize(s)); }; fm.getUI('statusbar').prepend(size).append(sel).show(); fm .bind('open reload add remove change searchend', function() { setstat(fm.files(), fm.cwd().hash) }) .search(function(e) { setstat(e.data.files); }) .select(function() { var s = 0, c = 0, files = fm.selectedFiles(); if (files.length == 1) { s = files[0].size; sel.html(fm.escape(files[0].name)+(s > 0 ? ', '+fm.formatSize(s) : '')); return; } $.each(files, function(i, file) { c++; s += parseInt(file.size)||0; }); sel.html(c ? titlesel+': '+c+', '+titlesize+': '+fm.formatSize(s) : ' '); }) ; }) } /* * File: /js/ui/toolbar.js */ /** * @class elFinder toolbar * * @author Dmitry (dio) Levashov **/ $.fn.elfindertoolbar = function(fm, opts) { this.not('.elfinder-toolbar').each(function() { var commands = fm._commands, self = $(this).addClass('ui-helper-clearfix ui-widget-header ui-corner-top elfinder-toolbar'), panels = opts || [], l = panels.length, i, cmd, panel, button; self.prev().length && self.parent().prepend(this); while (l--) { if (panels[l]) { panel = $('<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>'); i = panels[l].length; while (i--) { if ((cmd = commands[panels[l][i]])) { button = 'elfinder'+cmd.options.ui; $.fn[button] && panel.prepend($('<div/>')[button](cmd)); } } panel.children().length && self.prepend(panel); panel.children(':gt(0)').before('<span class="ui-widget-content elfinder-toolbar-button-separator"/>'); } } self.children().length && self.show(); }); return this; } /* * File: /js/ui/tree.js */ /** * @class elFinder folders tree * * @author Dmitry (dio) Levashov **/ $.fn.elfindertree = function(fm, opts) { var treeclass = fm.res('class', 'tree'); this.not('.'+treeclass).each(function() { var c = 'class', /** * Root directory class name * * @type String */ root = fm.res(c, 'treeroot'), /** * Open root dir if not opened yet * * @type Boolean */ openRoot = opts.openRootOnLoad, /** * Subtree class name * * @type String */ subtree = fm.res(c, 'navsubtree'), /** * Directory class name * * @type String */ navdir = fm.res(c, 'treedir'), /** * Collapsed arrow class name * * @type String */ collapsed = fm.res(c, 'navcollapse'), /** * Expanded arrow class name * * @type String */ expanded = fm.res(c, 'navexpand'), /** * Class name to mark arrow for directory with already loaded children * * @type String */ loaded = 'elfinder-subtree-loaded', /** * Arraw class name * * @type String */ arrow = fm.res(c, 'navarrow'), /** * Current directory class name * * @type String */ active = fm.res(c, 'active'), /** * Droppable dirs dropover class * * @type String */ dropover = fm.res(c, 'adroppable'), /** * Hover class name * * @type String */ hover = fm.res(c, 'hover'), /** * Disabled dir class name * * @type String */ disabled = fm.res(c, 'disabled'), /** * Draggable dir class name * * @type String */ draggable = fm.res(c, 'draggable'), /** * Droppable dir class name * * @type String */ droppable = fm.res(c, 'droppable'), insideNavbar = function(x) { var left = navbar.offset().left; return left <= x && x <= left + navbar.width(); }, drop = fm.droppable.drop, /** * Droppable options * * @type Object */ droppableopts = $.extend(true, {}, fm.droppable, { // show subfolders on dropover over : function(e) { var link = $(this), cl = hover+' '+dropover; if (insideNavbar(e.clientX)) { link.addClass(cl) if (link.is('.'+collapsed+':not(.'+expanded+')')) { setTimeout(function() { link.is('.'+dropover) && link.children('.'+arrow).click(); }, 500); } } else { link.removeClass(cl); } }, out : function() { $(this).removeClass(hover+' '+dropover); }, drop : function(e, ui) { insideNavbar(e.clientX) && drop.call(this, e, ui); } }), spinner = $(fm.res('tpl', 'navspinner')), /** * Directory html template * * @type String */ tpl = fm.res('tpl', 'navdir'), /** * Permissions marker html template * * @type String */ ptpl = fm.res('tpl', 'perms'), /** * Symlink marker html template * * @type String */ stpl = fm.res('tpl', 'symlink'), /** * Html template replacement methods * * @type Object */ replace = { id : function(dir) { return fm.navHash2Id(dir.hash) }, cssclass : function(dir) { return (dir.phash ? '' : root)+' '+navdir+' '+fm.perms2class(dir)+' '+(dir.dirs && !dir.link ? collapsed : ''); }, permissions : function(dir) { return !dir.read || !dir.write ? ptpl : ''; }, symlink : function(dir) { return dir.alias ? stpl : ''; } }, /** * Return html for given dir * * @param Object directory * @return String */ itemhtml = function(dir) { dir.name = fm.escape(dir.i18 || dir.name); return tpl.replace(/(?:\{([a-z]+)\})/ig, function(m, key) { return dir[key] || (replace[key] ? replace[key](dir) : ''); }); }, /** * Return only dirs from files list * * @param Array files list * @return Array */ filter = function(files) { return $.map(files||[], function(f) { return f.mime == 'directory' ? f : null }); }, /** * Find parent subtree for required directory * * @param String dir hash * @return jQuery */ findSubtree = function(hash) { return hash ? tree.find('#'+fm.navHash2Id(hash)).next('.'+subtree) : tree; }, /** * Find directory (wrapper) in required node * before which we can insert new directory * * @param jQuery parent directory * @param Object new directory * @return jQuery */ findSibling = function(subtree, dir) { var node = subtree.children(':first'), info; while (node.length) { info = fm.file(fm.navId2Hash(node.children('[id]').attr('id'))); if ((info = fm.file(fm.navId2Hash(node.children('[id]').attr('id')))) && dir.name.toLowerCase().localeCompare(info.name.toLowerCase()) < 0) { return node; } node = node.next(); } return $(''); }, /** * Add new dirs in tree * * @param Array dirs list * @return void */ updateTree = function(dirs) { var length = dirs.length, orphans = [], i = dirs.length, dir, html, parent, sibling; var firstVol = true; // check for netmount volume while (i--) { dir = dirs[i]; if (tree.find('#'+fm.navHash2Id(dir.hash)).length) { continue; } if ((parent = findSubtree(dir.phash)).length) { html = itemhtml(dir); if (dir.phash && (sibling = findSibling(parent, dir)).length) { sibling.before(html); } else { parent[firstVol || dir.phash ? 'append' : 'prepend'](html); firstVol = false; } } else { orphans.push(dir); } } if (orphans.length && orphans.length < length) { return updateTree(orphans); } setTimeout(function() { updateDroppable(); }, 10); }, /** * Mark current directory as active * If current directory is not in tree - load it and its parents * * @param {Boolean} do not recursive call * @return void */ sync = function(stopRec) { var cwd = fm.cwd().hash, current = tree.find('#'+fm.navHash2Id(cwd)), rootNode, dir; if (openRoot) { rootNode = tree.find('#'+fm.navHash2Id(fm.root())); rootNode.is('.'+loaded) && rootNode.addClass(expanded).next('.'+subtree).show(); openRoot = false; } if (!current.is('.'+active)) { tree.find('.'+navdir+'.'+active).removeClass(active); current.addClass(active); } if (opts.syncTree) { if (current.length) { return current.parentsUntil('.'+root).filter('.'+subtree).show().prev('.'+navdir).addClass(expanded); } if (fm.newAPI) { dir = fm.file(cwd); if (dir && dir.phash && tree.find('#'+fm.navHash2Id(dir.phash)).length) { updateTree([dir]); return sync(); } fm.request({ data : {cmd : 'parents', target : cwd}, preventFail : true }) .done(function(data) { var dirs = filter(data.tree); updateTree(dirs); updateArrows(dirs, loaded); cwd == fm.cwd().hash && sync(true); }) ; } } }, /** * Make writable and not root dirs droppable * * @return void */ updateDroppable = function() { tree.find('.'+navdir+':not(.'+droppable+',.elfinder-ro,.elfinder-na)').droppable(droppableopts); }, /** * Check required folders for subfolders and update arrow classes * * @param Array folders to check * @param String css class * @return void */ updateArrows = function(dirs, cls) { var sel = cls == loaded ? '.'+collapsed+':not(.'+loaded+')' : ':not(.'+collapsed+')'; //tree.find('.'+subtree+':has(*)').prev(':not(.'+collapsed+')').addClass(collapsed) $.each(dirs, function(i, dir) { tree.find('#'+fm.navHash2Id(dir.phash)+sel) .filter(function() { return $(this).next('.'+subtree).children().length > 0 }) .addClass(cls); }) }, /** * Navigation tree * * @type JQuery */ tree = $(this).addClass(treeclass) // make dirs draggable and toggle hover class .delegate('.'+navdir, 'hover', function(e) { var link = $(this), enter = e.type == 'mouseenter'; if (!link.is('.'+dropover+' ,.'+disabled)) { enter && !link.is('.'+root+',.'+draggable+',.elfinder-na,.elfinder-wo') && link.draggable(fm.draggable); link.toggleClass(hover, enter); } }) // add/remove dropover css class .delegate('.'+navdir, 'dropover dropout drop', function(e) { $(this)[e.type == 'dropover' ? 'addClass' : 'removeClass'](dropover+' '+hover); }) // open dir or open subfolders in tree .delegate('.'+navdir, 'click', function(e) { var link = $(this), hash = fm.navId2Hash(link.attr('id')), file = fm.file(hash); fm.trigger('searchend'); if (hash != fm.cwd().hash && !link.is('.'+disabled)) { fm.exec('open', file.thash || hash); } else if (link.is('.'+collapsed)) { link.children('.'+arrow).click(); } }) // toggle subfolders in tree .delegate('.'+navdir+'.'+collapsed+' .'+arrow, 'click', function(e) { var arrow = $(this), link = arrow.parent('.'+navdir), stree = link.next('.'+subtree); e.stopPropagation(); if (link.is('.'+loaded)) { link.toggleClass(expanded); stree.slideToggle() } else { spinner.insertBefore(arrow); link.removeClass(collapsed); fm.request({cmd : 'tree', target : fm.navId2Hash(link.attr('id'))}) .done(function(data) { updateTree(filter(data.tree)); if (stree.children().length) { link.addClass(collapsed+' '+expanded); stree.slideDown(); } sync(); }) .always(function(data) { spinner.remove(); link.addClass(loaded); }); } }) .delegate('.'+navdir, 'contextmenu', function(e) { e.preventDefault(); fm.trigger('contextmenu', { 'type' : 'navbar', 'targets' : [fm.navId2Hash($(this).attr('id'))], 'x' : e.clientX, 'y' : e.clientY }); }), // move tree into navbar navbar = fm.getUI('navbar').append(tree).show() ; fm.open(function(e) { var data = e.data, dirs = filter(data.files); data.init && tree.empty(); if (dirs.length) { updateTree(dirs); updateArrows(dirs, loaded); } sync(); }) // add new dirs .add(function(e) { var dirs = filter(e.data.added); if (dirs.length) { updateTree(dirs); updateArrows(dirs, collapsed); } }) // update changed dirs .change(function(e) { var dirs = filter(e.data.changed), l = dirs.length, dir, node, tmp, realParent, reqParent, realSibling, reqSibling, isExpanded, isLoaded; while (l--) { dir = dirs[l]; if ((node = tree.find('#'+fm.navHash2Id(dir.hash))).length) { if (dir.phash) { realParent = node.closest('.'+subtree); reqParent = findSubtree(dir.phash); realSibling = node.parent().next(); reqSibling = findSibling(reqParent, dir); if (!reqParent.length) { continue; } if (reqParent[0] !== realParent[0] || realSibling.get(0) !== reqSibling.get(0)) { reqSibling.length ? reqSibling.before(node) : reqParent.append(node); } } isExpanded = node.is('.'+expanded); isLoaded = node.is('.'+loaded); tmp = $(itemhtml(dir)); node.replaceWith(tmp.children('.'+navdir)); if (dir.dirs && (isExpanded || isLoaded) && (node = tree.find('#'+fm.navHash2Id(dir.hash))) && node.next('.'+subtree).children().length) { isExpanded && node.addClass(expanded); isLoaded && node.addClass(loaded); } } } sync(); updateDroppable(); }) // remove dirs .remove(function(e) { var dirs = e.data.removed, l = dirs.length, node, stree; while (l--) { if ((node = tree.find('#'+fm.navHash2Id(dirs[l]))).length) { stree = node.closest('.'+subtree); node.parent().detach(); if (!stree.children().length) { stree.hide().prev('.'+navdir).removeClass(collapsed+' '+expanded+' '+loaded); } } } }) // add/remove active class for current dir .bind('search searchend', function(e) { tree.find('#'+fm.navHash2Id(fm.cwd().hash))[e.type == 'search' ? 'removeClass' : 'addClass'](active); }) // lock/unlock dirs while moving .bind('lockfiles unlockfiles', function(e) { var lock = e.type == 'lockfiles', act = lock ? 'disable' : 'enable', dirs = $.map(e.data.files||[], function(h) { var dir = fm.file(h); return dir && dir.mime == 'directory' ? h : null; }) $.each(dirs, function(i, hash) { var dir = tree.find('#'+fm.navHash2Id(hash)); if (dir.length) { dir.is('.'+draggable) && dir.draggable(act); dir.is('.'+droppable) && dir.droppable(active); dir[lock ? 'addClass' : 'removeClass'](disabled); } }); }) }); return this; } /* * File: /js/ui/uploadButton.js */ /** * @class elFinder toolbar's button tor upload file * * @author Dmitry (dio) Levashov **/ $.fn.elfinderuploadbutton = function(cmd) { return this.each(function() { var button = $(this).elfinderbutton(cmd) .unbind('click'), form = $('<form/>').appendTo(button), input = $('<input type="file" multiple="true"/>') .change(function() { var _input = $(this); if (_input.val()) { cmd.exec({input : _input.remove()[0]}); input.clone(true).appendTo(form); } }); form.append(input.clone(true)); cmd.change(function() { form[cmd.disabled() ? 'hide' : 'show'](); }) .change(); }); } /* * File: /js/ui/viewbutton.js */ /** * @class elFinder toolbar button to switch current directory view. * * @author Dmitry (dio) Levashov **/ $.fn.elfinderviewbutton = function(cmd) { return this.each(function() { var button = $(this).elfinderbutton(cmd), icon = button.children('.elfinder-button-icon'); cmd.change(function() { var icons = cmd.value == 'icons'; icon.toggleClass('elfinder-button-icon-view-list', icons); button.attr('title', cmd.fm.i18n(icons ? 'viewlist' : 'viewicons')); }); }); } /* * File: /js/ui/workzone.js */ /** * @class elfinderworkzone - elFinder container for nav and current directory * @author Dmitry (dio) Levashov **/ $.fn.elfinderworkzone = function(fm) { var cl = 'elfinder-workzone'; this.not('.'+cl).each(function() { var wz = $(this).addClass(cl), wdelta = wz.outerHeight(true) - wz.height(), parent = wz.parent(); parent.add(window).bind('resize', function() { var height = parent.height(); parent.children(':visible:not(.'+cl+')').each(function() { var ch = $(this); if (ch.css('position') != 'absolute' && ch.css('position') != 'fixed') { height -= ch.outerHeight(true); } }); wz.height(height - wdelta); }); }); return this; } /* * File: /js/commands/archive.js */ /** * @class elFinder command "archive" * Archive selected files * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.archive = function() { var self = this, fm = self.fm, mimes = []; this.variants = []; this.disableOnSearch = true; /** * Update mimes on open/reload * * @return void **/ fm.bind('open reload', function() { self.variants = []; $.each((mimes = fm.option('archivers')['create'] || []), function(i, mime) { self.variants.push([mime, fm.mime2kind(mime)]) }); self.change(); }); this.getstate = function() { return !this._disabled && mimes.length && fm.selected().length && fm.cwd().write ? 0 : -1; } this.exec = function(hashes, type) { var files = this.files(hashes), cnt = files.length, mime = type || mimes[0], cwd = fm.cwd(), error = ['errArchive', 'errPerm', 'errCreatingTempDir', 'errFtpDownloadFile', 'errFtpUploadFile', 'errFtpMkdir', 'errArchiveExec', 'errExtractExec', 'errRm'], dfrd = $.Deferred().fail(function(error) { error && fm.error(error); }), i; if (!(this.enabled() && cnt && mimes.length && $.inArray(mime, mimes) !== -1)) { return dfrd.reject(); } if (!cwd.write) { return dfrd.reject(error); } for (i = 0; i < cnt; i++) { if (!files[i].read) { return dfrd.reject(error); } } return fm.request({ data : {cmd : 'archive', targets : this.hashes(hashes), type : mime}, notify : {type : 'archive', cnt : 1}, syncOnFail : true }); } } /* * File: /js/commands/back.js */ /** * @class elFinder command "back" * Open last visited folder * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.back = function() { this.alwaysEnabled = true; this.updateOnSelect = false; this.shortcuts = [{ pattern : 'ctrl+left backspace' }]; this.getstate = function() { return this.fm.history.canBack() ? 0 : -1; } this.exec = function() { return this.fm.history.back(); } } /* * File: /js/commands/copy.js */ /** * @class elFinder command "copy". * Put files in filemanager clipboard. * * @type elFinder.command * @author Dmitry (dio) Levashov */ elFinder.prototype.commands.copy = function() { this.shortcuts = [{ pattern : 'ctrl+c ctrl+insert' }]; this.getstate = function(sel) { var sel = this.files(sel), cnt = sel.length; return cnt && $.map(sel, function(f) { return f.phash && f.read ? f : null }).length == cnt ? 0 : -1; } this.exec = function(hashes) { var fm = this.fm, dfrd = $.Deferred() .fail(function(error) { fm.error(error); }); $.each(this.files(hashes), function(i, file) { if (!(file.read && file.phash)) { return !dfrd.reject(['errCopy', file.name, 'errPerm']); } }); return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes))); } } /* * File: /js/commands/cut.js */ /** * @class elFinder command "copy". * Put files in filemanager clipboard. * * @type elFinder.command * @author Dmitry (dio) Levashov */ elFinder.prototype.commands.cut = function() { this.shortcuts = [{ pattern : 'ctrl+x shift+insert' }]; this.getstate = function(sel) { var sel = this.files(sel), cnt = sel.length; return cnt && $.map(sel, function(f) { return f.phash && f.read && !f.locked ? f : null }).length == cnt ? 0 : -1; } this.exec = function(hashes) { var fm = this.fm, dfrd = $.Deferred() .fail(function(error) { fm.error(error); }); $.each(this.files(hashes), function(i, file) { if (!(file.read && file.phash) ) { return !dfrd.reject(['errCopy', file.name, 'errPerm']); } if (file.locked) { return !dfrd.reject(['errLocked', file.name]); } }); return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes), true)); } } /* * File: /js/commands/download.js */ /** * @class elFinder command "download". * Download selected files. * Only for new api * * @author Dmitry (dio) Levashov, dio@std42.ru **/ elFinder.prototype.commands.download = function() { var self = this, fm = this.fm, filter = function(hashes) { return $.map(self.files(hashes), function(f) { return f.mime == 'directory' ? null : f }); }; this.shortcuts = [{ pattern : 'shift+enter' }]; this.getstate = function() { var sel = this.fm.selected(), cnt = sel.length; return !this._disabled && cnt && (!fm.UA.IE || cnt == 1) && cnt == filter(sel).length ? 0 : -1; } this.exec = function(hashes) { var fm = this.fm, base = fm.options.url, files = filter(hashes), dfrd = $.Deferred(), iframes = '', cdata = '', i, url; if (this.disabled()) { return dfrd.reject(); } if (fm.oldAPI) { fm.error('errCmdNoSupport'); return dfrd.reject(); } cdata = $.param(fm.options.customData || {}); if (cdata) { cdata = '&' + cdata; } base += base.indexOf('?') === -1 ? '?' : '&'; for (i = 0; i < files.length; i++) { iframes += '<iframe class="downloader" id="downloader-' + files[i].hash+'" style="display:none" src="'+base + 'cmd=file&target=' + files[i].hash+'&download=1'+cdata+'"/>'; } $(iframes) .appendTo('body') .ready(function() { setTimeout(function() { $(iframes).each(function() { $('#' + $(this).attr('id')).remove(); }); }, fm.UA.Firefox? (20000 + (10000 * i)) : 1000); // give mozilla 20 sec + 10 sec for each file to be saved }); fm.trigger('download', {files : files}); return dfrd.resolve(hashes); } } /* * 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(sel) { var sel = this.files(sel), cnt = sel.length; return !this._disabled && cnt && fm.cwd().write && $.map(sel, function(f) { return f.phash && f.read ? f : null }).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 || this._disabled) { 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} }); } } /* * 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, mimes = fm.res('mimes', 'text') || [], /** * Return files acceptable to edit * * @param Array files hashes * @return Array **/ filter = function(files) { return $.map(files, function(file) { return (file.mime.indexOf('text/') === 0 || $.inArray(file.mime, mimes) !== -1) && file.mime.indexOf('text/rtf') && (!self.onlyMimes.length || $.inArray(file.mime, self.onlyMimes) !== -1) && file.read && file.write ? file : null; }); }, /** * 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) { var dfrd = $.Deferred(), ta = $('<textarea class="elfinder-file-edit" rows="20" id="'+id+'-ta">'+fm.escape(content)+'</textarea>'), save = function() { ta.editor && ta.editor.save(ta[0], ta.editor.instance); dfrd.resolve(ta.getContent()); ta.elfinderdialog('close'); }, cancel = function() { dfrd.reject(); ta.elfinderdialog('close'); }, opts = { title : file.name, width : self.options.dialogWidth || 450, buttons : {}, close : function() { ta.editor && ta.editor.close(ta[0], ta.editor.instance); $(this).elfinderdialog('destroy'); }, open : function() { fm.disable(); ta.focus(); ta[0].setSelectionRange && ta[0].setSelectionRange(0, 0); ta.editor && ta.editor.load(ta[0]); } }; ta.getContent = function() { return ta.val() } $.each(self.options.editors || [], function(i, editor) { if ($.inArray(file.mime, editor.mimes || []) !== -1 && typeof editor.load == 'function' && typeof editor.save == 'function') { ta.editor = { load : editor.load, save : editor.save, close : typeof editor.close == 'function' ? editor.close : function() {}, instance : null } return false; } }); if (!ta.editor) { ta.keydown(function(e) { var code = e.keyCode, value, start; e.stopPropagation(); if (code == 9) { 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 == 81 || code == 87) { e.preventDefault(); cancel(); } if (code == 83) { e.preventDefault(); save(); } } }) } opts.buttons[fm.i18n('Save')] = save; opts.buttons[fm.i18n('Cancel')] = cancel fm.dialog(ta, opts).attr('id', id); 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) { var hash = file.hash, opts = fm.options, dfrd = $.Deferred(), data = {cmd : 'file', target : hash}, url = fm.url(hash) || fm.options.url, id = 'edit-'+fm.namespace+'-'+file.hash, d = fm.getUI().find('#'+id), error; if (d.length) { d.elfinderdialog('toTop'); return dfrd.resolve(); } if (!file.read || !file.write) { error = ['errOpen', file.name, 'errPerm'] fm.error(error) return dfrd.reject(error); } fm.request({ data : {cmd : 'get', target : hash}, notify : {type : 'openfile', cnt : 1}, syncOnFail : true }) .done(function(data) { dialog(id, file, data.content) .done(function(content) { fm.request({ options : {type : 'post'}, data : { cmd : 'put', target : hash, content : content }, notify : {type : 'save', cnt : 1}, syncOnFail : true }) .fail(function(error) { dfrd.reject(error); }) .done(function(data) { data.changed && data.changed.length && fm.change(data); dfrd.resolve(data); }); }) }) .fail(function(error) { dfrd.reject(error); }) return dfrd.promise(); }; this.shortcuts = [{ pattern : 'ctrl+e' }]; this.init = function() { this.onlyMimes = this.options.mimes || [] } this.getstate = function(sel) { var sel = this.files(sel), cnt = sel.length; return !this._disabled && cnt && filter(sel).length == cnt ? 0 : -1; } this.exec = function(hashes) { var files = filter(this.files(hashes)), list = [], file; if (this.disabled()) { return $.Deferred().reject(); } while ((file = files.shift())) { list.push(edit(file)); } return list.length ? $.when.apply(null, list) : $.Deferred().reject(); } } /* * File: /js/commands/extract.js */ /** * @class elFinder command "extract" * Extract files from archive * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.extract = function() { var self = this, fm = self.fm, mimes = [], filter = function(files) { return $.map(files, function(file) { return file.read && $.inArray(file.mime, mimes) !== -1 ? file : null }) }; this.disableOnSearch = true; // Update mimes list on open/reload fm.bind('open reload', function() { mimes = fm.option('archivers')['extract'] || []; self.change(); }); this.getstate = function(sel) { var sel = this.files(sel), cnt = sel.length; return !this._disabled && cnt && this.fm.cwd().write && filter(sel).length == cnt ? 0 : -1; } this.exec = function(hashes) { var files = this.files(hashes), dfrd = $.Deferred(), cnt = files.length, i, error, decision; var overwriteAll = false; var omitAll = false; var names = $.map(fm.files(hashes), function(file) { return file.name; }); var map = {}; $.map(fm.files(hashes), function(file) { map[file.name] = file; }); 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}, notify:{type:'extract', cnt:1}, syncOnFail:true }) .fail(function (error) { if (dfrd.state() != 'rejected') { dfrd.reject(error); } }) .done(function () { fm.exec("reload"); }); } }; var confirm = function(files, index) { var file = files[index]; var name = file.name.replace(/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/ig, ''); var existed = ($.inArray(name, names) >= 0); if(existed && map[name].mime != 'directory') { fm.confirm( { title : fm.i18n('ntfextract'), text : fm.i18n(['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 = 0; 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 : (cnt > 1) } ); } else { unpack(file); if((index+1) < cnt) { confirm(files, index+1); } else { dfrd.resolve(); } } }; 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(); } } /* * File: /js/commands/getfile.js */ /** * @class elFinder command "getfile". * Return selected files info into outer callback. * For use elFinder with wysiwyg editors etc. * * @author Dmitry (dio) Levashov, dio@std42.ru **/ elFinder.prototype.commands.getfile = function() { var self = this, fm = this.fm, filter = function(files) { var o = self.options; files = $.map(files, function(file) { return file.mime != 'directory' || o.folders ? file : null; }); return o.multiple || files.length == 1 ? files : []; }; this.alwaysEnabled = true; this.callback = fm.options.getFileCallback; this._disabled = typeof(this.callback) == 'function'; this.getstate = function(sel) { var sel = this.files(sel), 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) { fm.trigger('getfile', {files : data}); self.callback(data, fm); if (opts.oncomplete == 'close') { fm.hide(); } else if (opts.oncomplete == 'destroy') { fm.destroy(); } }), 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; if (this.getstate() == -1) { return dfrd.reject(); } for (i = 0; i < cnt; i++) { file = files[i]; if (file.mime == 'directory' && !opts.folders) { return dfrd.reject(); } file.baseUrl = url; file.url = fm.url(file.hash); file.path = fm.path(file.hash); 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 (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)); } } /* * File: /js/commands/help.js */ /** * @class elFinder command "help" * "About" dialog * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.help = function() { var fm = this.fm, self = this, linktpl = '<div class="elfinder-help-link"> <a href="{url}">{link}</a></div>', linktpltgt = '<div class="elfinder-help-link"> <a href="{url}" target="_blank">{link}</a></div>', atpl = '<div class="elfinder-help-team"><div>{author}</div>{work}</div>', url = /\{url\}/, link = /\{link\}/, author = /\{author\}/, work = /\{work\}/, r = 'replace', prim = 'ui-priority-primary', sec = 'ui-priority-secondary', lic = 'elfinder-help-license', tab = '<li class="ui-state-default ui-corner-top"><a href="#{id}">{title}</a></li>', html = ['<div class="ui-tabs ui-widget ui-widget-content ui-corner-all elfinder-help">', '<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all">'], stpl = '<div class="elfinder-help-shortcut"><div class="elfinder-help-shortcut-pattern">{pattern}</div> {descrip}</div>', sep = '<div class="elfinder-help-separator"/>', about = function() { html.push('<div id="about" class="ui-tabs-panel ui-widget-content ui-corner-bottom"><div class="elfinder-help-logo"/>'); html.push('<h3>elFinder</h3>'); html.push('<div class="'+prim+'">'+fm.i18n('webfm')+'</div>'); html.push('<div class="'+sec+'">'+fm.i18n('ver')+': '+fm.version+', '+fm.i18n('protocolver')+': <span id="apiver"></span></div>'); html.push('<div class="'+sec+'">jQuery/jQuery UI: '+$().jquery+'/'+$.ui.version+'</div>'); html.push(sep); html.push(linktpltgt[r](url, 'http://elfinder.org/')[r](link, fm.i18n('homepage'))); html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder/wiki')[r](link, fm.i18n('docs'))); html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder')[r](link, fm.i18n('github'))); html.push(linktpltgt[r](url, 'http://twitter.com/elrte_elfinder')[r](link, fm.i18n('twitter'))); html.push(sep); html.push('<div class="'+prim+'">'+fm.i18n('team')+'</div>'); html.push(atpl[r](author, 'Dmitry "dio" Levashov <dio@std42.ru>')[r](work, fm.i18n('chiefdev'))); html.push(atpl[r](author, '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'))); html.push(atpl[r](author, 'Naoki Sawada <hypweb@gmail.com>')[r](work, fm.i18n('contributor'))); fm.i18[fm.lang].translator && html.push(atpl[r](author, fm.i18[fm.lang].translator)[r](work, fm.i18n('translator')+' ('+fm.i18[fm.lang].language+')')); html.push(sep); html.push('<div class="'+lic+'">'+fm.i18n('icons')+': Pixelmixer, <a href="http://p.yusukekamiyamane.com" target="_blank">Fugue</a></div>'); html.push(sep); html.push('<div class="'+lic+'">Licence: BSD Licence</div>'); html.push('<div class="'+lic+'">Copyright © 2009-2015, Studio 42</div>'); html.push('<div class="'+lic+'">„ …'+fm.i18n('dontforget')+' ”</div>'); html.push('</div>'); }, shortcuts = function() { var sh = fm.shortcuts(); // shortcuts tab html.push('<div id="shortcuts" class="ui-tabs-panel ui-widget-content ui-corner-bottom">'); if (sh.length) { html.push('<div class="ui-widget-content elfinder-help-shortcuts">'); $.each(sh, function(i, s) { html.push(stpl.replace(/\{pattern\}/, s[0]).replace(/\{descrip\}/, s[1])); }); html.push('</div>'); } else { html.push('<div class="elfinder-help-disabled">'+fm.i18n('shortcutsof')+'</div>'); } html.push('</div>'); }, help = function() { // help tab html.push('<div id="help" class="ui-tabs-panel ui-widget-content ui-corner-bottom">'); html.push('<a href="http://elfinder.org/forum/" target="_blank" class="elfinder-dont-panic"><span>DON\'T PANIC</span></a>'); html.push('</div>'); // end help }, content = ''; this.alwaysEnabled = true; this.updateOnSelect = false; this.state = 0; this.shortcuts = [{ pattern : 'f1', description : this.title }]; setTimeout(function() { var parts = self.options.view || ['about', 'shortcuts', 'help']; $.each(parts, function(i, title) { html.push(tab[r](/\{id\}/, title)[r](/\{title\}/, fm.i18n(title))); }); html.push('</ul>'); $.inArray('about', parts) !== -1 && about(); $.inArray('shortcuts', parts) !== -1 && shortcuts(); $.inArray('help', parts) !== -1 && help(); html.push('</div>'); content = $(html.join('')); content.find('.ui-tabs-nav li') .hover(function() { $(this).toggleClass('ui-state-hover'); }) .children() .click(function(e) { var link = $(this); e.preventDefault(); e.stopPropagation(); if (!link.is('.ui-tabs-selected')) { link.parent().addClass('ui-tabs-selected ui-state-active').siblings().removeClass('ui-tabs-selected').removeClass('ui-state-active'); content.find('.ui-tabs-panel').hide().filter(link.attr('href')).show(); } }) .filter(':first').click(); }, 200); this.getstate = function() { return 0; }; this.exec = function() { if (!this.dialog) { content.find('#apiver').text(this.fm.api); this.dialog = this.fm.dialog(content, {title : this.title, width : 530, autoOpen : false, destroyOnClose : false}); } this.dialog.elfinderdialog('open').find('.ui-tabs-nav li a:first').click(); }; }; /* * 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()); } } /* * 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-info-spinner', 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'), items : fm.i18n('items'), yes : fm.i18n('yes'), no : fm.i18n('no'), link : fm.i18n('link') }; this.tpl = { main : '<div class="ui-helper-clearfix elfinder-info-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}</div><table class="elfinder-info-tb">{content}</table>', itemTitle : '<strong>{name}</strong><span class="elfinder-info-kind">{kind}</span>', groupTitle : '<strong>{items}: {num}</strong>', row : '<tr><td>{label} : </td><td>{value}</td></tr>', spinner : '<span>{text}</span> <span class="'+spclass+'"/>' } 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}', opts = { title : this.title, width : 'auto', close : function() { $(this).elfinderdialog('destroy'); } }, count = [], replSpinner = function(msg) { dialog.find('.'+spclass).parent().text(msg); }, id = fm.namespace+'-info-'+$.map(files, function(f) { return f.hash }).join('-'), dialog = fm.getUI().find('#'+id), size, tmb, file, title, dcnt; if (!cnt) { return $.Deferred().reject(); } if (dialog.length) { dialog.elfinderdialog('toTop'); return $.Deferred().resolve(); } if (cnt == 1) { file = files[0]; view = view.replace('{class}', fm.mime2class(file.mime)); title = tpl.itemTitle.replace('{name}', fm.escape(file.i18 || file.name)).replace('{kind}', fm.mime2kind(file)); if (file.tmb) { tmb = fm.option('tmbUrl')+file.tmb; } 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); count.push(file.hash); } content.push(row.replace(l, msg.size).replace(v, size)); file.alias && content.push(row.replace(l, msg.aliasfor).replace(v, file.alias)); content.push(row.replace(l, msg.path).replace(v, fm.escape(fm.path(file.hash, true)))); if (file.read) { var href; if (o.nullUrlDirLinkSelf && file.mime == 'directory' && file.url === null) { var loc = window.location; href = loc.pathname + loc.search + '#elf_' + file.hash; } else { href = fm.url(file.hash); } content.push(row.replace(l, msg.link).replace(v, '<a href="'+href+'" target="_blank">'+file.name+'</a>')); } if (file.dim) { // old api content.push(row.replace(l, msg.dim).replace(v, file.dim)); } else if (file.mime.indexOf('image') !== -1) { if (file.width && file.height) { content.push(row.replace(l, msg.dim).replace(v, file.width+'x'+file.height)); } else { content.push(row.replace(l, msg.dim).replace(v, tpl.spinner.replace('{text}', msg.calc))); fm.request({ data : {cmd : 'dim', target : file.hash}, preventDefault : true }) .fail(function() { replSpinner(msg.unknown); }) .done(function(data) { replSpinner(data.dim || msg.unknown); if (data.dim) { var dim = data.dim.split('x'); var rfile = fm.file(file.hash); rfile.width = dim[0]; rfile.height = dim[1]; } }); } } content.push(row.replace(l, msg.modify).replace(v, fm.formatDate(file))); content.push(row.replace(l, msg.perms).replace(v, fm.formatPermissions(file))); content.push(row.replace(l, msg.locked).replace(v, file.locked ? msg.yes : msg.no)); } else { view = view.replace('{class}', 'elfinder-cwd-icon-group'); title = tpl.groupTitle.replace('{items}', msg.items).replace('{num}', cnt); dcnt = $.map(files, function(f) { return f.mime == 'directory' ? 1 : null }).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)); content.push(row.replace(l, msg.size).replace(v, fm.formatSize(size))); } else { content.push(row.replace(l, msg.kind).replace(v, dcnt == cnt ? msg.folders : msg.folders+' '+dcnt+', '+msg.files+' '+(cnt-dcnt))) content.push(row.replace(l, msg.size).replace(v, tpl.spinner.replace('{text}', msg.calc))); count = $.map(files, function(f) { return f.hash }); } } view = view.replace('{title}', title).replace('{content}', content.join('')); dialog = fm.dialog(view, opts); dialog.attr('id', id) // load thumbnail if (tmb) { $('<img/>') .load(function() { dialog.find('.elfinder-cwd-icon').css('background', 'url("'+tmb+'") center center no-repeat'); }) .attr('src', tmb); } // send request to count total size if (count.length) { fm.request({ data : {cmd : 'size', targets : count}, preventDefault : true }) .fail(function() { replSpinner(msg.unknown); }) .done(function(data) { var size = parseInt(data.size); replSpinner(size >= 0 ? fm.formatSize(size) : msg.unknown); }); } } } /* * File: /js/commands/mkdir.js */ /** * @class elFinder command "mkdir" * Create new folder * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.mkdir = function() { this.disableOnSearch = true; this.updateOnSelect = false; this.mime = 'directory'; this.prefix = 'untitled folder'; this.exec = $.proxy(this.fm.res('mixin', 'make'), this); this.shortcuts = [{ pattern : 'ctrl+shift+n' }]; this.getstate = function() { return !this._disabled && this.fm.cwd().write ? 0 : -1; } } /* * File: /js/commands/mkfile.js */ /** * @class elFinder command "mkfile" * Create new empty file * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.mkfile = function() { this.disableOnSearch = true; this.updateOnSelect = false; this.mime = 'text/plain'; this.prefix = 'untitled file.txt'; this.exec = $.proxy(this.fm.res('mixin', 'make'), this); this.getstate = function() { return !this._disabled && this.fm.cwd().write ? 0 : -1; } } /* * 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; this.alwaysEnabled = true; this.updateOnSelect = false; this.drivers = []; this.handlers = { load : function() { this.drivers = this.fm.netDrivers; } } this.getstate = function() { return this.drivers.length ? 0 : -1; } this.exec = function() { var fm = self.fm, dfrd = $.Deferred(), create = function() { var inputs = { protocol : $('<select/>'), host : $('<input type="text"/>'), port : $('<input type="text"/>'), path : $('<input type="text" value="/"/>'), user : $('<input type="text"/>'), pass : $('<input type="password"/>') }, opts = { title : fm.i18n('netMountDialogTitle'), resizable : false, modal : true, destroyOnClose : true, close : function() { delete self.dialog; dfrd.state() == 'pending' && dfrd.reject(); }, buttons : {} }, content = $('<table class="elfinder-info-tb elfinder-netmount-tb"/>'); $.each(self.drivers, function(i, protocol) { inputs.protocol.append('<option value="'+protocol+'">'+fm.i18n(protocol)+'</option>'); }); $.each(inputs, function(name, input) { name != 'protocol' && input.addClass('ui-corner-all'); content.append($('<tr/>').append($('<td>'+fm.i18n(name)+'</td>')).append($('<td/>').append(input))); }); opts.buttons[fm.i18n('btnMount')] = function() { var data = {cmd : 'netmount'}; $.each(inputs, function(name, input) { var val = $.trim(input.val()); if (val) { data[name] = val; } }); if (!data.host) { return self.fm.trigger('error', {error : 'errNetMountHostReq'}); } self.fm.request({data : data, notify : {type : 'netmount', cnt : 1}}) .done(function() { dfrd.resolve(); }) .fail(function(error) { dfrd.reject(error); }); self.dialog.elfinderdialog('close'); } opts.buttons[fm.i18n('btnCancel')] = function() { self.dialog.elfinderdialog('close'); } return fm.dialog(content, opts); } ; if (!self.dialog) { self.dialog = create() } return dfrd.promise(); } } /* * 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() { this.alwaysEnabled = true; this._handlers = { dblclick : function(e) { e.preventDefault(); this.exec() }, 'select enable disable reload' : function(e) { this.update(e.type == 'disable' ? -1 : void(0)); } } this.shortcuts = [{ pattern : 'ctrl+down numpad_enter'+(this.fm.OS != 'mac' && ' enter') }]; this.getstate = function(sel) { var sel = this.files(sel), cnt = sel.length; return cnt == 1 ? 0 : cnt ? ($.map(sel, function(file) { return file.mime == 'directory' ? null : file}).length == cnt ? 0 : -1) : -1 } this.exec = function(hashes) { var fm = this.fm, dfrd = $.Deferred().fail(function(error) { error && fm.error(error); }), files = this.files(hashes), cnt = files.length, file, url, s, w; if (!cnt) { return dfrd.reject(); } // open folder if (cnt == 1 && (file = files[0]) && file.mime == 'directory') { return file && !file.read ? dfrd.reject(['errOpen', file.name, 'errPerm']) : fm.request({ data : {cmd : 'open', target : file.thash || file.hash}, notify : {type : 'open', cnt : 1, hideCnt : true}, syncOnFail : true }); } files = $.map(files, function(file) { return file.mime != 'directory' ? file : null }); // nothing to open or files and folders selected - do nothing if (cnt != files.length) { return dfrd.reject(); } // open files cnt = files.length; while (cnt--) { file = files[cnt]; if (!file.read) { return dfrd.reject(['errOpen', file.name, 'errPerm']); } if (!(url = fm.url(/*file.thash || */file.hash))) { url = fm.options.url; url = url + (url.indexOf('?') === -1 ? '?' : '&') + (fm.oldAPI ? 'cmd=open¤t='+file.phash : 'cmd=file') + '&target=' + file.hash; } // set window size for image if set if (file.dim) { s = file.dim.split('x'); w = 'width='+(parseInt(s[0])+20) + ',height='+(parseInt(s[1])+20); } else { w = 'width='+parseInt(2*$(window).width()/3)+',height='+parseInt(2*$(window).height()/3); } var wnd = window.open('', 'new_window', w + ',top=50,left=50,scrollbars=yes,resizable=yes'); if (!wnd) { return dfrd.reject('errPopup'); } var form = document.createElement("form"); form.action = fm.options.url; form.method = 'POST'; form.target = 'new_window'; form.style.display = 'none'; var params = $.extend({}, fm.options.customData, { cmd: 'file', target: file.hash }); $.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(); } return dfrd.resolve(hashes); } } /* * 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 ($.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(dst) { var self = this, fm = self.fm, dst = dst ? this.files(dst)[0] : fm.cwd(), files = fm.clipboard(), cnt = files.length, cut = cnt ? files[0].cut : false, error = cut ? 'errMove' : 'errCopy', fpaste = [], fcopy = [], dfrd = $.Deferred() .fail(function(error) { error && fm.error(error); }), copy = function(files) { return files.length && fm._commands.duplicate ? fm.exec('duplicate', files) : $.Deferred().resolve(); }, paste = function(files) { var dfrd = $.Deferred(), existed = [], 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(cut ? 'moveFiles' : 'copyFiles'), text : fm.i18n(['errExists', file.name, '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(); } } }) }, valid = function(names) { existed = intersect(files, names); existed.length ? confirm(0) : paste(files); }, paste = function(files) { var files = $.map(files, function(file) { return !file.remove ? file : null } ), cnt = files.length, groups = {}, args = [], src; if (!cnt) { return dfrd.resolve(); } src = files[0].phash; files = $.map(files, function(f) { return f.hash}); fm.request({ data : {cmd : 'paste', dst : dst.hash, targets : files, cut : cut ? 1 : 0, src : src}, notify : {type : cut ? 'move' : 'copy', cnt : cnt} }) .always(function() { dfrd.resolve(); fm.unlockfiles({files : files}); }); } ; if (self._disabled || !files.length) { return dfrd.resolve(); } if (fm.oldAPI) { paste(files); } else { if (!fm.option('copyOverwrite')) { paste(files); } else { dst.hash == fm.cwd().hash ? valid($.map(fm.files(), function(file) { return file.phash == dst.hash ? file.name : null })) : fm.request({ data : {cmd : 'ls', target : dst.hash}, notify : {type : 'prepare', cnt : 1, hideCnt : true}, preventFail : true }) .always(function(data) { valid(data.list || []) }); } } return dfrd; }, parents, fparents; if (!cnt || !dst || dst.mime != 'directory') { return dfrd.reject(); } if (!dst.write) { return dfrd.reject([error, files[0].name, 'errPerm']); } parents = fm.parents(dst.hash); $.each(files, function(i, file) { if (!file.read) { return !dfrd.reject([error, files[0].name, 'errPerm']); } if (cut && file.locked) { return !dfrd.reject(['errLocked', file.name]); } if ($.inArray(file.hash, parents) !== -1) { return !dfrd.reject(['errCopyInItself', file.name]); } fparents = fm.parents(file.hash); fparents.pop(); if ($.inArray(dst.hash, fparents) !== -1) { if ($.map(fparents, function(h) { var d = fm.file(h); return d.phash == dst.hash && d.name == file.name ? d : null }).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; } return $.when( copy(fcopy), paste(fpaste) ).always(function() { cut && fm.clipboard([]); }); } } /* * 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 state * * @type Number **/ state = closed, /** * next/prev event name (requied to cwd catch it) * * @type Number **/ // keydown = fm.UA.Firefox || fm.UA.Opera ? 'keypress' : 'keydown', /** * navbar icon class * * @type Number **/ navicon = 'elfinder-quicklook-navbar-icon', /** * navbar "fullscreen" icon class * * @type Number **/ fullscreen = 'elfinder-quicklook-fullscreen', /** * 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) { return { opacity : 0, width : 20,//node.width(), height : fm.view == 'list' ? 1 : 20, top : node.offset().top+'px', left : node.offset().left+'px' } }, /** * Return css for opened window * * @return void **/ openedCss = function() { var win = $(window); return { opacity : 1, width : width, height : height, top : parseInt((win.height() - height)/2 + win.scrollTop()), left : parseInt((win.width() - width)/2 + win.scrollLeft()) } }, support = function(codec) { var media = document.createElement(codec.substr(0, codec.indexOf('/'))), value = false; try { value = media.canPlayType && media.canPlayType(codec); } catch (e) { } return value && value !== '' && value != 'no'; }, /** * Opened window width (from config) * * @type Number **/ width, /** * Opened window height (from config) * * @type Number **/ height, /** * elFinder node * * @type jQuery **/ parent, /** * elFinder current directory node * * @type jQuery **/ cwd, title = $('<div class="elfinder-quicklook-title"/>'), icon = $('<div/>'), info = $('<div class="elfinder-quicklook-info"/>'),//.hide(), fsicon = $('<div class="'+navicon+' '+navicon+'-fullscreen"/>') .mousedown(function(e) { var win = self.window, full = win.is('.'+fullscreen), scroll = 'scroll.'+fm.namespace, $window = $(window); e.stopPropagation(); if (full) { win.css(win.data('position')).unbind('mousemove'); $window.unbind(scroll).trigger(self.resize).unbind(self.resize); navbar.unbind('mouseenter').unbind('mousemove'); } else { win.data('position', { left : win.css('left'), top : win.css('top'), width : win.width(), height : win.height() }) .css({ width : '100%', height : '100%' }); $(window).bind(scroll, function() { win.css({ left : parseInt($(window).scrollLeft())+'px', top : parseInt($(window).scrollTop()) +'px' }) }) .bind(self.resize, function(e) { self.preview.trigger('changesize'); }) .trigger(scroll) .trigger(self.resize); win.bind('mousemove', function(e) { navbar.stop(true, true).show().delay(3000).fadeOut('slow'); }) .mousemove(); navbar.mouseenter(function() { navbar.stop(true, true).show(); }) .mousemove(function(e) { e.stopPropagation(); }); } navbar.attr('style', '').draggable(full ? 'destroy' : {}); win.toggleClass(fullscreen); $(this).toggleClass(navicon+'-fullscreen-off'); $.fn.resizable && parent.add(win).resizable(full ? 'enable' : 'disable').removeClass('ui-state-disabled'); }), navbar = $('<div class="elfinder-quicklook-navbar"/>') .append($('<div class="'+navicon+' '+navicon+'-prev"/>').mousedown(function() { navtrigger(37); })) .append(fsicon) .append($('<div class="'+navicon+' '+navicon+'-next"/>').mousedown(function() { navtrigger(39); })) .append('<div class="elfinder-quicklook-navbar-separator"/>') .append($('<div class="'+navicon+' '+navicon+'-close"/>').mousedown(function() { self.window.trigger('close'); })) ; this.resize = 'resize.'+fm.namespace; this.info = $('<div class="elfinder-quicklook-info-wrapper"/>') .append(icon) .append(info); this.preview = $('<div class="elfinder-quicklook-preview ui-helper-clearfix"/>') // clean info/icon .bind('change', function(e) { self.info.attr('style', '').hide(); icon.removeAttr('class').attr('style', ''); info.html(''); }) // update info/icon .bind('update', function(e) { var fm = self.fm, preview = self.preview, file = e.file, tpl = '<div class="elfinder-quicklook-info-data">{value}</div>', tmb; if (file) { !file.read && e.stopImmediatePropagation(); self.window.data('hash', file.hash); self.preview.unbind('changesize').trigger('change').children().remove(); title.html(fm.escape(file.name)); info.html( tpl.replace(/\{value\}/, file.name) + tpl.replace(/\{value\}/, fm.mime2kind(file)) + (file.mime == 'directory' ? '' : tpl.replace(/\{value\}/, fm.formatSize(file.size))) + tpl.replace(/\{value\}/, fm.i18n('modify')+': '+ fm.formatDate(file)) ) icon.addClass('elfinder-cwd-icon ui-corner-all '+fm.mime2class(file.mime)); if (file.tmb) { $('<img/>') .hide() .appendTo(self.preview) .load(function() { icon.css('background', 'url("'+tmb+'") center center no-repeat'); $(this).remove(); }) .attr('src', (tmb = fm.tmb(file.hash))); } self.info.delay(100).fadeIn(10); } else { e.stopImmediatePropagation(); } }); this.window = $('<div class="ui-helper-reset ui-widget elfinder-quicklook" style="position:absolute"/>') .click(function(e) { e.stopPropagation(); }) .append( $('<div class="elfinder-quicklook-titlebar"/>') .mouseenter(function () { $(this).css({opacity: 1.0}); // animate seems to mess this up }) .mouseleave(function () { $(this).css({opacity: 0.0}) }) .append(title) .append($('<span class="ui-icon ui-icon-circle-close"/>').mousedown(function(e) { e.stopPropagation(); self.window.trigger('close'); })) ) .append(this.preview.add(navbar)) .append(self.info.hide()) .draggable({handle : 'div.elfinder-quicklook-titlebar'}) .bind('open', function(e) { var win = self.window, file = self.value, node; if (self.closed() && file && (node = cwd.find('#'+file.hash)).length) { navbar.attr('style', ''); state = animated; node.trigger('scrolltoview'); win.css(closedCss(node)) .show() .animate(openedCss(), 550, function() { state = opened; self.update(1, self.value); }); } }) .bind('close', function(e) { var win = self.window, preview = self.preview.trigger('change'), file = self.value, node = cwd.find('#'+win.data('hash')), close = function() { state = closed; win.hide(); preview.children().remove(); self.update(0, self.value); }; if (self.opened()) { state = animated; win.is('.'+fullscreen) && fsicon.mousedown() node.length ? win.animate(closedCss(node), 500, close) : close(); } }); /** * 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() { this.update(void(0), this.fm.selectedFiles()[0]); }, error : function() { self.window.is(':visible') && self.window.data('hash', '').trigger('close'); }, 'searchshow searchhide' : function() { this.opened() && this.window.trigger('close'); } } this.shortcuts = [{ pattern : 'space' }]; this.support = { audio : { ogg : support('audio/ogg; codecs="vorbis"'), mp3 : support('audio/mpeg;'), wav : support('audio/wav; codecs="1"'), m4a : support('audio/x-m4a;') || support('audio/aac;') }, video : { ogg : support('video/ogg; codecs="theora"'), webm : support('video/webm; codecs="vp8, vorbis"'), mp4 : support('video/mp4; codecs="avc1.42E01E"') || support('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') } } /** * Return true if quickLoock window is visible and not animated * * @return Boolean **/ this.closed = function() { return state == closed; } /** * Return true if quickLoock window is hidden * * @return Boolean **/ this.opened = function() { return state == opened; } /** * 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; width = o.width > 0 ? parseInt(o.width) : 450; height = o.height > 0 ? parseInt(o.height) : 300; fm.one('load', function() { parent = fm.getUI(); cwd = fm.getUI('cwd'); win.appendTo('body').zIndex(100 + parent.zIndex()); // close window on escape $(document).keydown(function(e) { e.keyCode == 27 && self.opened() && win.trigger('close') }) if ($.fn.resizable) { 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()) { self.value ? preview.trigger($.Event('update', {file : self.value})) : win.trigger('close'); } }); $.each(fm.commands.quicklook.plugins || [], function(i, plugin) { if (typeof(plugin) == 'function') { new plugin(self) } }); preview.bind('update', function() { self.info.show(); }); }); } this.getstate = function() { return this.fm.selected().length == 1 ? state == opened ? 1 : 0 : -1; } this.exec = function() { this.enabled() && this.window.trigger(this.opened() ? 'close' : 'open'); } this.hideinfo = function() { this.info.stop(true).hide(); } } /* * 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'], preview = ql.preview; // 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.bind('update', function(e) { var file = e.file, img; if ($.inArray(file.mime, mimes) !== -1) { // this is our file - stop event propagation e.stopImmediatePropagation(); img = $('<img/>') .hide() .appendTo(preview) .load(function() { // timeout - because of strange safari bug - // sometimes cant get image height 0_o setTimeout(function() { var prop = (img.width()/img.height()).toFixed(2); preview.bind('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'); // hide info/icon ql.hideinfo(); //show image img.fadeIn(100); }, 1) }) .attr('src', ql.fm.url(file.hash)); } }); }, /** * HTML preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var mimes = ['text/html', 'application/xhtml+xml'], preview = ql.preview, fm = ql.fm; preview.bind('update', function(e) { var file = e.file, jqxhr; if ($.inArray(file.mime, mimes) !== -1) { e.stopImmediatePropagation(); // stop loading on change file if not loaded yet preview.one('change', function() { jqxhr.state() == 'pending' && jqxhr.reject(); }); jqxhr = fm.request({ data : {cmd : 'get', target : file.hash, current : file.phash}, preventDefault : true }) .done(function(data) { ql.hideinfo(); doc = $('<iframe class="elfinder-quicklook-preview-html"/>').appendTo(preview)[0].contentWindow.document; doc.open(); doc.write(data.content); doc.close(); }); } }) }, /** * Texts preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, mimes = fm.res('mimes', 'text'), preview = ql.preview; preview.bind('update', function(e) { var file = e.file, mime = file.mime, jqxhr; if (mime.indexOf('text/') === 0 || $.inArray(mime, mimes) !== -1) { e.stopImmediatePropagation(); // stop loading on change file if not loadin yet preview.one('change', function() { jqxhr.state() == 'pending' && jqxhr.reject(); }); jqxhr = fm.request({ data : {cmd : 'get', target : file.hash }, preventDefault : true }) .done(function(data) { ql.hideinfo(); $('<div class="elfinder-quicklook-preview-text-wrapper"><pre class="elfinder-quicklook-preview-text">'+fm.escape(data.content)+'</pre></div>').appendTo(preview); }); } }); }, /** * PDF preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var fm = ql.fm, mime = 'application/pdf', preview = ql.preview, active = false; if ((fm.UA.Safari && fm.OS == 'mac') || fm.UA.IE) { active = true; } else { $.each(navigator.plugins, function(i, plugins) { $.each(plugins, function(i, plugin) { if (plugin.type == mime) { return !(active = true); } }); }); } active && preview.bind('update', function(e) { var file = e.file, node; if (file.mime == mime) { e.stopImmediatePropagation(); preview.one('change', function() { node.unbind('load').remove(); }); node = $('<iframe class="elfinder-quicklook-preview-pdf"/>') .hide() .appendTo(preview) .load(function() { ql.hideinfo(); node.show(); }) .attr('src', fm.url(file.hash)); } }) }, /** * 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.bind('update', function(e) { var file = e.file, node; if (file.mime == mime) { e.stopImmediatePropagation(); ql.hideinfo(); preview.append((node = $('<embed class="elfinder-quicklook-preview-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" src="'+fm.url(file.hash)+'" quality="high" type="application/x-shockwave-flash" />'))); } }); }, /** * HTML5 audio preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var preview = ql.preview, autoplay = !!ql.options['autoplay'], 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' }, node; preview.bind('update', function(e) { var file = e.file, type = mimes[file.mime]; if (ql.support.audio[type]) { e.stopImmediatePropagation(); node = $('<audio class="elfinder-quicklook-preview-audio" controls preload="auto" autobuffer><source src="'+ql.fm.url(file.hash)+'" /></audio>') .appendTo(preview); autoplay && node[0].play(); } }).bind('change', function() { if (node && node.parent().length) { node[0].pause(); node.remove(); node= null; } }); }, /** * HTML5 video preview plugin * * @param elFinder.commands.quicklook **/ function(ql) { var preview = ql.preview, autoplay = !!ql.options['autoplay'], mimes = { 'video/mp4' : 'mp4', 'video/x-m4v' : 'mp4', 'video/ogg' : 'ogg', 'application/ogg' : 'ogg', 'video/webm' : 'webm' }, node; preview.bind('update', function(e) { var file = e.file, type = mimes[file.mime]; if (ql.support.video[type]) { e.stopImmediatePropagation(); ql.hideinfo(); node = $('<video class="elfinder-quicklook-preview-video" controls preload="auto" autobuffer><source src="'+ql.fm.url(file.hash)+'" /></video>').appendTo(preview); autoplay && node[0].play(); } }).bind('change', function() { if (node && node.parent().length) { node[0].pause(); node.remove(); node= null; } }); }, /** * Audio/video preview plugin using browser plugins * * @param elFinder.commands.quicklook **/ function(ql) { var preview = ql.preview, mimes = [], node; $.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); }); }); preview.bind('update', function(e) { var file = e.file, mime = file.mime, video; if ($.inArray(file.mime, mimes) !== -1) { e.stopImmediatePropagation(); (video = mime.indexOf('video/') === 0) && ql.hideinfo(); node = $('<embed src="'+ql.fm.url(file.hash)+'" type="'+mime+'" class="elfinder-quicklook-preview-'+(video ? 'video' : 'audio')+'"/>') .appendTo(preview); } }).bind('change', function() { if (node && node.parent().length) { node.remove(); node= null; } }); } ] /* * File: /js/commands/reload.js */ /** * @class elFinder command "reload" * Sync files and folders * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.reload = function() { var 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(e) { search = e.type == 'search'; }); }; 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(); } }; }; /* * File: /js/commands/rename.js */ /** * @class elFinder command "rename". * Rename selected file. * * @author Dmitry (dio) Levashov, dio@std42.ru **/ elFinder.prototype.commands.rename = function() { this.shortcuts = [{ pattern : 'f2'+(this.fm.OS == 'mac' ? ' enter' : '') }]; this.getstate = function() { var sel = this.fm.selectedFiles(); return !this._disabled && sel.length == 1 && sel[0].phash && !sel[0].locked ? 0 : -1; } this.exec = function() { var fm = this.fm, cwd = fm.getUI('cwd'), sel = fm.selected(), cnt = sel.length, file = fm.file(sel.shift()), filename = '.elfinder-cwd-filename', dfrd = $.Deferred() .fail(function(error) { var parent = input.parent(), name = fm.escape(file.name); if (parent.length) { input.remove(); parent.html(name); } else { cwd.find('#'+file.hash).find(filename).html(name); setTimeout(function() { cwd.find('#'+file.hash).click(); }, 50); } error && fm.error(error); }) .always(function() { fm.enable(); }), input = $('<input type="text"/>') .keydown(function(e) { e.stopPropagation(); e.stopImmediatePropagation(); if (e.keyCode == $.ui.keyCode.ESCAPE) { dfrd.reject(); } else if (e.keyCode == $.ui.keyCode.ENTER) { input.blur(); } }) .mousedown(function(e) { e.stopPropagation(); }) .click(function(e) { // for touch device e.stopPropagation(); }) .dblclick(function(e) { e.stopPropagation(); e.preventDefault(); }) .blur(function() { var name = $.trim(input.val()), parent = input.parent(); if (parent.length) { if (input[0].setSelectionRange) { input[0].setSelectionRange(0, 0) } if (name == file.name) { return dfrd.reject(); } if (!name) { return dfrd.reject('errInvName'); } if (fm.fileByName(name, file.phash)) { return dfrd.reject(['errExists', name]); } parent.html(fm.escape(name)); fm.lockfiles({files : [file.hash]}); fm.request({ data : {cmd : 'rename', target : file.hash, name : name}, notify : {type : 'rename', cnt : 1} }) .fail(function(error) { dfrd.reject(); fm.sync(); }) .done(function(data) { dfrd.resolve(data); }) .always(function() { fm.unlockfiles({files : [file.hash]}) }); } }), node = cwd.find('#'+file.hash).find(filename).empty().append(input.val(file.name)), name = input.val().replace(/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/ig, '') ; if (this.disabled()) { return dfrd.reject(); } if (!file || cnt > 1 || !node.length) { return dfrd.reject('errCmdParams', this.title); } if (file.locked) { return dfrd.reject(['errLocked', file.name]); } fm.one('select', function() { input.parent().length && file && $.inArray(file.hash, fm.selected()) === -1 && input.blur(); }) input.select().focus(); input[0].setSelectionRange && input[0].setSelectionRange(0, name.length); return dfrd; } } /* * 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() { this.updateOnSelect = false; this.getstate = function() { var sel = this.fm.selectedFiles(); return !this._disabled && sel.length == 1 && sel[0].read && sel[0].write && sel[0].mime.indexOf('image/') !== -1 ? 0 : -1; }; this.exec = function(hashes) { var fm = this.fm, files = this.files(hashes), dfrd = $.Deferred(), open = function(file, id) { var dialog = $('<div class="elfinder-dialog-resize"/>'), input = '<input type="text" size="5"/>', row = '<div class="elfinder-resize-row"/>', label = '<div class="elfinder-resize-label"/>', control = $('<div class="elfinder-resize-control"/>'), preview = $('<div class="elfinder-resize-preview"/>'), spinner = $('<div class="elfinder-resize-spinner">'+fm.i18n('ntfloadimg')+'</div>'), rhandle = $('<div class="elfinder-resize-handle"/>'), rhandlec = $('<div class="elfinder-resize-handle"/>'), uiresize = $('<div class="elfinder-resize-uiresize"/>'), uicrop = $('<div class="elfinder-resize-uicrop"/>'), uibuttonset = '<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>', uibutton = '<div class="ui-state-default elfinder-button"/>', uiseparator = '<span class="ui-widget-content elfinder-toolbar-button-separator"/>', uirotate = $('<div class="elfinder-resize-rotate"/>'), uideg270 = $(uibutton).attr('title',fm.i18n('rotate-cw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-l"/>') .click(function(){ rdegree = rdegree - 90; rotate.update(rdegree); })), uideg90 = $(uibutton).attr('title',fm.i18n('rotate-ccw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-r"/>') .click(function(){ rdegree = rdegree + 90; rotate.update(rdegree); })), uiprop = $('<span />'), reset = $('<div class="ui-state-default ui-corner-all elfinder-resize-reset"><span class="ui-icon ui-icon-arrowreturnthick-1-w"/></div>'), uitype = $('<div class="elfinder-resize-type"/>') .append('<input type="radio" name="type" id="'+id+'-resize" value="resize" checked="checked" /><label for="'+id+'-resize">'+fm.i18n('resize')+'</label>') .append('<input type="radio" name="type" id="'+id+'-crop" value="crop" /><label for="'+id+'-crop">'+fm.i18n('crop')+'</label>') .append('<input type="radio" name="type" id="'+id+'-rotate" value="rotate" /><label for="'+id+'-rotate">'+fm.i18n('rotate')+'</label>'), type = $('input', uitype).attr('disabled', 'disabled') .change(function() { var val = $('input:checked', uitype).val(); resetView(); resizable(true); croppable(true); rotateable(true); if (val == 'resize') { uiresize.show(); uirotate.hide(); uicrop.hide(); resizable(); } else if (val == 'crop') { uirotate.hide(); uiresize.hide(); uicrop.show(); croppable(); } else if (val == 'rotate') { uiresize.hide(); uicrop.hide(); uirotate.show(); rotateable(); } }), constr = $('<input type="checkbox" checked="checked"/>') .change(function() { cratio = !!constr.prop('checked'); resize.fixHeight(); resizable(true); resizable(); }), width = $(input) .change(function() { var w = parseInt(width.val()), h = parseInt(cratio ? Math.round(w/ratio) : height.val()); if (w > 0 && h > 0) { resize.updateView(w, h); height.val(h); } }), height = $(input) .change(function() { var h = parseInt(height.val()), w = parseInt(cratio ? Math.round(h*ratio) : width.val()); if (w > 0 && h > 0) { resize.updateView(w, h); width.val(w); } }), pointX = $(input).change(function(){crop.updateView();}), pointY = $(input).change(function(){crop.updateView();}), offsetX = $(input).change(function(){crop.updateView();}), offsetY = $(input).change(function(){crop.updateView();}), degree = $('<input type="text" size="3" maxlength="3" value="0" />') .change(function() { rotate.update(); }), uidegslider = $('<div class="elfinder-resize-rotate-slider"/>') .slider({ min: 0, max: 359, value: degree.val(), animate: true, change: function(event, ui) { if (ui.value != uidegslider.slider('value')) { rotate.update(ui.value); } }, slide: function(event, ui) { rotate.update(ui.value, false); } }), ratio = 1, prop = 1, owidth = 0, oheight = 0, cratio = true, pwidth = 0, pheight = 0, rwidth = 0, rheight = 0, rdegree = 0, img = $('<img/>') .load(function() { spinner.remove(); owidth = img.width(); oheight = img.height(); ratio = owidth/oheight; resize.updateView(owidth, oheight); rhandle.append(img.show()).show(); width.val(owidth); height.val(oheight); var r_scale = Math.min(pwidth, pheight) / Math.sqrt(Math.pow(owidth, 2) + Math.pow(oheight, 2)); rwidth = owidth * r_scale; rheight = oheight * r_scale; type.button('enable'); control.find('input,select').removeAttr('disabled') .filter(':text').keydown(function(e) { var c = e.keyCode, i; e.stopPropagation(); if ((c >= 37 && c <= 40) || c == $.ui.keyCode.BACKSPACE || c == $.ui.keyCode.DELETE || (c == 65 && (e.ctrlKey||e.metaKey)) || c == 27) { return; } if (c == 9) { i = $(this).parent()[e.shiftKey ? 'prev' : 'next']('.elfinder-resize-row').children(':text'); if (i.length) { i.focus(); } else { $(this).parent().parent().find(':text:' + (e.shiftKey ? 'last' : 'first')).focus(); } } if (c == 13) { fm.confirm({ title : $('input:checked', uitype).val(), text : 'confirmReq', accept : { label : 'btnApply', callback : function() { save(); } }, cancel : { label : 'btnCancel', callback : function(){} } }); return; } if (!((c >= 48 && c <= 57) || (c >= 96 && c <= 105))) { e.preventDefault(); } }) .filter(':first').focus(); resizable(); reset.hover(function() { reset.toggleClass('ui-state-hover'); }).click(resetView); }) .error(function() { spinner.text('Unable to load image').css('background', 'transparent'); }), basec = $('<div/>'), imgc = $('<img/>'), coverc = $('<div/>'), imgr = $('<img/>'), resetView = function() { width.val(owidth); height.val(oheight); resize.updateView(owidth, oheight); }, resize = { update : function() { width.val(Math.round(img.width()/prop)); height.val(Math.round(img.height()/prop)); }, updateView : function(w, h) { if (w > pwidth || h > pheight) { if (w / pwidth > h / pheight) { prop = pwidth / w; img.width(pwidth).height(Math.ceil(h*prop)); } else { prop = pheight / h; img.height(pheight).width(Math.ceil(w*prop)); } } else { img.width(w).height(h); } prop = img.width()/w; uiprop.text('1 : '+(1/prop).toFixed(2)); resize.updateHandle(); }, updateHandle : function() { rhandle.width(img.width()).height(img.height()); }, fixWidth : function() { var w, h; if (cratio) { h = height.val(); h = Math.round(h*ratio); resize.updateView(w, h); width.val(w); } }, fixHeight : function() { var w, h; if (cratio) { w = width.val(); h = Math.round(w/ratio); resize.updateView(w, h); height.val(h); } } }, crop = { update : function() { offsetX.val(Math.round((rhandlec.data('w')||rhandlec.width())/prop)); offsetY.val(Math.round((rhandlec.data('h')||rhandlec.height())/prop)); pointX.val(Math.round(((rhandlec.data('x')||rhandlec.offset().left)-imgc.offset().left)/prop)); pointY.val(Math.round(((rhandlec.data('y')||rhandlec.offset().top)-imgc.offset().top)/prop)); }, updateView : function() { var x = parseInt(pointX.val()) * prop + imgc.offset().left; var y = parseInt(pointY.val()) * prop + imgc.offset().top; var w = offsetX.val() * prop; var h = offsetY.val() * prop; rhandlec.data({x: x, y: y, w: w, h: h}); rhandlec.width(Math.round(w)); rhandlec.height(Math.round(h)); coverc.width(rhandlec.width()); coverc.height(rhandlec.height()); rhandlec.offset({left: Math.round(x), top: Math.round(y)}); }, resize_update : function() { rhandlec.data({w: null, h: null}); crop.update(); coverc.width(rhandlec.width()); coverc.height(rhandlec.height()); }, drag_update : function() { rhandlec.data({x: null, y: null}); crop.update(); } }, rotate = { mouseStartAngle : 0, imageStartAngle : 0, imageBeingRotated : false, update : function(value, animate) { if (typeof value == 'undefined') { rdegree = value = parseInt(degree.val()); } if (typeof animate == 'undefined') { animate = true; } if (! animate || fm.UA.Opera || fm.UA.ltIE8) { imgr.rotate(value); } else { imgr.animate({rotate: value + 'deg'}); } value = value % 360; if (value < 0) { value += 360; } degree.val(parseInt(value)); uidegslider.slider('value', degree.val()); }, execute : function ( e ) { if ( !rotate.imageBeingRotated ) return; var imageCentre = rotate.getCenter( imgr ); var mouseXFromCentre = e.pageX - imageCentre[0]; var mouseYFromCentre = e.pageY - imageCentre[1]; var mouseAngle = Math.atan2( mouseYFromCentre, mouseXFromCentre ); var rotateAngle = mouseAngle - rotate.mouseStartAngle + rotate.imageStartAngle; rotateAngle = Math.round(parseFloat(rotateAngle) * 180 / Math.PI); if ( e.shiftKey ) { rotateAngle = Math.round((rotateAngle + 6)/15) * 15; } imgr.rotate(rotateAngle); rotateAngle = rotateAngle % 360; if (rotateAngle < 0) { rotateAngle += 360; } degree.val(rotateAngle); uidegslider.slider('value', degree.val()); return false; }, start : function ( e ) { rotate.imageBeingRotated = true; var imageCentre = rotate.getCenter( imgr ); var mouseStartXFromCentre = e.pageX - imageCentre[0]; var mouseStartYFromCentre = e.pageY - imageCentre[1]; rotate.mouseStartAngle = Math.atan2( mouseStartYFromCentre, mouseStartXFromCentre ); rotate.imageStartAngle = parseFloat(imgr.rotate()) * Math.PI / 180.0; $(document).mousemove( rotate.execute ); return false; }, stop : function ( e ) { if ( !rotate.imageBeingRotated ) return; $(document).unbind( 'mousemove' , rotate.execute); setTimeout( function() { rotate.imageBeingRotated = false; }, 10 ); return false; }, getCenter : function ( image ) { var currentRotation = imgr.rotate(); imgr.rotate(0); var imageOffset = imgr.offset(); var imageCentreX = imageOffset.left + imgr.width() / 2; var imageCentreY = imageOffset.top + imgr.height() / 2; imgr.rotate(currentRotation); return Array( imageCentreX, imageCentreY ); } }, resizable = function(destroy) { if ($.fn.resizable) { if (destroy) { rhandle.filter(':ui-resizable').resizable('destroy'); rhandle.hide(); } else { rhandle.show(); rhandle.resizable({ alsoResize : img, aspectRatio : cratio, resize : resize.update, stop : resize.fixHeight }); } } }, croppable = function(destroy) { if ($.fn.draggable && $.fn.resizable) { if (destroy) { rhandlec.filter(':ui-resizable').resizable('destroy'); rhandlec.filter(':ui-draggable').draggable('destroy'); basec.hide(); } else { imgc .width(img.width()) .height(img.height()); coverc .width(img.width()) .height(img.height()); rhandlec .width(imgc.width()) .height(imgc.height()) .offset(imgc.offset()) .resizable({ containment : basec, resize : crop.resize_update, handles : 'all' }) .draggable({ handle : coverc, containment : imgc, drag : crop.drag_update }); basec.show() .width(img.width()) .height(img.height()); crop.update(); } } }, rotateable = function(destroy) { if ($.fn.draggable && $.fn.resizable) { if (destroy) { imgr.hide(); } else { imgr.show() .width(rwidth) .height(rheight) .css('margin-top', (pheight-rheight)/2 + 'px') .css('margin-left', (pwidth-rwidth)/2 + 'px'); } } }, save = function() { var w, h, x, y, d; var mode = $('input:checked', uitype).val(); //width.add(height).change(); // may be unnecessary if (mode == 'resize') { w = parseInt(width.val()) || 0; h = parseInt(height.val()) || 0; } else if (mode == 'crop') { w = parseInt(offsetX.val()) || 0; h = parseInt(offsetY.val()) || 0; x = parseInt(pointX.val()) || 0; y = parseInt(pointY.val()) || 0; } else if (mode == 'rotate') { w = owidth; h = oheight; d = parseInt(degree.val()) || 0; if (d < 0 || d > 360) { return fm.error('Invalid rotate degree'); } if (d == 0 || d == 360) { return fm.error('Image dose not rotated'); } } if (mode != 'rotate') { if (w <= 0 || h <= 0) { return fm.error('Invalid image size'); } if (w == owidth && h == oheight) { return fm.error('Image size not changed'); } } dialog.elfinderdialog('close'); fm.request({ data : { cmd : 'resize', target : file.hash, width : w, height : h, x : x, y : y, degree : d, mode : mode }, notify : {type : 'resize', cnt : 1} }) .fail(function(error) { dfrd.reject(error); }) .done(function() { dfrd.resolve(); }); }, buttons = {}, hline = 'elfinder-resize-handle-hline', vline = 'elfinder-resize-handle-vline', rpoint = 'elfinder-resize-handle-point', src = fm.url(file.hash) ; imgr.mousedown( rotate.start ); $(document).mouseup( rotate.stop ); uiresize.append($(row).append($(label).text(fm.i18n('width'))).append(width).append(reset)) .append($(row).append($(label).text(fm.i18n('height'))).append(height)) .append($(row).append($('<label/>').text(fm.i18n('aspectRatio')).prepend(constr))) .append($(row).append(fm.i18n('scale')+' ').append(uiprop)); uicrop.append($(row).append($(label).text('X')).append(pointX)) .append($(row).append($(label).text('Y')).append(pointY)) .append($(row).append($(label).text(fm.i18n('width'))).append(offsetX)) .append($(row).append($(label).text(fm.i18n('height'))).append(offsetY)); uirotate.append($(row) .append($(label).text(fm.i18n('rotate'))) .append($('<div style="float:left; width: 130px;">') .append($('<div style="float:left;">') .append(degree) .append($('<span/>').text(fm.i18n('degree'))) ) .append($(uibuttonset).append(uideg270).append($(uiseparator)).append(uideg90)) ) .append(uidegslider) ); dialog.append(uitype); control.append($(row)) .append(uiresize) .append(uicrop.hide()) .append(uirotate.hide()) .find('input,select').attr('disabled', 'disabled'); rhandle.append('<div class="'+hline+' '+hline+'-top"/>') .append('<div class="'+hline+' '+hline+'-bottom"/>') .append('<div class="'+vline+' '+vline+'-left"/>') .append('<div class="'+vline+' '+vline+'-right"/>') .append('<div class="'+rpoint+' '+rpoint+'-e"/>') .append('<div class="'+rpoint+' '+rpoint+'-se"/>') .append('<div class="'+rpoint+' '+rpoint+'-s"/>'); preview.append(spinner).append(rhandle.hide()).append(img.hide()); rhandlec.css('position', 'absolute') .append('<div class="'+hline+' '+hline+'-top"/>') .append('<div class="'+hline+' '+hline+'-bottom"/>') .append('<div class="'+vline+' '+vline+'-left"/>') .append('<div class="'+vline+' '+vline+'-right"/>') .append('<div class="'+rpoint+' '+rpoint+'-n"/>') .append('<div class="'+rpoint+' '+rpoint+'-e"/>') .append('<div class="'+rpoint+' '+rpoint+'-s"/>') .append('<div class="'+rpoint+' '+rpoint+'-w"/>') .append('<div class="'+rpoint+' '+rpoint+'-ne"/>') .append('<div class="'+rpoint+' '+rpoint+'-se"/>') .append('<div class="'+rpoint+' '+rpoint+'-sw"/>') .append('<div class="'+rpoint+' '+rpoint+'-nw"/>'); preview.append(basec.css('position', 'absolute').hide().append(imgc).append(rhandlec.append(coverc))); preview.append(imgr.hide()); preview.css('overflow', 'hidden'); dialog.append(preview).append(control); buttons[fm.i18n('btnApply')] = save; buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); }; fm.dialog(dialog, { title : file.name, width : 650, resizable : false, destroyOnClose : true, buttons : buttons, open : function() { preview.zIndex(1+$(this).parent().zIndex()); } }).attr('id', id); // for IE < 9 dialog mising at open second+ time. if (fm.UA.ltIE8) { $('.elfinder-dialog').css('filter', ''); } reset.css('left', width.position().left + width.width() + 12); coverc.css({ 'opacity': 0.2, 'background-color': '#fff', 'position': 'absolute'}), rhandlec.css('cursor', 'move'); rhandlec.find('.elfinder-resize-handle-point').css({ 'background-color' : '#fff', 'opacity': 0.5, 'border-color':'#000' }); imgr.css('cursor', 'pointer'); uitype.buttonset(); pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width()); pheight = preview.height() - (rhandle.outerHeight() - rhandle.height()); img.attr('src', src + (src.indexOf('?') === -1 ? '?' : '&')+'_='+Math.random()); imgc.attr('src', img.attr('src')); imgr.attr('src', img.attr('src')); }, id, dialog ; if (!files.length || files[0].mime.indexOf('image/') === -1) { return dfrd.reject(); } id = 'resize-'+fm.namespace+'-'+files[0].hash; dialog = fm.getUI().find('#'+id); if (dialog.length) { dialog.elfinderdialog('toTop'); return dfrd.resolve(); } open(files[0], id); return dfrd; }; }; (function ($) { var findProperty = function (styleObject, styleArgs) { var i = 0 ; for( i in styleArgs) { if (typeof styleObject[styleArgs[i]] != 'undefined') return styleArgs[i]; } styleObject[styleArgs[i]] = ''; return styleArgs[i]; }; $.cssHooks.rotate = { get: function(elem, computed, extra) { return $(elem).rotate(); }, set: function(elem, value) { $(elem).rotate(value); return value; } }; $.cssHooks.transform = { get: function(elem, computed, extra) { var name = findProperty( elem.style , ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] ); return elem.style[name]; }, set: function(elem, value) { var name = findProperty( elem.style , ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] ); elem.style[name] = value; return value; } }; $.fn.rotate = function(val) { if (typeof val == 'undefined') { if (!!window.opera) { var r = this.css('transform').match(/rotate\((.*?)\)/); return ( r && r[1])? Math.round(parseFloat(r[1]) * 180 / Math.PI) : 0; } else { var r = this.css('transform').match(/rotate\((.*?)\)/); return ( r && r[1])? parseInt(r[1]) : 0; } } this.css('transform', this.css('transform').replace(/none|rotate\(.*?\)/, '') + 'rotate(' + parseInt(val) + 'deg)'); return this; }; $.fx.step.rotate = function(fx) { if ( fx.state == 0 ) { fx.start = $(fx.elem).rotate(); fx.now = fx.start; } $(fx.elem).rotate(fx.now); }; if (typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined") { // IE & IE<9 var GetAbsoluteXY = function(element) { var pnode = element; var x = pnode.offsetLeft; var y = pnode.offsetTop; while ( pnode.offsetParent ) { pnode = pnode.offsetParent; if (pnode != document.body && pnode.currentStyle['position'] != 'static') { break; } if (pnode != document.body && pnode != document.documentElement) { x -= pnode.scrollLeft; y -= pnode.scrollTop; } x += pnode.offsetLeft; y += pnode.offsetTop; } return { x: x, y: y }; }; var StaticToAbsolute = function (element) { if ( element.currentStyle['position'] != 'static') { return ; } var xy = GetAbsoluteXY(element); element.style.position = 'absolute' ; element.style.left = xy.x + 'px'; element.style.top = xy.y + 'px'; }; var IETransform = function(element,transform){ var r; var m11 = 1; var m12 = 1; var m21 = 1; var m22 = 1; if (typeof element.style['msTransform'] != 'undefined'){ return true; } StaticToAbsolute(element); r = transform.match(/rotate\((.*?)\)/); var rotate = ( r && r[1]) ? parseInt(r[1]) : 0; rotate = rotate % 360; if (rotate < 0) rotate = 360 + rotate; var radian= rotate * Math.PI / 180; var cosX =Math.cos(radian); var sinY =Math.sin(radian); m11 *= cosX; m12 *= -sinY; m21 *= sinY; m22 *= cosX; element.style.filter = (element.style.filter || '').replace(/progid:DXImageTransform\.Microsoft\.Matrix\([^)]*\)/, "" ) + ("progid:DXImageTransform.Microsoft.Matrix(" + "M11=" + m11 + ",M12=" + m12 + ",M21=" + m21 + ",M22=" + m22 + ",FilterType='bilinear',sizingMethod='auto expand')") ; var ow = parseInt(element.style.width || element.width || 0 ); var oh = parseInt(element.style.height || element.height || 0 ); var radian = rotate * Math.PI / 180; var absCosX =Math.abs(Math.cos(radian)); var absSinY =Math.abs(Math.sin(radian)); var dx = (ow - (ow * absCosX + oh * absSinY)) / 2; var dy = (oh - (ow * absSinY + oh * absCosX)) / 2; element.style.marginLeft = Math.floor(dx) + "px"; element.style.marginTop = Math.floor(dy) + "px"; return(true); }; var transform_set = $.cssHooks.transform.set; $.cssHooks.transform.set = function(elem, value) { transform_set.apply(this, [elem, value] ); IETransform(elem,value); return value; }; } })(jQuery); /* * File: /js/commands/rm.js */ /** * @class elFinder command "rm" * Delete files * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.rm = function() { this.shortcuts = [{ pattern : 'delete ctrl+backspace' }]; this.getstate = function(sel) { var fm = this.fm; sel = sel || fm.selected(); return !this._disabled && sel.length && $.map(sel, function(h) { var f = fm.file(h); return f && f.phash && !f.locked ? h : null }).length == sel.length ? 0 : -1; } this.exec = function(hashes) { var self = this, fm = this.fm, dfrd = $.Deferred() .fail(function(error) { error && fm.error(error); }), files = this.files(hashes), cnt = files.length, cwd = fm.cwd().hash, goroot = false; if (!cnt || this._disabled) { return dfrd.reject(); } $.each(files, function(i, file) { if (!file.phash) { return !dfrd.reject(['errRm', file.name, 'errPerm']); } if (file.locked) { return !dfrd.reject(['errLocked', file.name]); } if (file.hash == cwd) { goroot = fm.root(file.hash); } }); if (dfrd.state() == 'pending') { files = this.hashes(hashes); fm.confirm({ title : self.title, text : 'confirmRm', accept : { label : 'btnRm', callback : function() { fm.lockfiles({files : files}); fm.request({ data : {cmd : 'rm', targets : files}, notify : {type : 'rm', cnt : cnt}, preventFail : true }) .fail(function(error) { dfrd.reject(error); }) .done(function(data) { dfrd.done(data); goroot && fm.exec('open', goroot) } ).always(function() { fm.unlockfiles({files : files}); }); } }, cancel : { label : 'btnCancel', callback : function() { dfrd.reject(); } } }); } return dfrd; } } /* * File: /js/commands/search.js */ /** * @class elFinder command "search" * Find files * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.search = function() { this.title = 'Find files'; this.options = {ui : 'searchbutton'} this.alwaysEnabled = true; this.updateOnSelect = false; /** * Return command status. * Search does not support old api. * * @return Number **/ this.getstate = function() { return 0; } /** * Send search request to backend. * * @param String search string * @return $.Deferred **/ this.exec = function(q) { var fm = this.fm; if (typeof(q) == 'string' && q) { fm.trigger('searchstart', {query : q}); return fm.request({ data : {cmd : 'search', q : q}, notify : {type : 'search', cnt : 1, hideCnt : true} }); } fm.getUI('toolbar').find('.'+fm.res('class', 'searchbtn')+' :text').focus(); return $.Deferred().reject(); } } /* * File: /js/commands/sort.js */ /** * @class elFinder command "sort" * Change sort files rule * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.sort = function() { var self = this, fm = self.fm; /** * Command options * * @type Object */ this.options = {ui : 'sortbutton'}; fm.bind('open sortchange', function() { self.variants = []; $.each(fm.sortRules, function(name, value) { var sort = { type : name, order : name == fm.sortType ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder }; var arr = name == fm.sortType ? (sort.order == 'asc'? 'n' : 's') : ''; self.variants.push([sort, (arr? '<span class="ui-icon ui-icon-arrowthick-1-'+arr+'"></span>' : '') + ' ' + fm.i18n('sort'+name)]); }); }); fm.bind('open sortchange viewchange', function() { var timer = null; timer && clearTimeout(timer); timer = setTimeout(function(){ var cols = $(fm.cwd).find('div.elfinder-cwd-wrapper-list table'); if (cols.length) { $.each(fm.sortRules, function(name, value) { var td = cols.find('thead tr td.elfinder-cwd-view-th-'+name); if (td.length) { var current = ( name == fm.sortType), sort = { type : name, order : current ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder },arr; if (current) { arr = fm.sortOrder == 'asc' ? 'n' : 's'; $('<span class="ui-icon ui-icon-triangle-1-'+arr+'"/>').css({left:'+center+'}).appendTo(td); } $(td).on('click', function(e){ e.stopPropagation(); self.exec([], sort); }) .hover(function() { $(this).addClass('ui-state-hover'); },function() { $(this).removeClass('ui-state-hover'); }); } }); } }, 100); }); this.getstate = function() { return 0; }; this.exec = function(hashes, sortopt) { var fm = this.fm, sort = $.extend({ type : fm.sortType, order : fm.sortOrder, stick : fm.sortStickFolders }, sortopt); this.fm.setSort(sort.type, sort.order, sort.stick); return $.Deferred().resolve(); }; }; /* * File: /js/commands/up.js */ /** * @class elFinder command "up" * Go into parent directory * * @author Dmitry (dio) Levashov **/ elFinder.prototype.commands.up = function() { this.alwaysEnabled = true; this.updateOnSelect = false; this.shortcuts = [{ pattern : 'ctrl+up' }]; this.getstate = function() { return this.fm.cwd().phash ? 0 : -1; } this.exec = function() { return this.fm.cwd().phash ? this.fm.exec('open', this.fm.cwd().phash) : $.Deferred().reject(); } } /* * File: /js/commands/upload.js */ /** * @class elFinder command "upload" * Upload files using iframe or XMLHttpRequest & FormData. * Dialog allow to send files using drag and drop * * @type elFinder.command * @author Dmitry (dio) Levashov */ elFinder.prototype.commands.upload = function() { var hover = this.fm.res('class', 'hover'); this.disableOnSearch = true; this.updateOnSelect = false; // Shortcut opens dialog this.shortcuts = [{ pattern : 'ctrl+u' }]; /** * Return command state * * @return Number **/ this.getstate = function() { return !this._disabled && this.fm.cwd().write ? 0 : -1; }; this.exec = function(data) { var fm = this.fm, upload = function(data) { dialog.elfinderdialog('close'); fm.upload(data) .fail(function(error) { dfrd.reject(error); }) .done(function(data) { dfrd.resolve(data); }); }, dfrd, dialog, input, button, dropbox, pastebox; if (this.disabled()) { return $.Deferred().reject(); } if (data && (data.input || data.files)) { return fm.upload(data); } dfrd = $.Deferred(); input = $('<input type="file" multiple="true"/>') .change(function() { upload({input : input[0]}); }); button = $('<div class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">'+fm.i18n('selectForUpload')+'</span></div>') .append($('<form/>').append(input)) .hover(function() { button.toggleClass(hover) }); dialog = $('<div class="elfinder-upload-dialog-wrapper"/>') .append(button); pastebox = $('<div class="ui-corner-all elfinder-upload-dropbox" contenteditable=true></div>') .focus(function() { if (this.innerHTML) { var src = this.innerHTML.replace(/<br[^>]*>/gi, ' '); var type = src.match(/<[^>]+>/)? 'html' : 'text'; this.innerHTML = ''; upload({files : [ src ], type : type}); } }) .bind('dragenter mouseover', function(){ this.focus(); $(pastebox).addClass(hover); }) .bind('dragleave mouseout', function(){ this.blur(); $(pastebox).removeClass(hover); }) .bind('mouseup keyup', function() { setTimeout(function(){ $(pastebox).focus(); }, 100); }); if (fm.dragUpload) { dropbox = $('<div class="ui-corner-all elfinder-upload-dropbox">'+fm.i18n('dropFiles')+'</div>') .prependTo(dialog) .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>') .after(pastebox) .after('<div>'+fm.i18n('dropFilesBrowser')+'</div>') .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0]; dropbox.addEventListener('dragenter', function(e) { e.stopPropagation(); e.preventDefault(); $(dropbox).addClass(hover); }, false); dropbox.addEventListener('dragleave', function(e) { e.stopPropagation(); e.preventDefault(); $(dropbox).removeClass(hover); }, false); dropbox.addEventListener('dragover', function(e) { e.stopPropagation(); e.preventDefault(); $(dropbox).addClass(hover); }, false); dropbox.addEventListener('drop', function(e) { e.stopPropagation(); e.preventDefault(); var file = false; var type = ''; if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length) { file = e.dataTransfer.files; type = 'files'; } else if (e.dataTransfer.getData('text/html')) { file = [ e.dataTransfer.getData('text/html') ]; type = 'html'; } else if (e.dataTransfer.getData('text')) { file = [ e.dataTransfer.getData('text') ]; type = 'text'; } if (file) { upload({files : file, type : type}); } }, false); } else { $('<div>'+fm.i18n('dropFilesBrowser')+'</div>') .append(pastebox) .prependTo(dialog) .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0]; } fm.dialog(dialog, { title : this.title, modal : true, resizable : false, destroyOnClose : true }); 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() { this.value = this.fm.viewType; this.alwaysEnabled = true; this.updateOnSelect = false; this.options = { ui : 'viewbutton'}; this.getstate = function() { return 0; } this.exec = function() { var value = this.fm.storage('view', this.value == 'list' ? 'icons' : 'list'); this.fm.viewchange(); this.update(void(0), value); } } })(jQuery);