/backend/Metis/src/assets/lib/elFinder/js/elFinder.js
https://gitlab.com/project-store/theme · JavaScript · 2742 lines · 1631 code · 353 blank · 758 comment · 500 complexity · 76bcca0dd66f5f0a77d2a35d278fd2cd MD5 · raw file
Large files are truncated click here to view the full file
- "use strict";
- /**
- * @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) {
- 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
- code == 9 && 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;
-
- /**
- * 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';
-
- /**
- * 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;
- }
- })();
- /**
- * 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,
- revert : true,
- refreshPositions : true,
- cursor : 'move',
- cursorAt : {left : 50, top : 47},
- drag : function(e, ui) { ui.helper.toggleClass('elfinder-drag-helper-plus', e.shiftKey||e.ctrlKey||e.metaKey); },
- 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);
- if ((l = hashes.length) > 1) {
- helper.append(icon(files[hashes[l-1]].mime) + '<span class="elfinder-drag-num">'+l+'</span>');
- }
-
- return helper;
- }
- };
-
- /**
- * Base droppable options
- *
- * @type Object
- **/
- this.droppable = {
- 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));
- self.exec('paste', hash).always(function() { self.clipboard([]); });
- 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) {
- var file,
- path = [];
-
- while (hash && (file = files[hash]) && file.hash) {
- path.unshift(file.name);
- hash = file.phash;
- }
-
- return path;
- }
-
- /**
- * Return file path
- *
- * @param Object file
- * @return String
- */
- this.path = function(hash) {
- return files[hash] && files[hash].path
- ? files[hash].path
- : this.path2array(hash).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 && ($.browser.opera || $.browser.msie)) {
- 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] || 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);
- }
- 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.isRejected() && !_xhr.isResolved()) {
- _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 : 'parents', target : cwd},
- 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}
- });
- })
- .done(function(odata, pdata) {
- var diff = self.diff(odata.files.concat(pdata && pdata.tree ? pdata.tree : []));
- 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 ****************/
- // set sort variant
- this.setSort(this.options.sort, this.options.sortDirect);
-
- // 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 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.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;
- $('texarea: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;
- 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 duble 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),
- // 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);
-
- // send initial request and start to pray >_<
- this.trigger('init')
- .request({
- data : {cmd : 'open', target : self.lastDir(), 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 : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
- 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.ms-excel' : 'MsExcel',
- 'application/vnd.ms-powerpoint' : 'MsPP',
- 'application/pdf' : 'PDF',
- 'application/xml' : 'XML',
- 'application/vnd.oasis.opendocument.text' : '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)}
- },
-
- /**
- * Sort types for current directory content
- *
- * @type Object
- */
- sorts : {
- nameDirsFirst : 1,
- kindDirsFirst : 2,
- sizeDirsFirst : 3,
- dateDirsFirst : 4,
- name : 5,
- kind : 6,
- size : 7,
- date : 8
- },
-
- setSort : function(type, dir) {
- this.sort = this.sorts[type] || 1;
- this.sortDirect = dir == 'asc' || dir == 'desc' ? dir : 'asc';
- this.trigger('sortchange');
- },
-
- /**
- * 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 : {
- // upload transport using iframe
- iframe : function(data, fm) {
- var self = fm ? fm : this,
- input = data.input,
- 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 = $.browser.msie,
- // 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 (input && $(input).is(':file') && $(input).val()) {
- form.append(input);
- } else {
- return dfrd.reject();
- }
-
- cnt = input.files ? input.files.length : 1;
-
- 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 : data.files,
- 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) {…