').appendTo(node)
};
self.trigger('uiready');
// load required ui
jQuery.each(self.options.ui || [], function(i, ui) {
var name = 'elfinder'+ui,
opts = self.options.uiOptions[ui] || {};
if (!self.ui[ui] && jQuery.fn[name]) {
// regist to self.ui before make instance
self.ui[ui] = jQuery('<'+(opts.tag || 'div')+'/>').appendTo(node);
self.ui[ui][name](self, opts);
}
});
self.ui.progressbar.appendTo(self.ui.workzone);
self.ui.notify.prev('.ui-dialog-titlebar').append('
');
// update size
self.resize(width, height);
// make node resizable
if (self.options.resizable) {
node.resizable({
resize : function(e, ui) {
self.resize(ui.size.width, ui.size.height);
},
handles : 'se',
minWidth : 300,
minHeight : 200
});
if (self.UA.Touch) {
node.addClass('touch-punch');
}
}
(function() {
var navbar = self.getUI('navbar'),
cwd = self.getUI('cwd').parent();
self.autoScroll = {
navbarUp : function(v) {
navbar.scrollTop(Math.max(0, navbar.scrollTop() - v));
},
navbarDown : function(v) {
navbar.scrollTop(navbar.scrollTop() + v);
},
cwdUp : function(v) {
cwd.scrollTop(Math.max(0, cwd.scrollTop() - v));
},
cwdDown : function(v) {
cwd.scrollTop(cwd.scrollTop() + v);
}
};
})();
// Swipe on the touch devices to show/hide of toolbar or navbar
if (self.UA.Touch) {
(function() {
var lastX, lastY, nodeOffset, nodeWidth, nodeTop, navbarW, toolbarH,
navbar = self.getUI('navbar'),
toolbar = self.getUI('toolbar'),
moveEv = 'touchmove.stopscroll',
moveTm,
moveUpOn = function(e) {
var touches = e.originalEvent.touches || [{}],
y = touches[0].pageY || null;
if (!lastY || y < lastY) {
e.preventDefault();
moveTm && clearTimeout(moveTm);
}
},
moveDownOn = function(e) {
e.preventDefault();
moveTm && clearTimeout(moveTm);
},
moveOff = function() {
moveTm = setTimeout(function() {
node.off(moveEv);
}, 100);
},
handleW, handleH = 50;
navbar = navbar.children().length? navbar : null;
toolbar = toolbar.length? toolbar : null;
node.on('touchstart touchmove touchend', function(e) {
if (e.type === 'touchend') {
lastX = false;
lastY = false;
moveOff();
return;
}
var touches = e.originalEvent.touches || [{}],
x = touches[0].pageX || null,
y = touches[0].pageY || null,
ltr = (self.direction === 'ltr'),
navbarMode, treeWidth, swipeX, moveX, toolbarT, mode;
if (x === null || y === null || (e.type === 'touchstart' && touches.length > 1)) {
return;
}
if (e.type === 'touchstart') {
nodeOffset = node.offset();
nodeWidth = node.width();
if (navbar) {
lastX = false;
if (navbar.is(':hidden')) {
if (! handleW) {
handleW = Math.max(50, nodeWidth / 10);
}
if ((ltr? (x - nodeOffset.left) : (nodeWidth + nodeOffset.left - x)) < handleW) {
lastX = x;
}
} else if (! e.originalEvent._preventSwipeX) {
navbarW = navbar.width();
if (ltr) {
swipeX = (x < nodeOffset.left + navbarW);
} else {
swipeX = (x > nodeOffset.left + nodeWidth - navbarW);
}
if (swipeX) {
handleW = Math.max(50, nodeWidth / 10);
lastX = x;
} else {
lastX = false;
}
}
}
if (toolbar) {
lastY = false;
if (! e.originalEvent._preventSwipeY) {
toolbarH = toolbar.height();
nodeTop = nodeOffset.top;
if (y - nodeTop < (toolbar.is(':hidden')? handleH : (toolbarH + 30))) {
lastY = y;
node.on(moveEv, toolbar.is(':hidden')? moveDownOn: moveUpOn);
}
}
}
} else {
if (navbar && lastX !== false) {
navbarMode = (ltr? (lastX > x) : (lastX < x))? 'navhide' : 'navshow';
moveX = Math.abs(lastX - x);
if (navbarMode === 'navhide' && moveX > navbarW * 0.6
|| (moveX > (navbarMode === 'navhide'? navbarW / 3 : 45)
&& (navbarMode === 'navshow'
|| (ltr? x < nodeOffset.left + 20 : x > nodeOffset.left + nodeWidth - 20)
))
) {
self.getUI('navbar').trigger(navbarMode, {handleW: handleW});
lastX = false;
}
}
if (toolbar && lastY !== false ) {
toolbarT = toolbar.offset().top;
if (Math.abs(lastY - y) > Math.min(45, toolbarH / 3)) {
mode = (lastY > y)? 'slideUp' : 'slideDown';
if (mode === 'slideDown' || toolbarT + 20 > y) {
if (toolbar.is(mode === 'slideDown' ? ':hidden' : ':visible')) {
toolbar.stop(true, true).trigger('toggle', {duration: 100, handleH: handleH});
}
lastY = false;
}
}
}
}
});
})();
}
if (self.dragUpload) {
// add event listener for HTML5 DnD upload
(function() {
var isin = function(e) {
return (e.target.nodeName !== 'TEXTAREA' && e.target.nodeName !== 'INPUT' && jQuery(e.target).closest('div.ui-dialog-content').length === 0);
},
ent = 'native-drag-enter',
disable = 'native-drag-disable',
c = 'class',
navdir = self.res(c, 'navdir'),
droppable = self.res(c, 'droppable'),
dropover = self.res(c, 'adroppable'),
arrow = self.res(c, 'navarrow'),
clDropActive = self.res(c, 'adroppable'),
wz = self.getUI('workzone'),
ltr = (self.direction === 'ltr'),
clearTm = function() {
autoScrTm && cancelAnimationFrame(autoScrTm);
autoScrTm = null;
},
wzRect, autoScrFn, autoScrTm;
node.on('dragenter', function(e) {
clearTm();
if (isin(e)) {
e.preventDefault();
e.stopPropagation();
wzRect = wz.data('rectangle');
}
})
.on('dragleave', function(e) {
clearTm();
if (isin(e)) {
e.preventDefault();
e.stopPropagation();
}
})
.on('dragover', function(e) {
var autoUp;
if (isin(e)) {
e.preventDefault();
e.stopPropagation();
e.originalEvent.dataTransfer.dropEffect = 'none';
if (! autoScrTm) {
autoScrTm = requestAnimationFrame(function() {
var wzBottom = wzRect.top + wzRect.height,
wzBottom2 = wzBottom - self.getUI('navdock').outerHeight(true),
fn;
if ((autoUp = e.pageY < wzRect.top) || e.pageY > wzBottom2 ) {
if (wzRect.cwdEdge > e.pageX) {
fn = (ltr? 'navbar' : 'cwd') + (autoUp? 'Up' : 'Down');
} else {
fn = (ltr? 'cwd' : 'navbar') + (autoUp? 'Up' : 'Down');
}
if (!autoUp) {
if (fn.substr(0, 3) === 'cwd') {
if (wzBottom < e.pageY) {
wzBottom2 = wzBottom;
} else {
fn = '';
}
}
}
fn && self.autoScroll[fn](Math.pow((autoUp? wzRect.top - e.pageY : e.pageY - wzBottom2), 1.3));
}
autoScrTm = null;
});
}
} else {
clearTm();
}
})
.on('drop', function(e) {
clearTm();
if (isin(e)) {
e.stopPropagation();
e.preventDefault();
}
});
node.on('dragenter', '.native-droppable', function(e){
if (e.originalEvent.dataTransfer) {
var $elm = jQuery(e.currentTarget),
id = e.currentTarget.id || null,
cwd = null,
elfFrom;
if (!id) { // target is cwd
cwd = self.cwd();
$elm.data(disable, false);
try {
jQuery.each(e.originalEvent.dataTransfer.types, function(i, v){
if (v.substr(0, 13) === 'elfinderfrom:') {
elfFrom = v.substr(13).toLowerCase();
}
});
} catch(e) {}
}
if (!cwd || (cwd.write && (!elfFrom || elfFrom !== (window.location.href + cwd.hash).toLowerCase()))) {
e.preventDefault();
e.stopPropagation();
$elm.data(ent, true);
$elm.addClass(clDropActive);
} else {
$elm.data(disable, true);
}
}
})
.on('dragleave', '.native-droppable', function(e){
if (e.originalEvent.dataTransfer) {
var $elm = jQuery(e.currentTarget);
e.preventDefault();
e.stopPropagation();
if ($elm.data(ent)) {
$elm.data(ent, false);
} else {
$elm.removeClass(clDropActive);
}
}
})
.on('dragover', '.native-droppable', function(e){
if (e.originalEvent.dataTransfer) {
var $elm = jQuery(e.currentTarget);
e.preventDefault();
e.stopPropagation();
e.originalEvent.dataTransfer.dropEffect = $elm.data(disable)? 'none' : 'copy';
$elm.data(ent, false);
}
})
.on('drop', '.native-droppable', function(e){
if (e.originalEvent && e.originalEvent.dataTransfer) {
var $elm = jQuery(e.currentTarget),
id;
e.preventDefault();
e.stopPropagation();
$elm.removeClass(clDropActive);
if (e.currentTarget.id) {
id = $elm.hasClass(navdir)? self.navId2Hash(e.currentTarget.id) : self.cwdId2Hash(e.currentTarget.id);
} else {
id = self.cwd().hash;
}
e.originalEvent._target = id;
self.exec('upload', {dropEvt: e.originalEvent, target: id}, void 0, id);
}
});
})();
}
// trigger event cssloaded if cssAutoLoad disabled
if (self.cssloaded === false) {
self.cssloaded = true;
self.trigger('cssloaded');
}
// calculate elFinder node z-index
self.zIndexCalc();
// send initial request and start to pray >_<
self.trigger('init')
.request({
data : {cmd : 'open', target : self.startDir(), init : 1, tree : 1},
preventDone : true,
notify : {type : 'open', cnt : 1, hideCnt : true},
freeze : true
})
.fail(function() {
self.trigger('fail').disable().lastDir('');
listeners = {};
shortcuts = {};
jQuery(document).add(node).off('.'+namespace);
self.trigger = function() { };
})
.done(function(data) {
var trashDisable = function(th) {
var src = self.file(self.trashes[th]),
d = self.options.debug,
error;
if (src && src.volumeid) {
delete self.volOptions[src.volumeid].trashHash;
}
self.trashes[th] = false;
self.debug('backend-error', 'Trash hash "'+th+'" was not found or not writable.');
},
toChkTh = {};
// regist rawStringDecoder
if (self.options.rawStringDecoder) {
self.registRawStringDecoder(self.options.rawStringDecoder);
}
// re-calculate elFinder node z-index
self.zIndexCalc();
self.load().debug('api', self.api);
// update ui's size after init
node.trigger('resize');
// initial open
open(data);
self.trigger('open', data, false);
self.trigger('opendone');
if (inFrame && self.options.enableAlways) {
jQuery(window).trigger('focus');
}
// check self.trashes
jQuery.each(self.trashes, function(th) {
var dir = self.file(th),
src;
if (! dir) {
toChkTh[th] = true;
} else if (dir.mime !== 'directory' || ! dir.write) {
trashDisable(th);
}
});
if (Object.keys(toChkTh).length) {
self.request({
data : {cmd : 'info', targets : Object.keys(toChkTh)},
preventDefault : true
}).done(function(data) {
if (data && data.files) {
jQuery.each(data.files, function(i, dir) {
if (dir.mime === 'directory' && dir.write) {
delete toChkTh[dir.hash];
}
});
}
}).always(function() {
jQuery.each(toChkTh, trashDisable);
});
}
// to enable / disable
self[self.options.enableAlways? 'enable' : 'disable']();
});
// self.timeEnd('load');
// End of bootUp()
};
// call bootCallback function with elFinder instance, extraObject - { dfrdsBeforeBootup: dfrdsBeforeBootup }
if (bootCallback && typeof bootCallback === 'function') {
self.bootCallback = bootCallback;
bootCallback.call(node.get(0), self, { dfrdsBeforeBootup: dfrdsBeforeBootup });
}
// call dfrdsBeforeBootup functions then boot up elFinder
jQuery.when.apply(null, dfrdsBeforeBootup).done(function() {
bootUp();
}).fail(function(error) {
self.error(error);
});
};
//register elFinder to global scope
if (typeof toGlobal === 'undefined' || toGlobal) {
window.elFinder = elFinder;
}
/**
* Prototype
*
* @type Object
*/
elFinder.prototype = {
uniqueid : 0,
res : function(type, id) {
return this.resources[type] && this.resources[type][id];
},
/**
* User os. Required to bind native shortcuts for open/rename
*
* @type String
**/
OS : navigator.userAgent.indexOf('Mac') !== -1 ? 'mac' : navigator.userAgent.indexOf('Win') !== -1 ? 'win' : 'other',
/**
* User browser UA.
* jQuery.browser: version deprecated: 1.3, removed: 1.9
*
* @type Object
**/
UA : (function(){
var self = this,
webkit = !document.unqueID && !window.opera && !window.sidebar && 'localStorage' in window && 'WebkitAppearance' in document.documentElement.style,
chrome = webkit && window.chrome,
/*setRotated = function() {
var a = ((screen && screen.orientation && screen.orientation.angle) || window.orientation || 0) + 0;
if (a === -90) {
a = 270;
}
UA.Angle = a;
UA.Rotated = a % 180 === 0? false : true;
},*/
UA = {
// Browser IE <= IE 6
ltIE6 : typeof window.addEventListener == "undefined" && typeof document.documentElement.style.maxHeight == "undefined",
// Browser IE <= IE 7
ltIE7 : typeof window.addEventListener == "undefined" && typeof document.querySelectorAll == "undefined",
// Browser IE <= IE 8
ltIE8 : typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined",
// Browser IE <= IE 9
ltIE9 : document.uniqueID && document.documentMode <= 9,
// Browser IE <= IE 10
ltIE10 : document.uniqueID && document.documentMode <= 10,
// Browser IE >= IE 11
gtIE11 : document.uniqueID && document.documentMode >= 11,
IE : document.uniqueID,
Firefox : window.sidebar,
Opera : window.opera,
Webkit : webkit,
Chrome : chrome,
Edge : (chrome && window.msCredentials)? true : false,
Safari : webkit && !window.chrome,
Mobile : typeof window.orientation != "undefined",
Touch : typeof window.ontouchstart != "undefined",
iOS : navigator.platform.match(/^iP(?:[ao]d|hone)/),
Mac : navigator.platform.match(/^Mac/),
Fullscreen : (typeof (document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen || document.msExitFullscreen) !== 'undefined'),
Angle : 0,
Rotated : false,
CSS : (function() {
var aStyle = document.createElement('a').style,
pStyle = document.createElement('p').style,
css;
css = 'position:sticky;position:-webkit-sticky;';
css += 'width:-webkit-max-content;width:-moz-max-content;width:-ms-max-content;width:max-content;';
aStyle.cssText = css;
return {
positionSticky : aStyle.position.indexOf('sticky')!==-1,
widthMaxContent : aStyle.width.indexOf('max-content')!==-1,
flex : typeof pStyle.flex !== 'undefined'
};
})()
};
return UA;
})(),
/**
* Is cookie enabled
*
* @type Boolean
*/
cookieEnabled : window.navigator.cookieEnabled,
/**
* Has RequireJS?
*
* @type Boolean
*/
hasRequire : (typeof define === 'function' && define.amd),
/**
* Current request command
*
* @type String
*/
currentReqCmd : '',
/**
* Current keyboard state
*
* @type Object
*/
keyState : {},
/**
* Internationalization object
*
* @type Object
*/
i18 : {
en : {
translator : '',
language : 'English',
direction : 'ltr',
dateFormat : 'd.m.Y H:i',
fancyDateFormat : '$1 H:i',
nonameDateFormat : 'ymd-His',
messages : {}
},
months : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
monthsShort : ['msJan', 'msFeb', 'msMar', 'msApr', 'msMay', 'msJun', 'msJul', 'msAug', 'msSep', 'msOct', 'msNov', 'msDec'],
days : ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
daysShort : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
},
/**
* File mimetype to kind mapping
*
* @type Object
*/
kinds : {
'unknown' : 'Unknown',
'directory' : 'Folder',
'group' : 'Selects',
'symlink' : 'Alias',
'symlink-broken' : 'AliasBroken',
'application/x-empty' : 'TextPlain',
'application/postscript' : 'Postscript',
'application/vnd.ms-office' : 'MsOffice',
'application/msword' : 'MsWord',
'application/vnd.ms-word' : 'MsWord',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' : 'MsWord',
'application/vnd.ms-word.document.macroEnabled.12' : 'MsWord',
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' : 'MsWord',
'application/vnd.ms-word.template.macroEnabled.12' : 'MsWord',
'application/vnd.ms-excel' : 'MsExcel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' : 'MsExcel',
'application/vnd.ms-excel.sheet.macroEnabled.12' : 'MsExcel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' : 'MsExcel',
'application/vnd.ms-excel.template.macroEnabled.12' : 'MsExcel',
'application/vnd.ms-excel.sheet.binary.macroEnabled.12' : 'MsExcel',
'application/vnd.ms-excel.addin.macroEnabled.12' : 'MsExcel',
'application/vnd.ms-powerpoint' : 'MsPP',
'application/vnd.openxmlformats-officedocument.presentationml.presentation' : 'MsPP',
'application/vnd.ms-powerpoint.presentation.macroEnabled.12' : 'MsPP',
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' : 'MsPP',
'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' : 'MsPP',
'application/vnd.openxmlformats-officedocument.presentationml.template' : 'MsPP',
'application/vnd.ms-powerpoint.template.macroEnabled.12' : 'MsPP',
'application/vnd.ms-powerpoint.addin.macroEnabled.12' : 'MsPP',
'application/vnd.openxmlformats-officedocument.presentationml.slide' : 'MsPP',
'application/vnd.ms-powerpoint.slide.macroEnabled.12' : 'MsPP',
'application/pdf' : 'PDF',
'application/xml' : 'XML',
'application/vnd.oasis.opendocument.text' : 'OO',
'application/vnd.oasis.opendocument.text-template' : 'OO',
'application/vnd.oasis.opendocument.text-web' : 'OO',
'application/vnd.oasis.opendocument.text-master' : 'OO',
'application/vnd.oasis.opendocument.graphics' : 'OO',
'application/vnd.oasis.opendocument.graphics-template' : 'OO',
'application/vnd.oasis.opendocument.presentation' : 'OO',
'application/vnd.oasis.opendocument.presentation-template' : 'OO',
'application/vnd.oasis.opendocument.spreadsheet' : 'OO',
'application/vnd.oasis.opendocument.spreadsheet-template' : 'OO',
'application/vnd.oasis.opendocument.chart' : 'OO',
'application/vnd.oasis.opendocument.formula' : 'OO',
'application/vnd.oasis.opendocument.database' : 'OO',
'application/vnd.oasis.opendocument.image' : 'OO',
'application/vnd.openofficeorg.extension' : 'OO',
'application/x-shockwave-flash' : 'AppFlash',
'application/flash-video' : 'Flash video',
'application/x-bittorrent' : 'Torrent',
'application/javascript' : 'JS',
'application/rtf' : 'RTF',
'application/rtfd' : 'RTF',
'application/x-font-ttf' : 'TTF',
'application/x-font-otf' : 'OTF',
'application/x-rpm' : 'RPM',
'application/x-web-config' : 'TextPlain',
'application/xhtml+xml' : 'HTML',
'application/docbook+xml' : 'DOCBOOK',
'application/x-awk' : 'AWK',
'application/x-gzip' : 'GZIP',
'application/x-bzip2' : 'BZIP',
'application/x-xz' : 'XZ',
'application/zip' : 'ZIP',
'application/x-zip' : 'ZIP',
'application/x-rar' : 'RAR',
'application/x-tar' : 'TAR',
'application/x-7z-compressed' : '7z',
'application/x-jar' : 'JAR',
'text/plain' : 'TextPlain',
'text/x-php' : 'PHP',
'text/html' : 'HTML',
'text/javascript' : 'JS',
'text/css' : 'CSS',
'text/rtf' : 'RTF',
'text/rtfd' : 'RTF',
'text/x-c' : 'C',
'text/x-csrc' : 'C',
'text/x-chdr' : 'CHeader',
'text/x-c++' : 'CPP',
'text/x-c++src' : 'CPP',
'text/x-c++hdr' : 'CPPHeader',
'text/x-shellscript' : 'Shell',
'application/x-csh' : 'Shell',
'text/x-python' : 'Python',
'text/x-java' : 'Java',
'text/x-java-source' : 'Java',
'text/x-ruby' : 'Ruby',
'text/x-perl' : 'Perl',
'text/x-sql' : 'SQL',
'text/xml' : 'XML',
'text/x-comma-separated-values' : 'CSV',
'text/x-markdown' : 'Markdown',
'image/x-ms-bmp' : 'BMP',
'image/jpeg' : 'JPEG',
'image/gif' : 'GIF',
'image/png' : 'PNG',
'image/tiff' : 'TIFF',
'image/x-targa' : 'TGA',
'image/vnd.adobe.photoshop' : 'PSD',
'image/xbm' : 'XBITMAP',
'image/pxm' : 'PXM',
'audio/mpeg' : 'AudioMPEG',
'audio/midi' : 'AudioMIDI',
'audio/ogg' : 'AudioOGG',
'audio/mp4' : 'AudioMPEG4',
'audio/x-m4a' : 'AudioMPEG4',
'audio/wav' : 'AudioWAV',
'audio/x-mp3-playlist' : 'AudioPlaylist',
'video/x-dv' : 'VideoDV',
'video/mp4' : 'VideoMPEG4',
'video/mpeg' : 'VideoMPEG',
'video/x-msvideo' : 'VideoAVI',
'video/quicktime' : 'VideoMOV',
'video/x-ms-wmv' : 'VideoWM',
'video/x-flv' : 'VideoFlash',
'video/x-matroska' : 'VideoMKV',
'video/ogg' : 'VideoOGG'
},
/**
* File mimetype to file extention mapping
*
* @type Object
* @see elFinder.mimetypes.js
*/
mimeTypes : {},
/**
* Ajax request data validation rules
*
* @type Object
*/
rules : {
defaults : function(data) {
if (!data
|| (data.added && !Array.isArray(data.added))
|| (data.removed && !Array.isArray(data.removed))
|| (data.changed && !Array.isArray(data.changed))) {
return false;
}
return true;
},
open : function(data) { return data && data.cwd && data.files && jQuery.isPlainObject(data.cwd) && Array.isArray(data.files); },
tree : function(data) { return data && data.tree && Array.isArray(data.tree); },
parents : function(data) { return data && data.tree && Array.isArray(data.tree); },
tmb : function(data) { return data && data.images && (jQuery.isPlainObject(data.images) || Array.isArray(data.images)); },
upload : function(data) { return data && (jQuery.isPlainObject(data.added) || Array.isArray(data.added));},
search : function(data) { return data && data.files && Array.isArray(data.files); }
},
/**
* Commands costructors
*
* @type Object
*/
commands : {},
/**
* Commands to add the item (space delimited)
*
* @type String
*/
cmdsToAdd : 'archive duplicate extract mkdir mkfile paste rm upload',
parseUploadData : function(text) {
var self = this,
data;
if (!jQuery.trim(text)) {
return {error : ['errResponse', 'errDataEmpty']};
}
try {
data = JSON.parse(text);
} catch (e) {
return {error : ['errResponse', 'errDataNotJSON']};
}
data = self.normalize(data);
if (!self.validResponse('upload', data)) {
return {error : (data.norError || ['errResponse'])};
}
data.removed = jQuery.merge((data.removed || []), jQuery.map(data.added || [], function(f) { return self.file(f.hash)? f.hash : null; }));
return data;
},
iframeCnt : 0,
uploads : {
// xhr muiti uploading flag
xhrUploading: false,
// Timer of request fail to sync
failSyncTm: null,
// current chunkfail requesting chunk
chunkfailReq: {},
// check file/dir exists
checkExists: function(files, target, fm, isDir) {
var dfrd = jQuery.Deferred(),
names, renames = [], hashes = {}, chkFiles = [],
cancel = function() {
var i = files.length;
while (--i > -1) {
files[i]._remove = true;
}
},
resolve = function() {
dfrd.resolve(renames, hashes);
},
check = function() {
var existed = [], exists = [], i, c,
pathStr = target !== fm.cwd().hash? fm.path(target, true) + fm.option('separator', target) : '',
confirm = function(ndx) {
var last = ndx == exists.length-1,
opts = {
cssClass : 'elfinder-confirm-upload',
title : fm.i18n('cmdupload'),
text : ['errExists', pathStr + exists[ndx].name, 'confirmRepl'],
all : !last,
accept : {
label : 'btnYes',
callback : function(all) {
!last && !all
? confirm(++ndx)
: resolve();
}
},
reject : {
label : 'btnNo',
callback : function(all) {
var i;
if (all) {
i = exists.length;
while (ndx < i--) {
files[exists[i].i]._remove = true;
}
} else {
files[exists[ndx].i]._remove = true;
}
!last && !all
? confirm(++ndx)
: resolve();
}
},
cancel : {
label : 'btnCancel',
callback : function() {
cancel();
resolve();
}
},
buttons : [
{
label : 'btnBackup',
cssClass : 'elfinder-confirm-btn-backup',
callback : function(all) {
var i;
if (all) {
i = exists.length;
while (ndx < i--) {
renames.push(exists[i].name);
}
} else {
renames.push(exists[ndx].name);
}
!last && !all
? confirm(++ndx)
: resolve();
}
}
]
};
if (!isDir) {
opts.buttons.push({
label : 'btnRename' + (last? '' : 'All'),
cssClass : 'elfinder-confirm-btn-rename',
callback : function() {
renames = null;
resolve();
}
});
}
if (fm.iframeCnt > 0) {
delete opts.reject;
}
fm.confirm(opts);
};
if (! fm.file(target).read) {
// for dropbox type
resolve();
return;
}
names = jQuery.map(files, function(file, i) { return file.name && (!fm.UA.iOS || file.name !== 'image.jpg')? {i: i, name: file.name} : null ;});
fm.request({
data : {cmd : 'ls', target : target, intersect : jQuery.map(names, function(item) { return item.name;})},
notify : {type : 'preupload', cnt : 1, hideCnt : true},
preventDefault : true
})
.done(function(data) {
var existedArr, cwdItems;
if (data) {
if (data.error) {
cancel();
} else {
if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
if (data.list) {
if (Array.isArray(data.list)) {
existed = data.list || [];
} else {
existedArr = [];
existed = jQuery.map(data.list, function(n) {
if (typeof n === 'string') {
return n;
} else {
// support to >=2.1.11 plugin Normalizer, Sanitizer
existedArr = existedArr.concat(n);
return false;
}
});
if (existedArr.length) {
existed = existed.concat(existedArr);
}
hashes = data.list;
}
exists = jQuery.grep(names, function(name){
return jQuery.inArray(name.name, existed) !== -1 ? true : false ;
});
if (exists.length && existed.length && target == fm.cwd().hash) {
cwdItems = jQuery.map(fm.files(target), function(file) { return file.name; } );
if (jQuery.grep(existed, function(n) {
return jQuery.inArray(n, cwdItems) === -1? true : false;
}).length){
fm.sync();
}
}
}
}
}
}
if (exists.length > 0) {
confirm(0);
} else {
resolve();
}
})
.fail(function(error) {
cancel();
resolve();
error && fm.error(error);
});
};
if (fm.api >= 2.1 && typeof files[0] == 'object') {
check();
} else {
resolve();
}
return dfrd;
},
// check droped contents
checkFile : function(data, fm, target) {
if (!!data.checked || data.type == 'files') {
return data.files;
} else if (data.type == 'data') {
var dfrd = jQuery.Deferred(),
scanDfd = jQuery.Deferred(),
files = [],
paths = [],
dirctorys = [],
processing = 0,
items,
mkdirs = [],
cancel = false,
toArray = function(list) {
return Array.prototype.slice.call(list || [], 0);
},
doScan = function(items) {
var entry, readEntries,
excludes = fm.options.folderUploadExclude[fm.OS] || null,
length = items.length,
check = function() {
if (--processing < 1 && scanDfd.state() === 'pending') {
scanDfd.resolve();
}
},
pushItem = function(file) {
if (! excludes || ! file.name.match(excludes)) {
paths.push(entry.fullPath || '');
files.push(file);
}
check();
},
readEntries = function(dirReader) {
var entries = [],
read = function() {
dirReader.readEntries(function(results) {
if (cancel || !results.length) {
for (var i = 0; i < entries.length; i++) {
if (cancel) {
scanDfd.reject();
break;
}
doScan([entries[i]]);
}
check();
} else {
entries = entries.concat(toArray(results));
read();
}
}, check);
};
read();
};
processing++;
for (var i = 0; i < length; i++) {
if (cancel) {
scanDfd.reject();
break;
}
entry = items[i];
if (entry) {
if (entry.isFile) {
processing++;
entry.file(pushItem, check);
} else if (entry.isDirectory) {
if (fm.api >= 2.1) {
processing++;
mkdirs.push(entry.fullPath);
readEntries(entry.createReader()); // Start reading dirs.
}
}
}
}
check();
return scanDfd;
}, hasDirs;
items = jQuery.map(data.files.items, function(item){
return item.getAsEntry? item.getAsEntry() : item.webkitGetAsEntry();
});
jQuery.each(items, function(i, item) {
if (item.isDirectory) {
hasDirs = true;
return false;
}
});
if (items.length > 0) {
fm.uploads.checkExists(items, target, fm, hasDirs).done(function(renames, hashes){
var dfds = [];
if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
if (renames === null) {
data.overwrite = 0;
renames = [];
}
items = jQuery.grep(items, function(item){
var i, bak, hash, dfd, hi;
if (item.isDirectory && renames.length) {
i = jQuery.inArray(item.name, renames);
if (i !== -1) {
renames.splice(i, 1);
bak = fm.uniqueName(item.name + fm.options.backupSuffix , null, '');
jQuery.each(hashes, function(h, name) {
if (item.name == name) {
hash = h;
return false;
}
});
if (! hash) {
hash = fm.fileByName(item.name, target).hash;
}
fm.lockfiles({files : [hash]});
dfd = fm.request({
data : {cmd : 'rename', target : hash, name : bak},
notify : {type : 'rename', cnt : 1}
})
.fail(function() {
item._remove = true;
fm.sync();
})
.always(function() {
fm.unlockfiles({files : [hash]});
});
dfds.push(dfd);
}
}
return !item._remove? true : false;
});
}
jQuery.when.apply($, dfds).done(function(){
var notifyto, msg,
id = +new Date();
if (items.length > 0) {
msg = fm.escape(items[0].name);
if (items.length > 1) {
msg += ' ... ' + items.length + fm.i18n('items');
}
notifyto = setTimeout(function() {
fm.notify({
type : 'readdir',
id : id,
cnt : 1,
hideCnt: true,
msg : fm.i18n('ntfreaddir') + ' (' + msg + ')',
cancel: function() {
cancel = true;
}
});
}, fm.options.notifyDelay);
doScan(items).done(function() {
notifyto && clearTimeout(notifyto);
fm.notify({type : 'readdir', id: id, cnt : -1});
if (cancel) {
dfrd.reject();
} else {
dfrd.resolve([files, paths, renames, hashes, mkdirs]);
}
}).fail(function() {
dfrd.reject();
});
} else {
dfrd.reject();
}
});
});
return dfrd.promise();
} else {
return dfrd.reject();
}
} else {
var ret = [];
var check = [];
var str = data.files[0];
if (data.type == 'html') {
var tmp = jQuery("").append(jQuery.parseHTML(str.replace(/ src=/ig, ' _elfsrc='))),
atag;
jQuery('img[_elfsrc]', tmp).each(function(){
var url, purl,
self = jQuery(this),
pa = self.closest('a');
if (pa && pa.attr('href') && pa.attr('href').match(/\.(?:jpe?g|gif|bmp|png)/i)) {
purl = pa.attr('href');
}
url = self.attr('_elfsrc');
if (url) {
if (purl) {
jQuery.inArray(purl, ret) == -1 && ret.push(purl);
jQuery.inArray(url, check) == -1 && check.push(url);
} else {
jQuery.inArray(url, ret) == -1 && ret.push(url);
}
}
// Probably it's clipboard data
if (ret.length === 1 && ret[0].match(/^data:image\/png/)) {
data.clipdata = true;
}
});
atag = jQuery('a[href]', tmp);
atag.each(function(){
var text, loc,
parseUrl = function(url) {
var a = document.createElement('a');
a.href = url;
return a;
};
if (text = jQuery(this).text()) {
loc = parseUrl(jQuery(this).attr('href'));
if (loc.href && loc.href.match(/^(?:ht|f)tp/i) && (atag.length === 1 || ! loc.pathname.match(/(?:\.html?|\/[^\/.]*)$/i) || jQuery.trim(text).match(/\.[a-z0-9-]{1,10}$/i))) {
if (jQuery.inArray(loc.href, ret) == -1 && jQuery.inArray(loc.href, check) == -1) ret.push(loc.href);
}
}
});
} else {
var regex, m, url;
regex = /((?:ht|f)tps?:\/\/[-_.!~*\'()a-z0-9;/?:\@&=+\$,%#\*\[\]]+)/ig;
while (m = regex.exec(str)) {
url = m[1].replace(/&/g, '&');
if (jQuery.inArray(url, ret) == -1) ret.push(url);
}
}
return ret;
}
},
// upload transport using XMLHttpRequest
xhr : function(data, fm) {
var self = fm ? fm : this,
node = self.getUI(),
xhr = new XMLHttpRequest(),
notifyto = null,
notifyto1 = null,
notifyto2 = null,
dataChecked = data.checked,
isDataType = (data.isDataType || data.type == 'data'),
target = (data.target || self.cwd().hash),
dropEvt = (data.dropEvt || null),
extraData = data.extraData || null,
chunkEnable = (self.option('uploadMaxConn', target) != -1),
multiMax = Math.min(5, Math.max(1, self.option('uploadMaxConn', target))),
retryWait = 10000, // 10 sec
retryMax = 30, // 10 sec * 30 = 300 secs (Max 5 mins)
retry = 0,
getFile = function(files) {
var dfd = jQuery.Deferred(),
file;
if (files.promise) {
files.always(function(f) {
dfd.resolve(Array.isArray(f) && f.length? (isDataType? f[0][0] : f[0]) : {});
});
} else {
dfd.resolve(files.length? (isDataType? files[0][0] : files[0]) : {});
}
return dfd;
},
dfrd = jQuery.Deferred()
.fail(function(err) {
var error = self.parseError(err),
userAbort;
if (error === 'userabort') {
userAbort = true;
error = void 0;
}
if (files && (self.uploads.xhrUploading || userAbort)) {
// send request om fail
getFile(files).done(function(file) {
if (!userAbort) {
triggerError(error, file);
}
if (! file._cid) {
// send sync request
self.uploads.failSyncTm && clearTimeout(self.uploads.failSyncTm);
self.uploads.failSyncTm = setTimeout(function() {
self.sync(target);
}, 1000);
} else if (! self.uploads.chunkfailReq[file._cid]) {
// send chunkfail request
self.uploads.chunkfailReq[file._cid] = true;
setTimeout(function() {
fm.request({
data : {
cmd: 'upload',
target: target,
chunk: file._chunk,
cid: file._cid,
upload: ['chunkfail'],
mimes: 'chunkfail'
},
options : {
type: 'post',
url: self.uploadURL
},
preventDefault: true
}).always(function() {
delete self.uploads.chunkfailReq[file._chunk];
});
}, 1000);
}
});
} else {
triggerError(error);
}
!userAbort && self.sync();
self.uploads.xhrUploading = false;
files = null;
})
.done(function(data) {
self.uploads.xhrUploading = false;
files = null;
if (data) {
self.currentReqCmd = 'upload';
data.warning && triggerError(data.warning);
self.updateCache(data);
data.removed && data.removed.length && self.remove(data);
data.added && data.added.length && self.add(data);
data.changed && data.changed.length && self.change(data);
self.trigger('upload', data, false);
self.trigger('uploaddone');
if (data.toasts && Array.isArray(data.toasts)) {
jQuery.each(data.toasts, function() {
this.msg && self.toast(this);
});
}
data.sync && self.sync();
if (data.debug) {
self.responseDebug(data);
fm.debug('backend-debug', data);
}
}
})
.always(function() {
self.abortXHR(xhr);
// unregist fnAbort function
node.off('uploadabort', fnAbort);
jQuery(window).off('unload', fnAbort);
notifyto && clearTimeout(notifyto);
notifyto1 && clearTimeout(notifyto1);
notifyto2 && clearTimeout(notifyto2);
dataChecked && !data.multiupload && checkNotify() && self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
notifyto1 && uploadedNtf && self.notify({type : 'chunkmerge', cnt : -cnt});
chunkMerge && notifyElm.children('.elfinder-notify-chunkmerge').length && self.notify({type : 'chunkmerge', cnt : -1});
}),
formData = new FormData(),
files = data.input ? data.input.files : self.uploads.checkFile(data, self, target),
cnt = data.checked? (isDataType? files[0].length : files.length) : files.length,
isChunked = false,
loaded = 0,
prev = 0,
filesize = 0,
notify = false,
notifyElm = self.ui.notify,
cancelBtn = true,
uploadedNtf = false,
abort = false,
checkNotify = function() {
if (!notify && (ntfUpload = notifyElm.children('.elfinder-notify-upload')).length) {
notify = true;
}
return notify;
},
fnAbort = function(e, error) {
abort = true;
self.abortXHR(xhr, { quiet: true, abort: true });
dfrd.reject(error);
if (checkNotify()) {
self.notify({type : 'upload', cnt : ntfUpload.data('cnt') * -1, progress : 0, size : 0});
}
},
cancelToggle = function(show, hasChunk) {
ntfUpload.children('.elfinder-notify-cancel')[show? 'show':'hide']();
cancelBtn = show;
},
startNotify = function(size) {
if (!size) size = filesize;
return setTimeout(function() {
notify = true;
self.notify({type : 'upload', cnt : cnt, progress : loaded - prev, size : size,
cancel: function() {
node.trigger('uploadabort', 'userabort');
}
});
ntfUpload = notifyElm.children('.elfinder-notify-upload');
prev = loaded;
if (data.multiupload) {
cancelBtn && cancelToggle(true);
} else {
cancelToggle(cancelBtn && loaded < size);
}
}, self.options.notifyDelay);
},
doRetry = function() {
if (retry++ <= retryMax) {
if (checkNotify() && prev) {
self.notify({type : 'upload', cnt : 0, progress : 0, size : prev});
}
self.abortXHR(xhr, { quiet: true });
prev = loaded = 0;
setTimeout(function() {
var reqId;
if (! abort) {
xhr.open('POST', self.uploadURL, true);
if (self.api >= 2.1029) {
reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
(typeof formData['delete'] === 'function') && formData['delete']('reqid');
formData.append('reqid', reqId);
xhr._requestId = reqId;
}
xhr.send(formData);
}
}, retryWait);
} else {
node.trigger('uploadabort', ['errAbort', 'errTimeout']);
}
},
progress = function() {
var node;
if (notify) {
dfrd.notifyWith(ntfUpload, [{
cnt: ntfUpload.data('cnt'),
progress: ntfUpload.data('progress'),
total: ntfUpload.data('total')
}]);
}
},
triggerError = function(err, file, unite) {
err && self.trigger('xhruploadfail', { error: err, file: file });
if (unite) {
if (err) {
if (errCnt < self.options.maxErrorDialogs) {
if (Array.isArray(err)) {
errors = errors.concat(err);
} else {
errors.push(err);
}
}
errCnt++;
}
} else {
if (err) {
self.error(err);
} else {
if (errors.length) {
if (errCnt >= self.options.maxErrorDialogs) {
errors = errors.concat('moreErrors', errCnt - self.options.maxErrorDialogs);
}
self.error(errors);
}
errors = [];
errCnt = 0;
}
}
},
errors = [],
errCnt = 0,
renames = (data.renames || null),
hashes = (data.hashes || null),
chunkMerge = false,
ntfUpload = jQuery();
// regist fnAbort function
node.one('uploadabort', fnAbort);
jQuery(window).one('unload.' + fm.namespace, fnAbort);
!chunkMerge && (prev = loaded);
if (!isDataType && !cnt) {
return dfrd.reject(['errUploadNoFiles']);
}
xhr.addEventListener('error', function() {
if (xhr.status == 0) {
if (abort) {
dfrd.reject();
} else {
// ff bug while send zero sized file
// for safari - send directory
if (!isDataType && data.files && jQuery.grep(data.files, function(f){return ! f.type && f.size === (self.UA.Safari? 1802 : 0)? true : false;}).length) {
dfrd.reject(['errAbort', 'errFolderUpload']);
} else if (data.input && jQuery.grep(data.input.files, function(f){return ! f.type && f.size === (self.UA.Safari? 1802 : 0)? true : false;}).length) {
dfrd.reject(['errUploadNoFiles']);
} else {
doRetry();
}
}
} else {
node.trigger('uploadabort', 'errConnect');
}
}, false);
xhr.addEventListener('load', function(e) {
var status = xhr.status, res, curr = 0, error = '', errData, errObj;
self.setCustomHeaderByXhr(xhr);
if (status >= 400) {
if (status > 500) {
error = 'errResponse';
} else {
error = ['errResponse', 'errServerError'];
}
} else {
if (!xhr.responseText) {
error = ['errResponse', 'errDataEmpty'];
}
}
if (error) {
node.trigger('uploadabort');
getFile(files || {}).done(function(file) {
return dfrd.reject(file._cid? null : error);
});
}
loaded = filesize;
if (checkNotify() && (curr = loaded - prev)) {
self.notify({type : 'upload', cnt : 0, progress : curr, size : 0});
progress();
}
res = self.parseUploadData(xhr.responseText);
// chunked upload commit
if (res._chunkmerged) {
formData = new FormData();
var _file = [{_chunkmerged: res._chunkmerged, _name: res._name, _mtime: res._mtime}];
chunkMerge = true;
node.off('uploadabort', fnAbort);
notifyto2 = setTimeout(function() {
self.notify({type : 'chunkmerge', cnt : 1});
}, self.options.notifyDelay);
isDataType? send(_file, files[1]) : send(_file);
return;
}
res._multiupload = data.multiupload? true : false;
if (res.error) {
errData = {
cmd: 'upload',
err: res,
xhr: xhr,
rc: xhr.status
};
self.trigger('uploadfail', res);
// trigger "requestError" event
self.trigger('requestError', errData);
if (errData._getEvent && errData._getEvent().isDefaultPrevented()) {
res.error = '';
}
if (res._chunkfailure || res._multiupload) {
abort = true;
self.uploads.xhrUploading = false;
notifyto && clearTimeout(notifyto);
if (ntfUpload.length) {
self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
dfrd.reject(res);
} else {
// for multi connection
dfrd.reject();
}
} else {
dfrd.reject(res);
}
} else {
dfrd.resolve(res);
}
}, false);
xhr.upload.addEventListener('loadstart', function(e) {
if (!chunkMerge && e.lengthComputable) {
loaded = e.loaded;
retry && (loaded = 0);
filesize = e.total;
if (!loaded) {
loaded = parseInt(filesize * 0.05);
}
if (checkNotify()) {
self.notify({type : 'upload', cnt : 0, progress : loaded - prev, size : data.multiupload? 0 : filesize});
prev = loaded;
progress();
}
}
}, false);
xhr.upload.addEventListener('progress', function(e) {
var curr;
if (e.lengthComputable && !chunkMerge && xhr.readyState < 2) {
loaded = e.loaded;
// to avoid strange bug in safari (not in chrome) with drag&drop.
// bug: macos finder opened in any folder,
// reset safari cache (option+command+e), reload elfinder page,
// drop file from finder
// on first attempt request starts (progress callback called ones) but never ends.
// any next drop - successfull.
if (!data.checked && loaded > 0 && !notifyto) {
notifyto = startNotify(xhr._totalSize - loaded);
}
if (!filesize) {
filesize = e.total;
if (!loaded) {
loaded = parseInt(filesize * 0.05);
}
}
curr = loaded - prev;
if (checkNotify() && (curr/e.total) >= 0.05) {
self.notify({type : 'upload', cnt : 0, progress : curr, size : 0});
prev = loaded;
progress();
}
if (!uploadedNtf && loaded >= filesize && !isChunked) {
// Use "chunkmerge" for "server-in-process" notification
uploadedNtf = true;
notifyto1 = setTimeout(function() {
self.notify({type : 'chunkmerge', cnt : cnt});
}, self.options.notifyDelay);
}
if (cancelBtn && ! data.multiupload && loaded >= filesize) {
checkNotify() && cancelToggle(false);
}
}
}, false);
var send = function(files, paths){
var size = 0,
fcnt = 1,
sfiles = [],
c = 0,
total = cnt,
maxFileSize,
totalSize = 0,
chunked = [],
chunkID = new Date().getTime().toString().substr(-9), // for take care of the 32bit backend system
BYTES_PER_CHUNK = Math.min((fm.uplMaxSize? fm.uplMaxSize : 2097152) - 8190, fm.options.uploadMaxChunkSize), // uplMaxSize margin 8kb or options.uploadMaxChunkSize
blobSlice = chunkEnable? false : '',
blobSize, blobMtime, blobName, i, start, end, chunks, blob, chunk, added, done, last, failChunk,
multi = function(files, num){
var sfiles = [], cid, sfilesLen = 0, cancelChk, hasChunk;
if (!abort) {
while(files.length && sfiles.length < num) {
sfiles.push(files.shift());
}
sfilesLen = sfiles.length;
if (sfilesLen) {
cancelChk = sfilesLen;
for (var i=0; i < sfilesLen; i++) {
if (abort) {
break;
}
cid = isDataType? (sfiles[i][0][0]._cid || null) : (sfiles[i][0]._cid || null);
hasChunk = (hasChunk || cid)? true : false;
if (!!failChunk[cid]) {
last--;
continue;
}
fm.exec('upload', {
type: data.type,
isDataType: isDataType,
files: sfiles[i],
checked: true,
target: target,
dropEvt: dropEvt,
renames: renames,
hashes: hashes,
multiupload: true,
overwrite: data.overwrite === 0? 0 : void 0,
clipdata: data.clipdata
}, void 0, target)
.fail(function(error) {
if (error && error === 'No such command') {
abort = true;
fm.error(['errUpload', 'errPerm']);
}
if (cid) {
failChunk[cid] = true;
}
})
.always(function(e) {
if (e && e.added) added = jQuery.merge(added, e.added);
if (last <= ++done) {
fm.trigger('multiupload', {added: added});
notifyto && clearTimeout(notifyto);
if (checkNotify()) {
self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
}
}
if (files.length) {
multi(files, 1); // Next one
} else {
if (--cancelChk <= 1) {
if (cancelBtn) {
cancelToggle(false, hasChunk);
}
}
dfrd.resolve();
}
});
}
}
}
if (sfiles.length < 1 || abort) {
if (abort) {
notifyto && clearTimeout(notifyto);
if (cid) {
failChunk[cid] = true;
}
dfrd.reject();
} else {
dfrd.resolve();
self.uploads.xhrUploading = false;
}
}
},
check = function(){
if (!self.uploads.xhrUploading) {
self.uploads.xhrUploading = true;
multi(sfiles, multiMax); // Max connection: 3
} else {
setTimeout(check, 100);
}
},
reqId, err;
if (! dataChecked && (isDataType || data.type == 'files')) {
if (! (maxFileSize = fm.option('uploadMaxSize', target))) {
maxFileSize = 0;
}
for (i=0; i < files.length; i++) {
try {
blob = files[i];
blobSize = blob.size;
if (blobSlice === false) {
blobSlice = '';
if (self.api >= 2.1) {
if ('slice' in blob) {
blobSlice = 'slice';
} else if ('mozSlice' in blob) {
blobSlice = 'mozSlice';
} else if ('webkitSlice' in blob) {
blobSlice = 'webkitSlice';
}
}
}
} catch(e) {
cnt--;
total--;
continue;
}
// file size check
if ((maxFileSize && blobSize > maxFileSize) || (!blobSlice && fm.uplMaxSize && blobSize > fm.uplMaxSize)) {
triggerError(['errUploadFile', blob.name, 'errUploadFileSize'], blob, true);
cnt--;
total--;
continue;
}
// file mime check
if (blob.type && ! self.uploadMimeCheck(blob.type, target)) {
triggerError(['errUploadFile', blob.name, 'errUploadMime', '(' + blob.type + ')'], blob, true);
cnt--;
total--;
continue;
}
if (blobSlice && blobSize > BYTES_PER_CHUNK) {
start = 0;
end = BYTES_PER_CHUNK;
chunks = -1;
total = Math.floor((blobSize - 1) / BYTES_PER_CHUNK);
blobMtime = blob.lastModified? Math.round(blob.lastModified/1000) : 0;
blobName = data.clipdata? fm.date(fm.nonameDateFormat) + '.png' : blob.name;
totalSize += blobSize;
chunked[chunkID] = 0;
while(start < blobSize) {
chunk = blob[blobSlice](start, end);
chunk._chunk = blobName + '.' + (++chunks) + '_' + total + '.part';
chunk._cid = chunkID;
chunk._range = start + ',' + chunk.size + ',' + blobSize;
chunk._mtime = blobMtime;
chunked[chunkID]++;
if (size) {
c++;
}
if (typeof sfiles[c] == 'undefined') {
sfiles[c] = [];
if (isDataType) {
sfiles[c][0] = [];
sfiles[c][1] = [];
}
}
size = BYTES_PER_CHUNK;
fcnt = 1;
if (isDataType) {
sfiles[c][0].push(chunk);
sfiles[c][1].push(paths[i]);
} else {
sfiles[c].push(chunk);
}
start = end;
end = start + BYTES_PER_CHUNK;
}
if (chunk == null) {
triggerError(['errUploadFile', blob.name, 'errUploadFileSize'], blob, true);
cnt--;
total--;
} else {
total += chunks;
size = 0;
fcnt = 1;
c++;
}
continue;
}
if ((fm.uplMaxSize && size + blobSize > fm.uplMaxSize) || fcnt > fm.uplMaxFile) {
size = 0;
fcnt = 1;
c++;
}
if (typeof sfiles[c] == 'undefined') {
sfiles[c] = [];
if (isDataType) {
sfiles[c][0] = [];
sfiles[c][1] = [];
}
}
if (isDataType) {
sfiles[c][0].push(blob);
sfiles[c][1].push(paths[i]);
} else {
sfiles[c].push(blob);
}
size += blobSize;
totalSize += blobSize;
fcnt++;
}
if (errors.length) {
triggerError();
}
if (sfiles.length == 0) {
// no data
data.checked = true;
return false;
}
if (sfiles.length > 1) {
// multi upload
notifyto = startNotify(totalSize);
added = [];
done = 0;
last = sfiles.length;
failChunk = [];
check();
return true;
}
// single upload
if (isDataType) {
files = sfiles[0][0];
paths = sfiles[0][1];
} else {
files = sfiles[0];
}
}
if (!dataChecked) {
if (!fm.UA.Safari || !data.files) {
notifyto = startNotify(totalSize);
} else {
xhr._totalSize = totalSize;
}
}
dataChecked = true;
if (! files.length) {
dfrd.reject(['errUploadNoFiles']);
}
xhr.open('POST', self.uploadURL, true);
// set request headers
if (fm.customHeaders) {
jQuery.each(fm.customHeaders, function(key) {
xhr.setRequestHeader(key, this);
});
}
// set xhrFields
if (fm.xhrFields) {
jQuery.each(fm.xhrFields, function(key) {
if (key in xhr) {
xhr[key] = this;
}
});
}
if (self.api >= 2.1029) {
// request ID
reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
formData.append('reqid', reqId);
xhr._requestId = reqId;
}
formData.append('cmd', 'upload');
formData.append(self.newAPI ? 'target' : 'current', target);
if (renames && renames.length) {
jQuery.each(renames, function(i, v) {
formData.append('renames[]', v);
});
formData.append('suffix', fm.options.backupSuffix);
}
if (hashes) {
jQuery.each(hashes, function(i, v) {
formData.append('hashes['+ i +']', v);
});
}
jQuery.each(self.customData, function(key, val) {
formData.append(key, val);
});
jQuery.each(self.options.onlyMimes, function(i, mime) {
formData.append('mimes[]', mime);
});
jQuery.each(files, function(i, file) {
var name, relpath;
if (file._chunkmerged) {
formData.append('chunk', file._chunkmerged);
formData.append('upload[]', file._name);
formData.append('mtime[]', file._mtime);
data.clipdata && formData.append('overwrite', 0);
isChunked = true;
} else {
if (file._chunkfail) {
formData.append('upload[]', 'chunkfail');
formData.append('mimes', 'chunkfail');
} else {
if (data.clipdata) {
if (!file._chunk) {
data.overwrite = 0;
name = fm.date(fm.nonameDateFormat) + '.png';
}
} else {
if (file.name) {
name = file.name;
if (fm.UA.iOS) {
if (name.match(/^image\.jpe?g$/i)) {
data.overwrite = 0;
name = fm.date(fm.nonameDateFormat) + '.jpg';
} else if (name.match(/^capturedvideo\.mov$/i)) {
data.overwrite = 0;
name = fm.date(fm.nonameDateFormat) + '.mov';
}
}
relpath = (file.webkitRelativePath || file.relativePath || file._relativePath || '').replace(/[^\/]+$/, '');
name = relpath + name;
}
}
name? formData.append('upload[]', file, name) : formData.append('upload[]', file);
}
if (file._chunk) {
formData.append('chunk', file._chunk);
formData.append('cid' , file._cid);
formData.append('range', file._range);
formData.append('mtime[]', file._mtime);
isChunked = true;
} else {
formData.append('mtime[]', file.lastModified? Math.round(file.lastModified/1000) : 0);
}
}
});
if (isDataType) {
jQuery.each(paths, function(i, path) {
formData.append('upload_path[]', path);
});
}
if (data.overwrite === 0) {
formData.append('overwrite', 0);
}
// send int value that which meta key was pressed when dropped as `dropWith`
if (dropEvt) {
formData.append('dropWith', parseInt(
(dropEvt.altKey ? '1' : '0')+
(dropEvt.ctrlKey ? '1' : '0')+
(dropEvt.metaKey ? '1' : '0')+
(dropEvt.shiftKey? '1' : '0'), 2));
}
// set extraData on current request
if (extraData) {
jQuery.each(extraData, function(key, val) {
formData.append(key, val);
});
}
xhr.send(formData);
return true;
};
if (! isDataType) {
if (files.length > 0) {
if (! data.clipdata && renames == null) {
var mkdirs = [],
paths = [],
excludes = fm.options.folderUploadExclude[fm.OS] || null;
jQuery.each(files, function(i, file) {
var relPath = file.webkitRelativePath || file.relativePath || '',
idx, rootDir;
if (! relPath) {
return false;
}
if (excludes && file.name.match(excludes)) {
file._remove = true;
relPath = void(0);
} else {
// add '/' as prefix to make same to folder uploading with DnD, see #2607
relPath = '/' + relPath.replace(/\/[^\/]*$/, '').replace(/^\//, '');
if (relPath && jQuery.inArray(relPath, mkdirs) === -1) {
mkdirs.push(relPath);
// checking the root directory to supports
see #2378
idx = relPath.substr(1).indexOf('/');
if (idx !== -1 && (rootDir = relPath.substr(0, idx + 1)) && jQuery.inArray(rootDir, mkdirs) === -1) {
mkdirs.unshift(rootDir);
}
}
}
paths.push(relPath);
});
renames = [];
hashes = {};
if (mkdirs.length) {
(function() {
var checkDirs = jQuery.map(mkdirs, function(name) { return name.substr(1).indexOf('/') === -1 ? {name: name.substr(1)} : null;}),
cancelDirs = [];
fm.uploads.checkExists(checkDirs, target, fm, true).done(
function(res, res2) {
var dfds = [], dfd, bak, hash;
if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
cancelDirs = jQuery.map(checkDirs, function(dir) { return dir._remove? dir.name : null ;} );
checkDirs = jQuery.grep(checkDirs, function(dir) { return !dir._remove? true : false ;} );
}
if (cancelDirs.length) {
jQuery.each(paths.concat(), function(i, path) {
if (jQuery.inArray(path, cancelDirs) === 0) {
files[i]._remove = true;
paths[i] = void(0);
}
});
}
files = jQuery.grep(files, function(file) { return file._remove? false : true; });
paths = jQuery.grep(paths, function(path) { return path === void 0 ? false : true; });
if (checkDirs.length) {
dfd = jQuery.Deferred();
if (res.length) {
jQuery.each(res, function(i, existName) {
// backup
bak = fm.uniqueName(existName + fm.options.backupSuffix , null, '');
jQuery.each(res2, function(h, name) {
if (res[0] == name) {
hash = h;
return false;
}
});
if (! hash) {
hash = fm.fileByName(res[0], target).hash;
}
fm.lockfiles({files : [hash]});
dfds.push(
fm.request({
data : {cmd : 'rename', target : hash, name : bak},
notify : {type : 'rename', cnt : 1}
})
.fail(function(error) {
dfrd.reject(error);
fm.sync();
})
.always(function() {
fm.unlockfiles({files : [hash]});
})
);
});
} else {
dfds.push(null);
}
jQuery.when.apply($, dfds).done(function() {
// ensure directories
fm.request({
data : {cmd : 'mkdir', target : target, dirs : mkdirs},
notify : {type : 'mkdir', cnt : mkdirs.length},
preventFail: true
})
.fail(function(error) {
error = error || ['errUnknown'];
if (error[0] === 'errCmdParams') {
multiMax = 1;
} else {
multiMax = 0;
dfrd.reject(error);
}
})
.done(function(data) {
var rm = false;
if (!data.hashes) {
data.hashes = {};
}
paths = jQuery.map(paths.concat(), function(p, i) {
if (p === '/') {
return target;
} else {
if (data.hashes[p]) {
return data.hashes[p];
} else {
rm = true;
files[i]._remove = true;
return null;
}
}
});
if (rm) {
files = jQuery.grep(files, function(file) { return file._remove? false : true; });
}
})
.always(function(data) {
if (multiMax) {
isDataType = true;
if (! send(files, paths)) {
dfrd.reject();
}
}
});
});
} else {
dfrd.reject();
}
}
);
})();
} else {
fm.uploads.checkExists(files, target, fm).done(
function(res, res2){
if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
hashes = res2;
if (res === null) {
data.overwrite = 0;
} else {
renames = res;
}
files = jQuery.grep(files, function(file){return !file._remove? true : false ;});
}
cnt = files.length;
if (cnt > 0) {
if (! send(files)) {
dfrd.reject();
}
} else {
dfrd.reject();
}
}
);
}
} else {
if (! send(files)) {
dfrd.reject();
}
}
} else {
dfrd.reject();
}
} else {
if (dataChecked) {
send(files[0], files[1]);
} else {
files.done(function(result) { // result: [files, paths, renames, hashes, mkdirs]
renames = [];
cnt = result[0].length;
if (cnt) {
if (result[4] && result[4].length) {
// ensure directories
fm.request({
data : {cmd : 'mkdir', target : target, dirs : result[4]},
notify : {type : 'mkdir', cnt : result[4].length},
preventFail: true
})
.fail(function(error) {
error = error || ['errUnknown'];
if (error[0] === 'errCmdParams') {
multiMax = 1;
} else {
multiMax = 0;
dfrd.reject(error);
}
})
.done(function(data) {
var rm = false;
if (!data.hashes) {
data.hashes = {};
}
result[1] = jQuery.map(result[1], function(p, i) {
result[0][i]._relativePath = p.replace(/^\//, '');
p = p.replace(/\/[^\/]*$/, '');
if (p === '') {
return target;
} else {
if (data.hashes[p]) {
return data.hashes[p];
} else {
rm = true;
result[0][i]._remove = true;
return null;
}
}
});
if (rm) {
result[0] = jQuery.grep(result[0], function(file) { return file._remove? false : true; });
}
})
.always(function(data) {
if (multiMax) {
renames = result[2];
hashes = result[3];
send(result[0], result[1]);
}
});
return;
} else {
result[1] = jQuery.map(result[1], function() { return target; });
}
renames = result[2];
hashes = result[3];
send(result[0], result[1]);
} else {
dfrd.reject(['errUploadNoFiles']);
}
}).fail(function(){
dfrd.reject();
});
}
}
return dfrd;
},
// upload transport using iframe
iframe : function(data, fm) {
var self = fm ? fm : this,
input = data.input? data.input : false,
files = !input ? self.uploads.checkFile(data, self) : false,
dfrd = jQuery.Deferred()
.fail(function(error) {
error && self.error(error);
}),
name = 'iframe-'+fm.namespace+(++self.iframeCnt),
form = jQuery('
'),
msie = this.UA.IE,
// clear timeouts, close notification dialog, remove form/iframe
onload = function() {
abortto && clearTimeout(abortto);
notifyto && clearTimeout(notifyto);
notify && self.notify({type : 'upload', cnt : -cnt});
setTimeout(function() {
msie && jQuery('
').appendTo(form);
form.remove();
iframe.remove();
}, 100);
},
iframe = jQuery('
')
.on('load', function() {
iframe.off('load')
.on('load', function() {
onload();
// data will be processed in callback response or window onmessage
dfrd.resolve();
});
// notify dialog
notifyto = setTimeout(function() {
notify = true;
self.notify({type : 'upload', cnt : cnt});
}, self.options.notifyDelay);
// emulate abort on timeout
if (self.options.iframeTimeout > 0) {
abortto = setTimeout(function() {
onload();
dfrd.reject(['errConnect', 'errTimeout']);
}, self.options.iframeTimeout);
}
form.submit();
}),
target = (data.target || self.cwd().hash),
names = [],
dfds = [],
renames = [],
hashes = {},
cnt, notify, notifyto, abortto;
if (files && files.length) {
jQuery.each(files, function(i, val) {
form.append('
');
});
cnt = 1;
} else if (input && jQuery(input).is(':file') && jQuery(input).val()) {
if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
names = input.files? input.files : [{ name: jQuery(input).val().replace(/^(?:.+[\\\/])?([^\\\/]+)$/, '$1') }];
//names = jQuery.map(names, function(file){return file.name? { name: file.name } : null ;});
dfds.push(self.uploads.checkExists(names, target, self).done(
function(res, res2){
hashes = res2;
if (res === null) {
data.overwrite = 0;
} else{
renames = res;
cnt = jQuery.grep(names, function(file){return !file._remove? true : false ;}).length;
if (cnt != names.length) {
cnt = 0;
}
}
}
));
}
cnt = input.files ? input.files.length : 1;
form.append(input);
} else {
return dfrd.reject();
}
jQuery.when.apply($, dfds).done(function() {
if (cnt < 1) {
return dfrd.reject();
}
form.append('
')
.append('
')
.append('
')
.append(jQuery(input).attr('name', 'upload[]'));
if (renames.length > 0) {
jQuery.each(renames, function(i, rename) {
form.append('
');
});
form.append('
');
}
if (hashes) {
jQuery.each(renames, function(i, v) {
form.append('
');
});
}
if (data.overwrite === 0) {
form.append('
');
}
jQuery.each(self.options.onlyMimes||[], function(i, mime) {
form.append('
');
});
jQuery.each(self.customData, function(key, val) {
form.append('
');
});
form.appendTo('body');
iframe.appendTo('body');
});
return dfrd;
}
},
/**
* Bind callback to event(s) The callback is executed at most once per event.
* To bind to multiply events at once, separate events names by space
*
* @param String event name
* @param Function callback
* @param Boolan priority first
* @return elFinder
*/
one : function(ev, callback, priorityFirst) {
var self = this,
event = ev.toLowerCase(),
h = function(e, f) {
if (!self.toUnbindEvents[event]) {
self.toUnbindEvents[event] = [];
}
self.toUnbindEvents[event].push({
type: event,
callback: h
});
return (callback.done? callback.done : callback).apply(this, arguments);
};
if (callback.done) {
h = {done: h};
}
return this.bind(event, h, priorityFirst);
},
/**
* Set/get data into/from localStorage
*
* @param String key
* @param String|void value
* @return String|null
*/
localStorage : function(key, val) {
var self = this,
s = window.localStorage,
oldkey = 'elfinder-'+(key || '')+this.id, // old key of elFinder < 2.1.6
prefix = window.location.pathname+'-elfinder-',
suffix = this.id,
clrs = [],
retval, oldval, t, precnt, sufcnt;
// reset this node data
if (typeof(key) === 'undefined') {
precnt = prefix.length;
sufcnt = suffix.length * -1;
jQuery.each(s, function(key) {
if (key.substr(0, precnt) === prefix && key.substr(sufcnt) === suffix) {
clrs.push(key);
}
});
jQuery.each(clrs, function(i, key) {
s.removeItem(key);
});
return true;
}
// new key of elFinder >= 2.1.6
key = prefix+key+suffix;
if (val === null) {
return s.removeItem(key);
}
if (val === void(0) && !(retval = s.getItem(key)) && (oldval = s.getItem(oldkey))) {
val = oldval;
s.removeItem(oldkey);
}
if (val !== void(0)) {
t = typeof val;
if (t !== 'string' && t !== 'number') {
val = JSON.stringify(val);
}
try {
s.setItem(key, val);
} catch (e) {
try {
s.clear();
s.setItem(key, val);
} catch (e) {
self.debug('error', e.toString());
}
}
retval = s.getItem(key);
}
if (retval && (retval.substr(0,1) === '{' || retval.substr(0,1) === '[')) {
try {
return JSON.parse(retval);
} catch(e) {}
}
return retval;
},
/**
* Set/get data into/from sessionStorage
*
* @param String key
* @param String|void value
* @return String|null
*/
sessionStorage : function(key, val) {
var self = this,
s, retval, t;
try {
s = window.sessionStorage;
} catch(e) {}
if (!s) {
return;
}
if (val === null) {
return s.removeItem(key);
}
if (val !== void(0)) {
t = typeof val;
if (t !== 'string' && t !== 'number') {
val = JSON.stringify(val);
}
try {
s.setItem(key, val);
} catch (e) {
try {
s.clear();
s.setItem(key, val);
} catch (e) {
self.debug('error', e.toString());
}
}
}
retval = s.getItem(key);
if (retval && (retval.substr(0,1) === '{' || retval.substr(0,1) === '[')) {
try {
return JSON.parse(retval);
} catch(e) {}
}
return retval;
},
/**
* Get/set cookie
*
* @param String cookie name
* @param String|void cookie value
* @return String|null
*/
cookie : function(name, value) {
var d, o, c, i, retval, t;
name = 'elfinder-'+name+this.id;
if (value === void(0)) {
if (this.cookieEnabled && document.cookie && document.cookie != '') {
c = document.cookie.split(';');
name += '=';
for (i=0; i
'),
/**
* Replace not html-safe symbols to html entities
*
* @param String text to escape
* @return String
*/
escape : function(name) {
return this._node.text(name).html().replace(/"/g, '"').replace(/'/g, ''');
},
/**
* Cleanup ajax data.
* For old api convert data into new api format
*
* @param String command name
* @param Object data from backend
* @return Object
*/
normalize : function(data) {
var self = this,
fileFilter = (function() {
var func, filter;
if (filter = self.options.fileFilter) {
if (typeof filter === 'function') {
func = function(file) {
return filter.call(self, file);
};
} else if (filter instanceof RegExp) {
func = function(file) {
return filter.test(file.name);
};
}
}
return func? func : null;
})(),
chkCmdMap = function(opts) {
// Disable command to replace with other command
var disabled;
if (opts.uiCmdMap) {
if (jQuery.isPlainObject(opts.uiCmdMap) && Object.keys(opts.uiCmdMap).length) {
if (!opts.disabledFlip) {
opts.disabledFlip = {};
}
disabled = opts.disabledFlip;
jQuery.each(opts.uiCmdMap, function(f, t) {
if (t === 'hidden' && !disabled[f]) {
opts.disabled.push(f);
opts.disabledFlip[f] = true;
}
});
} else {
delete opts.uiCmdMap;
}
}
},
normalizeOptions = function(opts) {
var getType = function(v) {
var type = typeof v;
if (type === 'object' && Array.isArray(v)) {
type = 'array';
}
return type;
};
jQuery.each(self.optionProperties, function(k, empty) {
if (empty !== void(0)) {
if (opts[k] && getType(opts[k]) !== getType(empty)) {
opts[k] = empty;
}
}
});
if (opts.disabled) {
opts.disabledFlip = self.arrayFlip(opts.disabled, true);
jQuery.each(self.options.disabledCmdsRels, function(com, rels) {
var m, flg;
if (opts.disabledFlip[com]) {
flg = true;
} else if (m = com.match(/^([^&]+)&([^=]+)=(.*)$/)) {
if (opts.disabledFlip[m[1]] && opts[m[2]] == m[3]) {
flg = true;
}
}
if (flg) {
jQuery.each(rels, function(i, rel) {
if (!opts.disabledFlip[rel]) {
opts.disabledFlip[rel] = true;
opts.disabled.push(rel);
}
});
}
});
} else {
opts.disabledFlip = {};
}
return opts;
},
filter = function(file, asMap, type) {
var res = asMap? file : true,
ign = asMap? null : false,
vid, targetOptions, isRoot, rootNames;
if (file && file.hash && file.name && file.mime) {
if (file.mime === 'application/x-empty') {
file.mime = 'text/plain';
}
isRoot = self.isRoot(file);
if (isRoot && ! file.volumeid) {
self.debug('warning', 'The volume root statuses requires `volumeid` property.');
}
if (isRoot || file.mime === 'directory') {
// Prevention of circular reference
if (file.phash) {
if (file.phash === file.hash) {
error = error.concat(['Parent folder of "$1" is itself.', file.name]);
return ign;
}
if (isRoot && file.volumeid && file.phash.indexOf(file.volumeid) === 0) {
error = error.concat(['Parent folder of "$1" is inner itself.', file.name]);
return ign;
}
}
// set options, tmbUrls for each volume
if (file.volumeid) {
vid = file.volumeid;
if (isRoot) {
// make or update of leaf roots cache
if (file.phash) {
if (! self.leafRoots[file.phash]) {
self.leafRoots[file.phash] = [ file.hash ];
} else {
if (jQuery.inArray(file.hash, self.leafRoots[file.phash]) === -1) {
self.leafRoots[file.phash].push(file.hash);
}
}
}
self.hasVolOptions = true;
if (! self.volOptions[vid]) {
self.volOptions[vid] = {
// set dispInlineRegex
dispInlineRegex: self.options.dispInlineRegex
};
}
targetOptions = self.volOptions[vid];
if (file.options) {
// >= v.2.1.14 has file.options
Object.assign(targetOptions, file.options);
}
// for compat <= v2.1.13
if (file.disabled) {
targetOptions.disabled = file.disabled;
targetOptions.disabledFlip = self.arrayFlip(file.disabled, true);
}
if (file.tmbUrl) {
targetOptions.tmbUrl = file.tmbUrl;
}
// '/' required at the end of url
if (targetOptions.url && targetOptions.url.substr(-1) !== '/') {
targetOptions.url += '/';
}
// check uiCmdMap
chkCmdMap(targetOptions);
// check trash bin hash
if (targetOptions.trashHash) {
if (self.trashes[targetOptions.trashHash] === false) {
delete targetOptions.trashHash;
} else {
self.trashes[targetOptions.trashHash] = file.hash;
}
}
// set immediate properties
jQuery.each(self.optionProperties, function(k) {
if (targetOptions[k]) {
file[k] = targetOptions[k];
}
});
// regist fm.roots
if (type !== 'cwd') {
self.roots[vid] = file.hash;
}
// regist fm.volumeExpires
if (file.expires) {
self.volumeExpires[vid] = file.expires;
}
}
if (prevId !== vid) {
prevId = vid;
i18nFolderName = self.option('i18nFolderName', vid);
}
}
// volume root i18n name
if (isRoot && ! file.i18) {
name = 'volume_' + file.name,
i18 = self.i18n(false, name);
if (name !== i18) {
file.i18 = i18;
}
}
// i18nFolderName
if (i18nFolderName && ! file.i18) {
name = 'folder_' + file.name,
i18 = self.i18n(false, name);
if (name !== i18) {
file.i18 = i18;
}
}
if (isRoot) {
if (rootNames = self.storage('rootNames')) {
if (rootNames[file.hash]) {
file._name = file.name;
file._i18 = file.i18;
file.name = rootNames[file.hash] = rootNames[file.hash];
delete file.i18;
}
self.storage('rootNames', rootNames);
}
}
// lock trash bins holder
if (self.trashes[file.hash]) {
file.locked = true;
}
} else {
if (fileFilter) {
try {
if (! fileFilter(file)) {
return ign;
}
} catch(e) {
self.debug(e);
}
}
if (file.size == 0) {
file.mime = self.getMimetype(file.name, file.mime);
}
}
if (file.options) {
self.optionsByHashes[file.hash] = normalizeOptions(file.options);
}
delete file.options;
return res;
}
return ign;
},
getDescendants = function(hashes) {
var res = [];
jQuery.each(self.files(), function(h, f) {
jQuery.each(self.parents(h), function(i, ph) {
if (jQuery.inArray(ph, hashes) !== -1 && jQuery.inArray(h, hashes) === -1) {
res.push(h);
return false;
}
});
});
return res;
},
applyLeafRootStats = function(dataArr, type) {
jQuery.each(dataArr, function(i, f) {
var pfile, done;
if (self.leafRoots[f.hash]) {
self.applyLeafRootStats(f);
}
// update leaf root parent stat
if (type !== 'change' && f.phash && self.isRoot(f) && (pfile = self.file(f.phash))) {
self.applyLeafRootStats(pfile);
// add to data.changed
if (!data.changed) {
data.changed = [pfile];
} else {
jQuery.each(data.changed, function(i, f) {
if (f.hash === pfile.hash) {
data.changed[i] = pfile;
done = true;
return false;
}
});
if (!done) {
data.changed.push(pfile);
}
}
}
});
},
error = [],
name, i18, i18nFolderName, prevId, cData;
// set cunstom data
if (data.customData && (!self.prevCustomData || (JSON.stringify(data.customData) !== JSON.stringify(self.prevCustomData)))) {
self.prevCustomData = data.customData;
try {
cData = JSON.parse(data.customData);
if (jQuery.isPlainObject(cData)) {
self.prevCustomData = cData;
jQuery.each(Object.keys(cData), function(i, key) {
if (cData[key] === null) {
delete cData[key];
delete self.optsCustomData[key];
}
});
self.customData = Object.assign({}, self.optsCustomData, cData);
}
} catch(e) {}
}
if (data.options) {
normalizeOptions(data.options);
}
if (data.cwd) {
if (data.cwd.volumeid && data.options && Object.keys(data.options).length && self.isRoot(data.cwd)) {
self.hasVolOptions = true;
self.volOptions[data.cwd.volumeid] = data.options;
}
data.cwd = filter(data.cwd, true, 'cwd');
}
if (data.files) {
data.files = jQuery.grep(data.files, filter);
}
if (data.tree) {
data.tree = jQuery.grep(data.tree, filter);
}
if (data.added) {
data.added = jQuery.grep(data.added, filter);
}
if (data.changed) {
data.changed = jQuery.grep(data.changed, filter);
}
if (data.removed && data.removed.length && self.searchStatus.state === 2) {
data.removed = data.removed.concat(getDescendants(data.removed));
}
if (data.api) {
data.init = true;
}
if (Object.keys(self.leafRoots).length) {
data.files && applyLeafRootStats(data.files);
data.tree && applyLeafRootStats(data.tree);
data.added && applyLeafRootStats(data.added);
data.changed && applyLeafRootStats(data.changed, 'change');
}
// merge options that apply only to cwd
if (data.cwd && data.cwd.options && data.options) {
Object.assign(data.options, normalizeOptions(data.cwd.options));
}
// '/' required at the end of url
if (data.options && data.options.url && data.options.url.substr(-1) !== '/') {
data.options.url += '/';
}
// check error
if (error.length) {
data.norError = ['errResponse'].concat(error);
}
return data;
},
/**
* Update sort options
*
* @param {String} sort type
* @param {String} sort order
* @param {Boolean} show folder first
*/
setSort : function(type, order, stickFolders, alsoTreeview) {
this.storage('sortType', (this.sortType = this.sortRules[type] ? type : 'name'));
this.storage('sortOrder', (this.sortOrder = /asc|desc/.test(order) ? order : 'asc'));
this.storage('sortStickFolders', (this.sortStickFolders = !!stickFolders) ? 1 : '');
this.storage('sortAlsoTreeview', (this.sortAlsoTreeview = !!alsoTreeview) ? 1 : '');
this.trigger('sortchange');
},
_sortRules : {
name : function(file1, file2) {
return elFinder.prototype.naturalCompare(file1.i18 || file1.name, file2.i18 || file2.name);
},
size : function(file1, file2) {
var size1 = parseInt(file1.size) || 0,
size2 = parseInt(file2.size) || 0;
return size1 === size2 ? 0 : size1 > size2 ? 1 : -1;
},
kind : function(file1, file2) {
return elFinder.prototype.naturalCompare(file1.mime, file2.mime);
},
date : function(file1, file2) {
var date1 = file1.ts || file1.date || 0,
date2 = file2.ts || file2.date || 0;
return date1 === date2 ? 0 : date1 > date2 ? 1 : -1;
},
perm : function(file1, file2) {
var val = function(file) { return (file.write? 2 : 0) + (file.read? 1 : 0); },
v1 = val(file1),
v2 = val(file2);
return v1 === v2 ? 0 : v1 > v2 ? 1 : -1;
},
mode : function(file1, file2) {
var v1 = file1.mode || (file1.perm || ''),
v2 = file2.mode || (file2.perm || '');
return elFinder.prototype.naturalCompare(v1, v2);
},
owner : function(file1, file2) {
var v1 = file1.owner || '',
v2 = file2.owner || '';
return elFinder.prototype.naturalCompare(v1, v2);
},
group : function(file1, file2) {
var v1 = file1.group || '',
v2 = file2.group || '';
return elFinder.prototype.naturalCompare(v1, v2);
}
},
/**
* Valid sort rule names
*
* @type Object
*/
sorters : {},
/**
* Compare strings for natural sort
*
* @param String
* @param String
* @return Number
*/
naturalCompare : function(a, b) {
var self = elFinder.prototype.naturalCompare;
if (typeof self.loc == 'undefined') {
self.loc = (navigator.userLanguage || navigator.browserLanguage || navigator.language || 'en-US');
}
if (typeof self.sort == 'undefined') {
if ('11'.localeCompare('2', self.loc, {numeric: true}) > 0) {
// Native support
if (window.Intl && window.Intl.Collator) {
self.sort = new Intl.Collator(self.loc, {numeric: true}).compare;
} else {
self.sort = function(a, b) {
return a.localeCompare(b, self.loc, {numeric: true});
};
}
} else {
/*
* Edited for elFinder (emulates localeCompare() by numeric) by Naoki Sawada aka nao-pon
*/
/*
* Huddle/javascript-natural-sort (https://github.com/Huddle/javascript-natural-sort)
*/
/*
* Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license
* Author: Jim Palmer (based on chunking idea from Dave Koelle)
* http://opensource.org/licenses/mit-license.php
*/
self.sort = function(a, b) {
var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
sre = /(^[ ]*|[ ]*$)/g,
dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
hre = /^0x[0-9a-f]+$/i,
ore = /^0/,
syre = /^[\x01\x21-\x2f\x3a-\x40\x5b-\x60\x7b-\x7e]/, // symbol first - (Naoki Sawada)
i = function(s) { return self.sort.insensitive && (''+s).toLowerCase() || ''+s; },
// convert all to strings strip whitespace
// first character is "_", it's smallest - (Naoki Sawada)
x = i(a).replace(sre, '').replace(/^_/, "\x01") || '',
y = i(b).replace(sre, '').replace(/^_/, "\x01") || '',
// chunk/tokenize
xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
// numeric, hex or date detection
xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
oFxNcL, oFyNcL,
locRes = 0;
// first try and sort Hex codes or Dates
if (yD) {
if ( xD < yD ) return -1;
else if ( xD > yD ) return 1;
}
// natural sorting through split numeric strings and default strings
for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
// find floats not starting with '0', string or 0 if not defined (Clint Priest)
oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
// handle numeric vs string comparison - number < string - (Kyle Adams)
// but symbol first < number - (Naoki Sawada)
if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
if (isNaN(oFxNcL) && (typeof oFxNcL !== 'string' || ! oFxNcL.match(syre))) {
return 1;
} else if (typeof oFyNcL !== 'string' || ! oFyNcL.match(syre)) {
return -1;
}
}
// use decimal number comparison if either value is string zero
if (parseInt(oFxNcL, 10) === 0) oFxNcL = 0;
if (parseInt(oFyNcL, 10) === 0) oFyNcL = 0;
// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
if (typeof oFxNcL !== typeof oFyNcL) {
oFxNcL += '';
oFyNcL += '';
}
// use locale sensitive sort for strings when case insensitive
// note: localeCompare interleaves uppercase with lowercase (e.g. A,a,B,b)
if (self.sort.insensitive && typeof oFxNcL === 'string' && typeof oFyNcL === 'string') {
locRes = oFxNcL.localeCompare(oFyNcL, self.loc);
if (locRes !== 0) return locRes;
}
if (oFxNcL < oFyNcL) return -1;
if (oFxNcL > oFyNcL) return 1;
}
return 0;
};
self.sort.insensitive = true;
}
}
return self.sort(a, b);
},
/**
* Compare files based on elFinder.sort
*
* @param Object file
* @param Object file
* @return Number
*/
compare : function(file1, file2) {
var self = this,
type = self.sortType,
asc = self.sortOrder == 'asc',
stick = self.sortStickFolders,
rules = self.sortRules,
sort = rules[type],
d1 = file1.mime == 'directory',
d2 = file2.mime == 'directory',
res;
if (stick) {
if (d1 && !d2) {
return -1;
} else if (!d1 && d2) {
return 1;
}
}
res = asc ? sort(file1, file2) : sort(file2, file1);
return type !== 'name' && res === 0
? res = asc ? rules.name(file1, file2) : rules.name(file2, file1)
: res;
},
/**
* Sort files based on config
*
* @param Array files
* @return Array
*/
sortFiles : function(files) {
return files.sort(this.compare);
},
/**
* Open notification dialog
* and append/update message for required notification type.
*
* @param Object options
* @example
* this.notify({
* type : 'copy',
* msg : 'Copy files', // not required for known types @see this.notifyType
* cnt : 3,
* hideCnt : false, // true for not show count
* progress : 10, // progress bar percents (use cnt : 0 to update progress bar)
* cancel : callback // callback function for cancel button
* })
* @return elFinder
*/
notify : function(opts) {
var self = this,
type = opts.type,
id = opts.id? 'elfinder-notify-'+opts.id : '',
msg = this.i18n((typeof opts.msg !== 'undefined')? opts.msg : (this.messages['ntf'+type] ? 'ntf'+type : 'ntfsmth')),
hiddens = this.arrayFlip(this.options.notifyDialog.hiddens || []),
ndialog = this.ui.notify,
dialog = ndialog.closest('.ui-dialog'),
notify = ndialog.children('.elfinder-notify-'+type+(id? ('.'+id) : '')),
button = notify.children('div.elfinder-notify-cancel').children('button'),
ntpl = '
',
delta = opts.cnt + 0,
size = (typeof opts.size != 'undefined')? parseInt(opts.size) : null,
progress = (typeof opts.progress != 'undefined' && opts.progress >= 0) ? opts.progress : null,
fakeint = opts.fakeinterval || 200,
cancel = opts.cancel,
clhover = 'ui-state-hover',
close = function() {
var prog = notify.find('.elfinder-notify-progress'),
rm = function() {
notify.remove();
if (!ndialog.children(dialog.data('minimized')? void(0) : ':visible').length) {
if (dialog.data('minimized')) {
dialog.data('minimized').hide();
} else {
ndialog.elfinderdialog('close');
}
}
setProgressbar();
};
notify._esc && jQuery(document).off('keydown', notify._esc);
if (notify.data('cur') < 100) {
prog.animate({
width : '100%'
}, 50, function() { requestAnimationFrame(function() { rm(); }); });
} else {
rm();
}
},
fakeUp = function(interval) {
var cur;
if (notify.length) {
cur = notify.data('cur') + 1;
if (cur <= 98) {
notify.find('.elfinder-notify-progress').width(cur + '%');
notify.data('cur', cur);
setProgressbar();
setTimeout(function() {
interval *= 1.05;
fakeUp(interval);
}, interval);
}
}
},
setProgressbar = function() {
var cnt = 0,
val = 0,
ntfs = ndialog.children('.elfinder-notify'),
w;
if (ntfs.length) {
ntfs.each(function() {
cnt++;
val += Math.min(jQuery(this).data('cur'), 100);
});
w = cnt? Math.floor(val / (cnt * 100) * 100) + '%' : 0;
self.ui.progressbar.width(w);
if (dialog.data('minimized')) {
dialog.data('minimized').title(w);
dialog.data('minimized').dialog().children('.ui-dialog-titlebar').children('.elfinder-ui-progressbar').width(w);
}
} else {
self.ui.progressbar.width(0);
dialog.data('minimized') && dialog.data('minimized').hide();
}
},
cnt, total, prc;
if (!type) {
return this;
}
if (!notify.length) {
notify = jQuery(ntpl.replace(/\{type\}/g, type).replace(/\{msg\}/g, msg));
if (hiddens[type]) {
notify.hide();
} else {
ndialog.on('minimize', function(e) {
dialog.data('minimized') && setProgressbar();
});
}
notify.appendTo(ndialog).data('cnt', 0);
if (progress != null) {
notify.data({progress : 0, total : 0, cur : 0});
} else {
notify.data({cur : 0});
fakeUp(fakeint);
}
if (cancel) {
button = jQuery('
')
.on('mouseenter mouseleave', function(e) {
jQuery(this).toggleClass(clhover, e.type === 'mouseenter');
});
notify.children('div.elfinder-notify-cancel').append(button);
}
ndialog.trigger('resize');
} else if (typeof opts.msg !== 'undefined') {
notify.children('span.elfinder-notify-msg').html(msg);
}
cnt = delta + parseInt(notify.data('cnt'));
if (cnt > 0) {
if (cancel && button.length) {
if (jQuery.isFunction(cancel) || (typeof cancel === 'object' && cancel.promise)) {
notify._esc = function(e) {
if (e.type == 'keydown' && e.keyCode != jQuery.ui.keyCode.ESCAPE) {
return;
}
e.preventDefault();
e.stopPropagation();
close();
if (cancel.promise) {
cancel.reject(0); // 0 is canceling flag
} else {
cancel(e);
}
};
button.on('click', function(e) {
notify._esc(e);
});
jQuery(document).on('keydown.' + this.namespace, notify._esc);
}
}
!opts.hideCnt && notify.children('.elfinder-notify-cnt').text('('+cnt+')');
if (delta > 0 && ndialog.is(':hidden') && !hiddens[type]) {
if (dialog.data('minimized')) {
dialog.data('minimized').show();
} else {
ndialog.elfinderdialog('open', this).height('auto');
}
}
notify.data('cnt', cnt);
if ((progress != null)
&& (total = notify.data('total')) >= 0
&& (prc = notify.data('progress')) >= 0) {
total += size != null? size : delta;
prc += progress;
(size == null && delta < 0) && (prc += delta * 100);
notify.data({progress : prc, total : total});
if (size != null) {
prc *= 100;
total = Math.max(1, total);
}
progress = Math.min(parseInt(prc/total), 100);
notify.find('.elfinder-notify-progress')
.animate({
width : (progress < 100 ? progress : 100)+'%'
}, 20, function() {
notify.data('cur', progress);
setProgressbar();
});
}
} else {
close();
}
return this;
},
/**
* Open confirmation dialog
*
* @param Object options
* @example
* this.confirm({
* cssClass : 'elfinder-confirm-mydialog',
* title : 'Remove files',
* text : 'Here is question text',
* accept : { // accept callback - required
* label : 'Continue',
* callback : function(applyToAll) { fm.log('Ok') }
* },
* cancel : { // cancel callback - required
* label : 'Cancel',
* callback : function() { fm.log('Cancel')}
* },
* reject : { // reject callback - optionally
* label : 'No',
* callback : function(applyToAll) { fm.log('No')}
* },
* buttons : [ // additional buttons callback - optionally
* {
* label : 'Btn1',
* callback : function(applyToAll) { fm.log('Btn1')}
* }
* ],
* all : true // display checkbox "Apply to all"
* })
* @return elFinder
*/
confirm : function(opts) {
var self = this,
complete = false,
options = {
cssClass : 'elfinder-dialog-confirm',
modal : true,
resizable : false,
title : this.i18n(opts.title || 'confirmReq'),
buttons : {},
close : function() {
!complete && opts.cancel.callback();
jQuery(this).elfinderdialog('destroy');
}
},
apply = this.i18n('apllyAll'),
label, checkbox, btnNum;
if (opts.cssClass) {
options.cssClass += ' ' + opts.cssClass;
}
options.buttons[this.i18n(opts.accept.label)] = function() {
opts.accept.callback(!!(checkbox && checkbox.prop('checked')));
complete = true;
jQuery(this).elfinderdialog('close');
};
options.buttons[this.i18n(opts.accept.label)]._cssClass = 'elfinder-confirm-accept';
if (opts.reject) {
options.buttons[this.i18n(opts.reject.label)] = function() {
opts.reject.callback(!!(checkbox && checkbox.prop('checked')));
complete = true;
jQuery(this).elfinderdialog('close');
};
options.buttons[this.i18n(opts.reject.label)]._cssClass = 'elfinder-confirm-reject';
}
if (opts.buttons && opts.buttons.length > 0) {
btnNum = 1;
jQuery.each(opts.buttons, function(i, v){
options.buttons[self.i18n(v.label)] = function() {
v.callback(!!(checkbox && checkbox.prop('checked')));
complete = true;
jQuery(this).elfinderdialog('close');
};
options.buttons[self.i18n(v.label)]._cssClass = 'elfinder-confirm-extbtn' + (btnNum++);
if (v.cssClass) {
options.buttons[self.i18n(v.label)]._cssClass += ' ' + v.cssClass;
}
});
}
options.buttons[this.i18n(opts.cancel.label)] = function() {
jQuery(this).elfinderdialog('close');
};
options.buttons[this.i18n(opts.cancel.label)]._cssClass = 'elfinder-confirm-cancel';
if (opts.all) {
options.create = function() {
var base = jQuery('
');
checkbox = jQuery('
');
jQuery(this).next().find('.ui-dialog-buttonset')
.prepend(base.append(jQuery('
').prepend(checkbox)));
};
}
if (opts.optionsCallback && jQuery.isFunction(opts.optionsCallback)) {
opts.optionsCallback(options);
}
return this.dialog('
' + this.i18n(opts.text), options);
},
/**
* Create unique file name in required dir
*
* @param String file name
* @param String parent dir hash
* @param String glue
* @return String
*/
uniqueName : function(prefix, phash, glue) {
var i = 0, ext = '', p, name;
prefix = this.i18n(false, prefix);
phash = phash || this.cwd().hash;
glue = (typeof glue === 'undefined')? ' ' : glue;
if (p = prefix.match(/^(.+)(\.[^.]+)$/)) {
ext = p[2];
prefix = p[1];
}
name = prefix+ext;
if (!this.fileByName(name, phash)) {
return name;
}
while (i < 10000) {
name = prefix + glue + (++i) + ext;
if (!this.fileByName(name, phash)) {
return name;
}
}
return prefix + Math.random() + ext;
},
/**
* Return message translated onto current language
* Allowed accept HTML element that was wrapped in jQuery object
* To be careful to XSS vulnerability of HTML element Ex. You should use `fm.escape(file.name)`
*
* @param String|Array message[s]|Object jQuery
* @return String
**/
i18n : function() {
var self = this,
messages = this.messages,
input = [],
ignore = [],
message = function(m) {
var file;
if (m.indexOf('#') === 0) {
if ((file = self.file(m.substr(1)))) {
return file.name;
}
}
return m;
},
i, j, m, escFunc, start = 0, isErr;
if (arguments.length && arguments[0] === false) {
escFunc = function(m){ return m; };
start = 1;
}
for (i = start; i< arguments.length; i++) {
m = arguments[i];
if (Array.isArray(m)) {
for (j = 0; j < m.length; j++) {
if (m[j] instanceof jQuery) {
// jQuery object is HTML element
input.push(m[j]);
} else if (typeof m[j] !== 'undefined'){
input.push(message('' + m[j]));
}
}
} else if (m instanceof jQuery) {
// jQuery object is HTML element
input.push(m[j]);
} else if (typeof m !== 'undefined'){
input.push(message('' + m));
}
}
for (i = 0; i < input.length; i++) {
// dont translate placeholders
if (jQuery.inArray(i, ignore) !== -1) {
continue;
}
m = input[i];
if (typeof m == 'string') {
isErr = !!(messages[m] && m.match(/^err/));
// translate message
m = messages[m] || (escFunc? escFunc(m) : self.escape(m));
// replace placeholders in message
m = m.replace(/\$(\d+)/g, function(match, placeholder) {
var res;
placeholder = i + parseInt(placeholder);
if (placeholder > 0 && input[placeholder]) {
ignore.push(placeholder);
}
res = escFunc? escFunc(input[placeholder]) : self.escape(input[placeholder]);
if (isErr) {
res = '
' + res + '';
}
return res;
});
} else {
// get HTML from jQuery object
m = m.get(0).outerHTML;
}
input[i] = m;
}
return jQuery.grep(input, function(m, i) { return jQuery.inArray(i, ignore) === -1 ? true : false; }).join('
');
},
/**
* Get icon style from file.icon
*
* @param Object elFinder file object
* @return String|Object
*/
getIconStyle : function(file, asObject) {
var self = this,
template = {
'background' : 'url(\'{url}\') 0 0 no-repeat',
'background-size' : 'contain'
},
style = '',
cssObj = {},
i = 0;
if (file.icon) {
style = 'style="';
jQuery.each(template, function(k, v) {
if (i++ === 0) {
v = v.replace('{url}', self.escape(file.icon));
}
if (asObject) {
cssObj[k] = v;
} else {
style += k+':'+v+';';
}
});
style += '"';
}
return asObject? cssObj : style;
},
/**
* Convert mimetype into css classes
*
* @param String file mimetype
* @return String
*/
mime2class : function(mimeType) {
var prefix = 'elfinder-cwd-icon-',
mime = mimeType.toLowerCase(),
isText = this.textMimes[mime];
mime = mime.split('/');
if (isText) {
mime[0] += ' ' + prefix + 'text';
} else if (mime[1] && mime[1].match(/\+xml$/)) {
mime[0] += ' ' + prefix + 'xml';
}
return prefix + mime[0] + (mime[1] ? ' ' + prefix + mime[1].replace(/(\.|\+)/g, '-') : '');
},
/**
* Return localized kind of file
*
* @param Object|String file or file mimetype
* @return String
*/
mime2kind : function(f) {
var isObj = typeof(f) == 'object' ? true : false,
mime = isObj ? f.mime : f,
kind;
if (isObj && f.alias && mime != 'symlink-broken') {
kind = 'Alias';
} else if (this.kinds[mime]) {
if (isObj && mime === 'directory' && (! f.phash || f.isroot)) {
kind = 'Root';
} else {
kind = this.kinds[mime];
}
} else if (this.mimeTypes[mime]) {
kind = this.mimeTypes[mime].toUpperCase();
if (!this.messages['kind'+kind]) {
kind = null;
}
}
if (! kind) {
if (mime.indexOf('text') === 0) {
kind = 'Text';
} else if (mime.indexOf('image') === 0) {
kind = 'Image';
} else if (mime.indexOf('audio') === 0) {
kind = 'Audio';
} else if (mime.indexOf('video') === 0) {
kind = 'Video';
} else if (mime.indexOf('application') === 0) {
kind = 'App';
} else {
kind = mime;
}
}
return this.messages['kind'+kind] ? this.i18n('kind'+kind) : mime;
},
/**
* Return boolean Is mime-type text file
*
* @param String mime-type
* @return Boolean
*/
mimeIsText : function(mime) {
return (this.textMimes[mime.toLowerCase()] || (mime.indexOf('text/') === 0 && mime.substr(5, 3) !== 'rtf') || mime.match(/^application\/.+\+xml$/))? true : false;
},
/**
* Returns a date string formatted according to the given format string
*
* @param String format string
* @param Object Date object
* @return String
*/
date : function(format, date) {
var self = this,
output, d, dw, m, y, h, g, i, s;
if (! date) {
date = new Date();
}
h = date[self.getHours]();
g = h > 12 ? h - 12 : h;
i = date[self.getMinutes]();
s = date[self.getSeconds]();
d = date[self.getDate]();
dw = date[self.getDay]();
m = date[self.getMonth]() + 1;
y = date[self.getFullYear]();
output = format.replace(/[a-z]/gi, function(val) {
switch (val) {
case 'd': return d > 9 ? d : '0'+d;
case 'j': return d;
case 'D': return self.i18n(self.i18.daysShort[dw]);
case 'l': return self.i18n(self.i18.days[dw]);
case 'm': return m > 9 ? m : '0'+m;
case 'n': return m;
case 'M': return self.i18n(self.i18.monthsShort[m-1]);
case 'F': return self.i18n(self.i18.months[m-1]);
case 'Y': return y;
case 'y': return (''+y).substr(2);
case 'H': return h > 9 ? h : '0'+h;
case 'G': return h;
case 'g': return g;
case 'h': return g > 9 ? g : '0'+g;
case 'a': return h >= 12 ? 'pm' : 'am';
case 'A': return h >= 12 ? 'PM' : 'AM';
case 'i': return i > 9 ? i : '0'+i;
case 's': return s > 9 ? s : '0'+s;
}
return val;
});
return output;
},
/**
* Return localized date
*
* @param Object file object
* @return String
*/
formatDate : function(file, t) {
var self = this,
ts = t || file.ts,
i18 = self.i18,
date, format, output, d, dw, m, y, h, g, i, s;
if (self.options.clientFormatDate && ts > 0) {
date = new Date(ts*1000);
format = ts >= this.yesterday
? this.fancyFormat
: this.dateFormat;
output = self.date(format, date);
return ts >= this.yesterday
? output.replace('$1', this.i18n(ts >= this.today ? 'Today' : 'Yesterday'))
: output;
} else if (file.date) {
return file.date.replace(/([a-z]+)\s/i, function(a1, a2) { return self.i18n(a2)+' '; });
}
return self.i18n('dateUnknown');
},
/**
* Return localized number string
*
* @param Number
* @return String
*/
toLocaleString : function(num) {
var v = new Number(num);
if (v) {
if (v.toLocaleString) {
return v.toLocaleString();
} else {
return String(num).replace( /(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
}
}
return num;
},
/**
* Return css class marks file permissions
*
* @param Object file
* @return String
*/
perms2class : function(o) {
var c = '';
if (!o.read && !o.write) {
c = 'elfinder-na';
} else if (!o.read) {
c = 'elfinder-wo';
} else if (!o.write) {
c = 'elfinder-ro';
}
if (o.type) {
c += ' elfinder-' + this.escape(o.type);
}
return c;
},
/**
* Return localized string with file permissions
*
* @param Object file
* @return String
*/
formatPermissions : function(f) {
var p = [];
f.read && p.push(this.i18n('read'));
f.write && p.push(this.i18n('write'));
return p.length ? p.join(' '+this.i18n('and')+' ') : this.i18n('noaccess');
},
/**
* Return formated file size
*
* @param Number file size
* @return String
*/
formatSize : function(s) {
var n = 1, u = 'b';
if (s == 'unknown') {
return this.i18n('unknown');
}
if (s > 1073741824) {
n = 1073741824;
u = 'GB';
} else if (s > 1048576) {
n = 1048576;
u = 'MB';
} else if (s > 1024) {
n = 1024;
u = 'KB';
}
s = s/n;
return (s > 0 ? n >= 1048576 ? s.toFixed(2) : Math.round(s) : 0) +' '+u;
},
/**
* Return formated file mode by options.fileModeStyle
*
* @param String file mode
* @param String format style
* @return String
*/
formatFileMode : function(p, style) {
var i, o, s, b, sticy, suid, sgid, str, oct;
if (!style) {
style = this.options.fileModeStyle.toLowerCase();
}
p = jQuery.trim(p);
if (p.match(/[rwxs-]{9}$/i)) {
str = p = p.substr(-9);
if (style == 'string') {
return str;
}
oct = '';
s = 0;
for (i=0; i<7; i=i+3) {
o = p.substr(i, 3);
b = 0;
if (o.match(/[r]/i)) {
b += 4;
}
if (o.match(/[w]/i)) {
b += 2;
}
if (o.match(/[xs]/i)) {
if (o.match(/[xs]/)) {
b += 1;
}
if (o.match(/[s]/i)) {
if (i == 0) {
s += 4;
} else if (i == 3) {
s += 2;
}
}
}
oct += b.toString(8);
}
if (s) {
oct = s.toString(8) + oct;
}
} else {
p = parseInt(p, 8);
oct = p? p.toString(8) : '';
if (!p || style == 'octal') {
return oct;
}
o = p.toString(8);
s = 0;
if (o.length > 3) {
o = o.substr(-4);
s = parseInt(o.substr(0, 1), 8);
o = o.substr(1);
}
sticy = ((s & 1) == 1); // not support
sgid = ((s & 2) == 2);
suid = ((s & 4) == 4);
str = '';
for(i=0; i<3; i++) {
if ((parseInt(o.substr(i, 1), 8) & 4) == 4) {
str += 'r';
} else {
str += '-';
}
if ((parseInt(o.substr(i, 1), 8) & 2) == 2) {
str += 'w';
} else {
str += '-';
}
if ((parseInt(o.substr(i, 1), 8) & 1) == 1) {
str += ((i==0 && suid)||(i==1 && sgid))? 's' : 'x';
} else {
str += '-';
}
}
}
if (style == 'both') {
return str + ' (' + oct + ')';
} else if (style == 'string') {
return str;
} else {
return oct;
}
},
/**
* Regist this.decodeRawString function
*
* @return void
*/
registRawStringDecoder : function(rawStringDecoder) {
if (jQuery.isFunction(rawStringDecoder)) {
this.decodeRawString = this.options.rawStringDecoder = rawStringDecoder;
}
},
/**
* Return boolean that uploadable MIME type into target folder
*
* @param String mime MIME type
* @param String target target folder hash
* @return Bool
*/
uploadMimeCheck : function(mime, target) {
target = target || this.cwd().hash;
var res = true, // default is allow
mimeChecker = this.option('uploadMime', target),
allow,
deny,
check = function(checker) {
var ret = false;
if (typeof checker === 'string' && checker.toLowerCase() === 'all') {
ret = true;
} else if (Array.isArray(checker) && checker.length) {
jQuery.each(checker, function(i, v) {
v = v.toLowerCase();
if (v === 'all' || mime.indexOf(v) === 0) {
ret = true;
return false;
}
});
}
return ret;
};
if (mime && jQuery.isPlainObject(mimeChecker)) {
mime = mime.toLowerCase();
allow = check(mimeChecker.allow);
deny = check(mimeChecker.deny);
if (mimeChecker.firstOrder === 'allow') {
res = false; // default is deny
if (! deny && allow === true) { // match only allow
res = true;
}
} else {
res = true; // default is allow
if (deny === true && ! allow) { // match only deny
res = false;
}
}
}
return res;
},
/**
* call chained sequence of async deferred functions
*
* @param Array tasks async functions
* @return Object jQuery.Deferred
*/
sequence : function(tasks) {
var l = tasks.length,
chain = function(task, idx) {
++idx;
if (tasks[idx]) {
return chain(task.then(tasks[idx]), idx);
} else {
return task;
}
};
if (l > 1) {
return chain(tasks[0](), 0);
} else {
return tasks[0]();
}
},
/**
* Reload contents of target URL for clear browser cache
*
* @param String url target URL
* @return Object jQuery.Deferred
*/
reloadContents : function(url) {
var dfd = jQuery.Deferred(),
ifm;
try {
ifm = jQuery('