Created
March 14, 2016 12:04
-
-
Save nao-pon/fe9681ab43bf463bcbcd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* elFinder - file manager for web | |
* Version 2.1.9 (2.1-src Nightly: 0878b27) (2016-03-14) | |
* http://elfinder.org | |
* | |
* Copyright 2009-2016, Studio 42 | |
* Licensed under a 3 clauses BSD license | |
*/ | |
(function($) { | |
/* | |
* File: /js/elFinder.js | |
*/ | |
/** | |
* @class elFinder - file manager for web | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
window.elFinder = function(node, opts) { | |
//this.time('load'); | |
var self = this, | |
/** | |
* Node on which elfinder creating | |
* | |
* @type jQuery | |
**/ | |
node = $(node), | |
/** | |
* Store node contents. | |
* | |
* @see this.destroy | |
* @type jQuery | |
**/ | |
prevContent = $('<div/>').append(node.contents()), | |
/** | |
* Store node inline styles | |
* | |
* @see this.destroy | |
* @type String | |
**/ | |
prevStyle = node.attr('style'), | |
/** | |
* Instance ID. Required to get/set cookie | |
* | |
* @type String | |
**/ | |
id = node.attr('id') || '', | |
/** | |
* Events namespace | |
* | |
* @type String | |
**/ | |
namespace = 'elfinder-'+(id || Math.random().toString().substr(2, 7)), | |
/** | |
* Mousedown event | |
* | |
* @type String | |
**/ | |
mousedown = 'mousedown.'+namespace, | |
/** | |
* Keydown event | |
* | |
* @type String | |
**/ | |
keydown = 'keydown.'+namespace, | |
/** | |
* Keypress event | |
* | |
* @type String | |
**/ | |
keypress = 'keypress.'+namespace, | |
/** | |
* Is shortcuts/commands enabled | |
* | |
* @type Boolean | |
**/ | |
enabled = true, | |
/** | |
* Store enabled value before ajax requiest | |
* | |
* @type Boolean | |
**/ | |
prevEnabled = true, | |
/** | |
* List of build-in events which mapped into methods with same names | |
* | |
* @type Array | |
**/ | |
events = ['enable', 'disable', 'load', 'open', 'reload', 'select', 'add', 'remove', 'change', 'dblclick', 'getfile', 'lockfiles', 'unlockfiles', 'dragstart', 'dragstop'], | |
/** | |
* Rules to validate data from backend | |
* | |
* @type Object | |
**/ | |
rules = {}, | |
/** | |
* Current working directory hash | |
* | |
* @type String | |
**/ | |
cwd = '', | |
/** | |
* Current working directory options | |
* | |
* @type Object | |
**/ | |
cwdOptions = { | |
path : '', | |
url : '', | |
tmbUrl : '', | |
disabled : [], | |
separator : '/', | |
archives : [], | |
extract : [], | |
copyOverwrite : true, | |
uploadOverwrite : true, | |
uploadMaxSize : 0, | |
jpgQuality : 100, | |
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, | |
/** | |
* elfinder path for sound played on remove | |
* @type String | |
* @default ./sounds/ | |
**/ | |
soundPath = './sounds/', | |
beeper = $(document.createElement('audio')).hide().appendTo('body')[0], | |
syncInterval, | |
uiCmdMapPrev = '', | |
open = function(data) { | |
var volumeid, contextmenu, emptyDirs = {}, stayDirs = {}; | |
if (self.api >= 2.1) { | |
self.commandMap = (data.options.uiCmdMap && Object.keys(data.options.uiCmdMap).length)? data.options.uiCmdMap : {}; | |
// support volume driver option `uiCmdMap` | |
if (uiCmdMapPrev !== JSON.stringify(self.commandMap)) { | |
uiCmdMapPrev = JSON.stringify(self.commandMap); | |
if (Object.keys(self.commandMap).length) { | |
// for contextmenu | |
contextmenu = self.getUI('contextmenu'); | |
if (!contextmenu.data('cmdMaps')) { | |
contextmenu.data('cmdMaps', {}); | |
} | |
volumeid = data.cwd? data.cwd.volumeid : null; | |
if (volumeid && !contextmenu.data('cmdMaps')[volumeid]) { | |
contextmenu.data('cmdMaps')[volumeid] = self.commandMap; | |
} | |
} | |
} | |
} else { | |
self.options.sync = 0; | |
} | |
if (data.init) { | |
// init - reset cache | |
files = {}; | |
} else { | |
// remove only files from prev cwd | |
// and collapsed directory (included 100+ directories) to empty for perfomance tune in DnD | |
$.each(Object.keys(files), function(n, i) { | |
var isDir = (files[i].mime === 'directory'), | |
phash = files[i].phash, | |
collapsed = self.res('class', 'navcollapse'), | |
pnav; | |
if ( | |
(!isDir | |
|| emptyDirs[phash] | |
|| (!stayDirs[phash] | |
&& $('#'+self.navHash2Id(files[i].hash)).is(':hidden') | |
&& $('#'+self.navHash2Id(phash)).next('.elfinder-navbar-subtree').children().length > 100 | |
) | |
) | |
&& (isDir || phash === cwd) | |
&& $.inArray(i, remember) === -1 | |
) { | |
if (isDir && !emptyDirs[phash]) { | |
emptyDirs[phash] = true; | |
} | |
delete files[i]; | |
} else if (isDir) { | |
stayDirs[phash] = true; | |
} | |
}); | |
$.each(Object.keys(emptyDirs), function(n, i) { | |
var rmClass = 'elfinder-subtree-loaded ' + self.res('class', 'navexpand'); | |
$('#'+self.navHash2Id(i)) | |
.removeClass(rmClass) | |
.next('.elfinder-navbar-subtree').empty(); | |
}); | |
} | |
cwd = data.cwd.hash; | |
cache(data.files); | |
if (!files[cwd]) { | |
cache([data.cwd]); | |
} | |
self.lastDir(cwd); | |
self.autoSync(); | |
}, | |
/** | |
* Store info about files/dirs in "files" object. | |
* | |
* @param Array files | |
* @return void | |
**/ | |
cache = function(data) { | |
var l = data.length, f, i; | |
for (i = 0; i < l; i++) { | |
f = data[i]; | |
if (f.name && f.hash && f.mime) { | |
if (!f.phash) { | |
var name = 'volume_'+f.name, | |
i18 = self.i18n(name); | |
if (name != i18) { | |
f.i18 = i18; | |
} | |
// set disabledCmds, tmbUrls for each volume | |
if (f.volumeid) { | |
f.disabled && (self.disabledCmds[f.volumeid] = f.disabled); | |
if (f.tmbUrl) { | |
self.tmbUrls[f.volumeid] = f.tmbUrl; | |
} | |
self.roots[f.volumeid] = f.hash; | |
} | |
} | |
files[f.hash] = f; | |
} | |
} | |
}, | |
/** | |
* Exec shortcut | |
* | |
* @param jQuery.Event keydown/keypress event | |
* @return void | |
*/ | |
execShortcut = function(e) { | |
var code = e.keyCode, | |
ctrlKey = !!(e.ctrlKey || e.metaKey); | |
if (enabled) { | |
$.each(shortcuts, function(i, shortcut) { | |
if (shortcut.type == e.type | |
&& shortcut.keyCode == code | |
&& shortcut.shiftKey == e.shiftKey | |
&& shortcut.ctrlKey == ctrlKey | |
&& shortcut.altKey == e.altKey) { | |
e.preventDefault() | |
e.stopPropagation(); | |
shortcut.callback(e, self); | |
self.debug('shortcut-exec', i+' : '+shortcut.description); | |
} | |
}); | |
// prevent tab out of elfinder | |
if (code == 9 && !$(e.target).is(':input')) { | |
e.preventDefault(); | |
} | |
// cancel copy or cut by [Esc] key | |
if (code == 27 && self.clipboard().length) { | |
self.clipboard([]); | |
} | |
} | |
}, | |
date = new Date(), | |
utc, | |
i18n | |
; | |
/** | |
* Protocol version | |
* | |
* @type String | |
**/ | |
this.api = null; | |
/** | |
* elFinder use new api | |
* | |
* @type Boolean | |
**/ | |
this.newAPI = false; | |
/** | |
* elFinder use old api | |
* | |
* @type Boolean | |
**/ | |
this.oldAPI = false; | |
/** | |
* Net drivers names | |
* | |
* @type Array | |
**/ | |
this.netDrivers = []; | |
/** | |
* User os. Required to bind native shortcuts for open/rename | |
* | |
* @type String | |
**/ | |
this.OS = navigator.userAgent.indexOf('Mac') !== -1 ? 'mac' : navigator.userAgent.indexOf('Win') !== -1 ? 'win' : 'other'; | |
/** | |
* User browser UA. | |
* jQuery.browser: version deprecated: 1.3, removed: 1.9 | |
* | |
* @type Object | |
**/ | |
this.UA = (function(){ | |
var webkit = !document.uniqueID && !window.opera && !window.sidebar && window.localStorage && typeof window.orientation == "undefined"; | |
return { | |
// Browser IE <= IE 6 | |
ltIE6:typeof window.addEventListener == "undefined" && typeof document.documentElement.style.maxHeight == "undefined", | |
// Browser IE <= IE 7 | |
ltIE7:typeof window.addEventListener == "undefined" && typeof document.querySelectorAll == "undefined", | |
// Browser IE <= IE 8 | |
ltIE8:typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined", | |
IE:document.uniqueID, | |
Firefox:window.sidebar, | |
Opera:window.opera, | |
Webkit:webkit, | |
Chrome:webkit && window.chrome, | |
Safari:webkit && !window.chrome, | |
Mobile:typeof window.orientation != "undefined", | |
Touch:typeof window.ontouchstart != "undefined", | |
iOS: navigator.platform.match(/^iP(?:[ao]d|hone)/) | |
}; | |
})(); | |
/** | |
* 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; | |
} | |
if (opts.uiOptions && opts.uiOptions.cwd && opts.uiOptions.cwd.listView && opts.uiOptions.cwd.listView.columns) { | |
this.options.uiOptions.cwd.listView.columns = opts.uiOptions.cwd.listView.columns; | |
} | |
if (opts.uiOptions && opts.uiOptions.cwd && opts.uiOptions.cwd.listView && opts.uiOptions.cwd.listView.columnsCustomName) { | |
this.options.uiOptions.cwd.listView.columnsCustomName = opts.uiOptions.cwd.listView.columnsCustomName; | |
} | |
// configure for CORS | |
(function(){ | |
var parseUrl = document.createElement('a'), | |
parseUploadUrl; | |
parseUrl.href = opts.url; | |
if (opts.urlUpload && (opts.urlUpload !== opts.url)) { | |
parseUploadUrl = document.createElement('a'); | |
parseUploadUrl.href = opts.urlUpload; | |
} | |
if (window.location.host !== parseUrl.host || (parseUploadUrl && (window.location.host !== parseUploadUrl.host))) { | |
if (!$.isPlainObject(self.options.customHeaders)) { | |
self.options.customHeaders = {}; | |
} | |
if (!$.isPlainObject(self.options.xhrFields)) { | |
self.options.xhrFields = {}; | |
} | |
self.options.requestType = 'post'; | |
self.options.customHeaders['X-Requested-With'] = 'XMLHttpRequest'; | |
self.options.xhrFields['withCredentials'] = true; | |
} | |
})(); | |
$.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 : {}; | |
/** | |
* Any custom headers to send across every ajax request | |
* | |
* @type Object | |
* @default {} | |
*/ | |
this.customHeaders = $.isPlainObject(this.options.customHeaders) ? this.options.customHeaders : {}; | |
/** | |
* Any custom xhrFields to send across every ajax request | |
* | |
* @type Object | |
* @default {} | |
*/ | |
this.xhrFields = $.isPlainObject(this.options.xhrFields) ? this.options.xhrFields : {}; | |
/** | |
* ID. Required to create unique cookie name | |
* | |
* @type String | |
**/ | |
this.id = id; | |
/** | |
* ui.nav id prefix | |
* | |
* @type String | |
*/ | |
this.navPrefix = 'nav' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-'; | |
/** | |
* ui.cwd id prefix | |
* | |
* @type String | |
*/ | |
this.cwdPrefix = elFinder.prototype.uniqueid? ('cwd' + elFinder.prototype.uniqueid + '-') : ''; | |
// Increment elFinder.prototype.uniqueid | |
++elFinder.prototype.uniqueid; | |
/** | |
* URL to upload files | |
* | |
* @type String | |
**/ | |
this.uploadURL = opts.urlUpload || opts.url; | |
/** | |
* Events namespace | |
* | |
* @type String | |
**/ | |
this.namespace = namespace; | |
/** | |
* 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; | |
/** | |
* Current search status | |
* | |
* @type Object | |
*/ | |
this.searchStatus = { | |
state : 0, // 0: search ended, 1: search started, 2: in search result | |
query : '', | |
target : '', | |
mime : '' | |
}; | |
/** | |
* Method to store/fetch data | |
* | |
* @type Function | |
**/ | |
this.storage = (function() { | |
try { | |
return 'localStorage' in window && window['localStorage'] !== null ? self.localStorage : self.cookie; | |
} catch (e) { | |
return self.cookie; | |
} | |
})(); | |
this.viewType = this.storage('view') || this.options.defaultView || 'icons'; | |
this.sortType = this.storage('sortType') || this.options.sortType || 'name'; | |
this.sortOrder = this.storage('sortOrder') || this.options.sortOrder || 'asc'; | |
this.sortStickFolders = this.storage('sortStickFolders'); | |
if (this.sortStickFolders === null) { | |
this.sortStickFolders = !!this.options.sortStickFolders; | |
} else { | |
this.sortStickFolders = !!this.sortStickFolders | |
} | |
this.sortRules = $.extend(true, {}, this._sortRules, this.options.sortRules); | |
$.each(this.sortRules, function(name, method) { | |
if (typeof method != 'function') { | |
delete self.sortRules[name]; | |
} | |
}); | |
this.compare = $.proxy(this.compare, this); | |
/** | |
* Delay in ms before open notification dialog | |
* | |
* @type Number | |
* @default 500 | |
**/ | |
this.notifyDelay = this.options.notifyDelay > 0 ? parseInt(this.options.notifyDelay) : 500; | |
/** | |
* Dragging UI Helper object | |
* | |
* @type jQuery | null | |
**/ | |
this.draggingUiHelper = null, | |
/** | |
* Base draggable options | |
* | |
* @type Object | |
**/ | |
this.draggable = { | |
appendTo : 'body', | |
addClasses : true, | |
distance : 4, | |
revert : true, | |
refreshPositions : false, | |
cursor : 'crosshair', | |
cursorAt : {left : 50, top : 47}, | |
start : function(e, ui) { | |
var targets = $.map(ui.helper.data('files')||[], function(h) { return h || null ;}), | |
locked = false, | |
cnt, h; | |
self.draggingUiHelper = ui.helper; | |
cnt = targets.length; | |
while (cnt--) { | |
h = targets[cnt]; | |
if (files[h].locked) { | |
locked = true; | |
ui.helper.data('locked', true); | |
break; | |
} | |
} | |
!locked && self.trigger('lockfiles', {files : targets}); | |
}, | |
drag : function(e, ui) { | |
var helper = ui.helper; | |
if (helper.data('refreshPositions') && $(this).draggable('instance')) { | |
if (helper.data('refreshPositions') > 0) { | |
$(this).draggable('option', { refreshPositions : true }); | |
helper.data('refreshPositions', -1); | |
} else { | |
$(this).draggable('option', { refreshPositions : false }); | |
helper.data('refreshPositions', null); | |
} | |
} | |
}, | |
stop : function(e, ui) { | |
var files; | |
$(this).draggable('instance') && $(this).draggable('option', { refreshPositions : false }); | |
self.draggingUiHelper = null; | |
self.trigger('focus').trigger('dragstop'); | |
if (! ui.helper.data('droped')) { | |
files = $.map(ui.helper.data('files')||[], function(h) { return h || null ;}); | |
self.trigger('unlockfiles', {files : files}); | |
self.trigger('selectfiles', {files : files}); | |
} | |
}, | |
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-status"/></div>'), | |
icon = function(f) { | |
var mime = f.mime, i; | |
i = '<div class="elfinder-cwd-icon '+self.mime2class(mime)+' ui-corner-all"/>'; | |
if (f.tmb && f.tmb !== 1) { | |
i = $(i).css('background', "url('"+self.option('tmbUrl')+f.tmb+"') center center no-repeat").get(0).outerHTML; | |
} | |
return i; | |
}, | |
hashes, l, ctr; | |
self.draggingUiHelper && self.draggingUiHelper.stop(true, true); | |
self.trigger('dragstart', {target : element[0], originalEvent : e}); | |
hashes = element.hasClass(self.res('class', 'cwdfile')) | |
? self.selected() | |
: [self.navId2Hash(element.attr('id'))]; | |
helper.append(icon(files[hashes[0]])).data('files', hashes).data('locked', false).data('droped', false).data('namespace', self.namespace).data('dropover', 0); | |
if ((l = hashes.length) > 1) { | |
helper.append(icon(files[hashes[l-1]]) + '<span class="elfinder-drag-num">'+l+'</span>'); | |
} | |
$(document).on(keydown + ' keyup.' + namespace, function(e){ | |
var chk = (e.shiftKey||e.ctrlKey||e.metaKey); | |
if (ctr !== chk) { | |
ctr = chk; | |
if (helper.is(':visible') && helper.data('dropover') && ! helper.data('droped')) { | |
helper.toggleClass('elfinder-drag-helper-plus', helper.data('locked')? true : ctr); | |
self.trigger(ctr? 'unlockfiles' : 'lockfiles', {files : hashes, helper: helper}); | |
} | |
} | |
}); | |
return helper; | |
} | |
}; | |
/** | |
* Base droppable options | |
* | |
* @type Object | |
**/ | |
this.droppable = { | |
greedy : true, | |
tolerance : 'pointer', | |
accept : '.elfinder-cwd-file-wrapper,.elfinder-navbar-dir,.elfinder-cwd-file,.elfinder-cwd-filename', | |
hoverClass : this.res('class', 'adroppable'), | |
classes : { // Deprecated hoverClass jQueryUI>=1.12.0 | |
'ui-droppable-hover': this.res('class', 'adroppable') | |
}, | |
autoDisable: true, // elFinder original, see jquery.elfinder.js | |
drop : function(e, ui) { | |
var dst = $(this), | |
targets = $.map(ui.helper.data('files')||[], function(h) { return h || null }), | |
result = [], | |
dups = [], | |
faults = [], | |
isCopy = ui.helper.hasClass('elfinder-drag-helper-plus'), | |
c = 'class', | |
cnt, hash, i, h; | |
if (ui.helper.data('namespace') !== self.namespace) { | |
return false; | |
} | |
if (dst.hasClass(self.res(c, 'cwdfile'))) { | |
hash = self.cwdId2Hash(dst.attr('id')); | |
} else if (dst.hasClass(self.res(c, 'navdir'))) { | |
hash = self.navId2Hash(dst.attr('id')); | |
} else { | |
hash = cwd; | |
} | |
cnt = targets.length; | |
while (cnt--) { | |
h = targets[cnt]; | |
// ignore drop into itself or in own location | |
if (h != hash && files[h].phash != hash) { | |
result.push(h); | |
} else { | |
((isCopy && h !== hash && files[hash].write)? dups : faults).push(h); | |
} | |
} | |
if (faults.length) { | |
return false; | |
} | |
ui.helper.data('droped', true); | |
if (dups.length) { | |
ui.helper.hide(); | |
self.exec('duplicate', dups); | |
} | |
if (result.length) { | |
ui.helper.hide(); | |
self.clipboard(result, !isCopy); | |
self.exec('paste', hash, void 0, hash).always(function(){ | |
self.trigger('unlockfiles', {files : targets}); | |
}); | |
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; | |
} | |
$.each(self.roots, function(id, rhash) { | |
if (rhash.indexOf(id) === 0) { | |
dir = rhash; | |
return false; | |
} | |
}); | |
if (dir) { | |
return dir; | |
} | |
while (i in files && files.hasOwnProperty(i)) { | |
dir = files[i] | |
if (!dir.phash && !dir.mime == 'directory' && dir.read) { | |
return dir.hash; | |
} | |
} | |
return ''; | |
} | |
/** | |
* Return current working directory info | |
* | |
* @return Object | |
*/ | |
this.cwd = function() { | |
return files[cwd] || {}; | |
} | |
/** | |
* Return required cwd option | |
* | |
* @param String option name | |
* @return mixed | |
*/ | |
this.option = function(name) { | |
return cwdOptions[name]||''; | |
} | |
/** | |
* Return file data from current dir or tree by it's hash | |
* | |
* @param String file hash | |
* @return Object | |
*/ | |
this.file = function(hash) { | |
return files[hash]; | |
}; | |
/** | |
* Return all cached files | |
* | |
* @return Array | |
*/ | |
this.files = function() { | |
return $.extend(true, {}, files); | |
} | |
/** | |
* Return list of file parents hashes include file hash | |
* | |
* @param String file hash | |
* @return Array | |
*/ | |
this.parents = function(hash) { | |
var parents = [], | |
dir; | |
while ((dir = this.file(hash))) { | |
parents.unshift(dir.hash); | |
hash = dir.phash; | |
} | |
return parents; | |
} | |
this.path2array = function(hash, i18) { | |
var file, | |
path = []; | |
while (hash && (file = files[hash]) && file.hash) { | |
path.unshift(i18 && file.i18 ? file.i18 : file.name); | |
hash = file.phash; | |
} | |
return path; | |
} | |
/** | |
* Return file path | |
* | |
* @param Object file | |
* @return String | |
*/ | |
this.path = function(hash, i18) { | |
return files[hash] && files[hash].path | |
? files[hash].path | |
: this.path2array(hash, i18).join(cwdOptions.separator); | |
} | |
/** | |
* Return file url if set | |
* | |
* @param String file hash | |
* @return String | |
*/ | |
this.url = function(hash) { | |
var file = files[hash]; | |
if (!file || !file.read) { | |
return ''; | |
} | |
if (file.url == '1') { | |
this.request({ | |
data : {cmd : 'url', target : hash}, | |
preventFail : true, | |
options: {async: false} | |
}) | |
.done(function(data) { | |
file.url = data.url || ''; | |
}) | |
.fail(function() { | |
file.url = ''; | |
}); | |
} | |
if (file.url) { | |
return file.url; | |
} | |
if (cwdOptions.url && file.hash.indexOf(self.cwd().volumeid) === 0) { | |
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); | |
} | |
/** | |
* Convert from relative URL to abstract URL based on current URL | |
* | |
* @param String URL | |
* @return String | |
*/ | |
this.convAbsUrl = function(url) { | |
if (url.match(/^http/i)) { | |
return url; | |
} | |
var root = window.location.protocol + '//' + window.location.host, | |
reg = /[^\/]+\/\.\.\//, | |
ret; | |
if (url.substr(0, 1) === '/') { | |
ret = root + url; | |
} else { | |
ret = root + window.location.pathname + url; | |
} | |
ret = ret.replace('/./', '/'); | |
while(reg.test(ret)) { | |
ret = ret.replace(reg, ''); | |
} | |
return ret; | |
} | |
/** | |
* Return file url for open in elFinder | |
* | |
* @param String file hash | |
* @param Boolean for download link | |
* @return String | |
*/ | |
this.openUrl = function(hash, download) { | |
var file = files[hash], | |
url = ''; | |
if (!file || !file.read) { | |
return ''; | |
} | |
if (!download) { | |
if (file.url) { | |
if (file.url != 1) { | |
return file.url; | |
} | |
} else if (cwdOptions.url && file.hash.indexOf(self.cwd().volumeid) === 0) { | |
return cwdOptions.url + $.map(this.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/'); | |
} | |
} | |
url = this.options.url; | |
url = url + (url.indexOf('?') === -1 ? '?' : '&') | |
+ (this.oldAPI ? 'cmd=open¤t='+file.phash : 'cmd=file') | |
+ '&target=' + file.hash; | |
if (download) { | |
url += '&download=1'; | |
} | |
$.each(this.options.customData, function(key, val) { | |
url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(val); | |
}); | |
return url; | |
} | |
/** | |
* Return thumbnail url | |
* | |
* @param String file hash | |
* @return String | |
*/ | |
this.tmb = function(hash) { | |
var file = files[hash], | |
geturl = function(hash){ | |
var turl = ''; | |
$.each(self.tmbUrls, function(i, u){ | |
if (hash.indexOf(i) === 0) { | |
turl = self.tmbUrls[i]; | |
return false; | |
} | |
}); | |
return turl; | |
}, | |
tmbUrl = (self.searchStatus.state && hash.indexOf(self.cwd().volumeid) !== 0)? geturl(hash) : cwdOptions['tmbUrl'], | |
url = tmbUrl && file && file.tmb && file.tmb != 1 ? tmbUrl + file.tmb : ''; | |
return url; | |
} | |
/** | |
* Return selected files hashes | |
* | |
* @return Array | |
**/ | |
this.selected = function() { | |
return selected.slice(0); | |
} | |
/** | |
* Return selected files info | |
* | |
* @return Array | |
*/ | |
this.selectedFiles = function() { | |
return $.map(selected, function(hash) { return files[hash] ? $.extend({}, files[hash]) : null }); | |
}; | |
/** | |
* Return true if file with required name existsin required folder | |
* | |
* @param String file name | |
* @param String parent folder hash | |
* @return Boolean | |
*/ | |
this.fileByName = function(name, phash) { | |
var hash; | |
for (hash in files) { | |
if (files.hasOwnProperty(hash) && files[hash].phash == phash && files[hash].name == name) { | |
return files[hash]; | |
} | |
} | |
}; | |
/** | |
* Valid data for required command based on rules | |
* | |
* @param String command name | |
* @param Object cammand's data | |
* @return Boolean | |
*/ | |
this.validResponse = function(cmd, data) { | |
return data.error || this.rules[this.rules[cmd] ? cmd : 'defaults'](data); | |
} | |
/** | |
* Return bytes from ini formated size | |
* | |
* @param String ini formated size | |
* @return Integer | |
*/ | |
this.returnBytes = function(val) { | |
var last; | |
if (isNaN(val)) { | |
// for ex. 1mb, 1KB | |
val = val.replace(/b$/i, ''); | |
last = val.charAt(val.length - 1).toLowerCase(); | |
val = val.replace(/[tgmk]$/i, ''); | |
if (last == 't') { | |
val = val * 1024 * 1024 * 1024 * 1024; | |
} else if (last == 'g') { | |
val = val * 1024 * 1024 * 1024; | |
} else if (last == 'm') { | |
val = val * 1024 * 1024; | |
} else if (last == 'k') { | |
val = val * 1024; | |
} | |
val = isNaN(val)? 0 : parseInt(val); | |
} else { | |
val = parseInt(val); | |
if (val < 1) val = 0; | |
} | |
return val; | |
}; | |
/** | |
* 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), | |
// make cancel button | |
cancel = !!options.cancel, | |
// 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, | |
headers : this.customHeaders, | |
xhrFields: this.xhrFields | |
}, 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) { | |
// Set currrent request command name | |
self.currentReqCmd = cmd; | |
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; | |
if (self.api == '2.0' && typeof response.options.uploadMaxSize !== 'undefined') { | |
self.api = '2.1'; | |
} | |
self.newAPI = self.api >= 2; | |
self.oldAPI = !self.newAPI; | |
} | |
if (response.options) { | |
cwdOptions = $.extend({}, cwdOptions, response.options); | |
} | |
if (response.netDrivers) { | |
self.netDrivers = response.netDrivers; | |
} | |
if (cmd == 'open' && !!data.init) { | |
self.uplMaxSize = self.returnBytes(response.uplMaxSize); | |
self.uplMaxFile = !!response.uplMaxFile? parseInt(response.uplMaxFile) : 20; | |
} | |
dfrd.resolve(response); | |
response.debug && self.debug('backend-debug', response.debug); | |
}, | |
xhr, _xhr, | |
abort = function(e){ | |
if (e.type == 'autosync') { | |
if (e.data.action != 'stop') return; | |
} else { | |
if (!e.data.added || !e.data.added.length) { | |
return; | |
} | |
} | |
if (xhr.state() == 'pending') { | |
xhr.quiet = true; | |
xhr.abort(); | |
if (e.type != 'unload' && e.type != 'destroy') { | |
self.autoSync(); | |
} | |
} | |
}; | |
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) { | |
if (cancel) { | |
notify.cancel = dfrd; | |
} | |
timeout = setTimeout(function() { | |
self.notify(notify); | |
dfrd.always(function() { | |
notify.cnt = -(parseInt(notify.cnt)||0); | |
self.notify(notify); | |
}) | |
}, self.notifyDelay) | |
dfrd.always(function() { | |
clearTimeout(timeout); | |
}); | |
} | |
// quiet abort not completed "open" requests | |
if (cmd == 'open') { | |
while ((_xhr = queue.pop())) { | |
if (_xhr.state() == 'pending') { | |
_xhr.quiet = true; | |
_xhr.abort(); | |
} | |
} | |
} | |
delete options.preventFail | |
dfrd.xhr = xhr = this.transport.send(options).fail(error).done(success); | |
// add "open" xhr into queue | |
if (cmd == 'open' || (cmd == 'info' && data.compare)) { | |
queue.unshift(xhr); | |
self.bind(self.cmdsToAdd + ' autosync', abort); | |
dfrd.always(function() { | |
var ndx = $.inArray(xhr, queue); | |
self.unbind(self.cmdsToAdd + ' autosync', abort); | |
ndx !== -1 && queue.splice(ndx, 1); | |
}); | |
} | |
// abort pending xhr on window unload or elFinder destroy | |
self.bind('unload destroy', abort); | |
dfrd.always(function() { | |
self.unbind('unload destroy', abort); | |
}); | |
return dfrd; | |
}; | |
/** | |
* Compare current files cache with new files and return diff | |
* | |
* @param Array new files | |
* @return Object | |
*/ | |
this.diff = function(incoming, onlydir) { | |
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) { | |
if (!onlydir || f.phash === onlydir) { | |
!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(onlydir, polling) { | |
this.autoSync('stop'); | |
var self = this, | |
compare = function(){ | |
var c = '', cnt = 0, mtime = 0; | |
if (onlydir && polling) { | |
$.each(files, function(h, f) { | |
if (f.phash && f.phash === onlydir) { | |
++cnt; | |
mtime = Math.max(mtime, f.ts); | |
} | |
c = cnt+':'+mtime; | |
}); | |
} | |
return c; | |
}, | |
comp = compare(), | |
dfrd = $.Deferred().done(function() { self.trigger('sync'); }), | |
opts1 = { | |
data : {cmd : 'open', reload : 1, target : cwd, tree : (! onlydir && this.ui.tree) ? 1 : 0, compare : comp}, | |
preventDefault : true | |
}, | |
opts2 = { | |
data : {cmd : 'parents', target : cwd}, | |
preventDefault : true | |
}; | |
$.when( | |
this.request(opts1), | |
onlydir? null : this.request(opts2) | |
) | |
.fail(function(error) { | |
if (! polling) { | |
dfrd.reject(error); | |
error && self.request({ | |
data : {cmd : 'open', target : (self.lastDir('') || self.root()), tree : 1, init : 1}, | |
notify : {type : 'open', cnt : 1, hideCnt : true} | |
}); | |
} else { | |
dfrd.reject(); | |
} | |
}) | |
.done(function(odata, pdata) { | |
if (odata.cwd.compare) { | |
if (comp === odata.cwd.compare) { | |
return dfrd.reject(); | |
} | |
} | |
if (self.api < 2.1) { | |
pdata.tree = (pdata.tree || []).concat([odata.cwd]); | |
} | |
var diff = self.diff(odata.files.concat(pdata && pdata.tree ? pdata.tree : []), onlydir); | |
diff.added.push(odata.cwd) | |
diff.removed.length && self.remove(diff); | |
diff.added.length && self.add(diff); | |
diff.changed.length && self.change(diff); | |
return dfrd.resolve(diff); | |
}) | |
.always(function() { | |
self.autoSync(); | |
}); | |
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 | |
* To un-bind to multiply events at once, separate events names by space | |
* | |
* @param String event(s) name(s) | |
* @param Function callback | |
* @return elFinder | |
*/ | |
this.unbind = function(event, callback) { | |
var i, l, ci; | |
event = ('' + event).toLowerCase().split(/\s+/); | |
for (i = 0; i < event.length; i++) { | |
l = listeners[event[i]] || []; | |
ci = $.inArray(callback, l); | |
ci > -1 && l.splice(ci, 1); | |
} | |
callback = null | |
return this; | |
}; | |
/** | |
* Fire event - send notification to all event listeners | |
* | |
* @param String event type | |
* @param Object data to send across event | |
* @param Boolean allow modify data (call by reference of data) | |
* @return elFinder | |
*/ | |
this.trigger = function(event, data, allowModify) { | |
var event = event.toLowerCase(), | |
isopen = (event === 'open'), | |
handlers = listeners[event] || [], i, l, jst; | |
this.debug('event-'+event, data); | |
if (isopen && !allowModify) { | |
// for performance tuning | |
jst = JSON.stringify(data); | |
} | |
if (handlers.length) { | |
event = $.Event(event); | |
if (allowModify) { | |
event.data = data; | |
} | |
l = handlers.length; | |
for (i = 0; i < l; i++) { | |
// only callback has argument | |
if (handlers[i].length) { | |
if (!allowModify) { | |
// to avoid data modifications. remember about "sharing" passing arguments in js :) | |
event.data = isopen? JSON.parse(jst) : $.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; | |
}; | |
/** | |
* Get event listeners | |
* | |
* @param String event type | |
* @return Array listed event functions | |
*/ | |
this.getListeners = function(event) { | |
return event? listeners[event.toLowerCase()] : listeners; | |
}; | |
/** | |
* 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 | |
* @param String|void hash for check of own volume's disabled cmds | |
* @return Boolean | |
*/ | |
this.isCommandEnabled = function(name, dstHash) { | |
var disabled; | |
if (dstHash && self.root(dstHash) !== cwd) { | |
disabled = []; | |
$.each(self.disabledCmds, function(i, v){ | |
if (dstHash.indexOf(i, 0) == 0) { | |
disabled = v; | |
return false; | |
} | |
}); | |
} else { | |
disabled = cwdOptions.disabled; | |
} | |
return this._commands[name] ? $.inArray(name, disabled) === -1 : false; | |
} | |
/** | |
* Exec command and return result; | |
* | |
* @param String command name | |
* @param String|Array usualy files hashes | |
* @param String|Array command options | |
* @param String|void hash for enabled check of own volume's disabled cmds | |
* @return $.Deferred | |
*/ | |
this.exec = function(cmd, files, opts, dstHash) { | |
if (cmd === 'open') { | |
this.autoSync('stop'); | |
} | |
return this._commands[cmd] && this.isCommandEnabled(cmd, dstHash) | |
? 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) { | |
var dialog = $('<div/>').append(content).appendTo(node).elfinderdialog(options); | |
this.bind('resize', function(){ | |
dialog.elfinderdialog('posInit'); | |
}); | |
return dialog; | |
} | |
/** | |
* 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.autoSync('stop'); | |
this.trigger('destroy').disable(); | |
listeners = {}; | |
shortcuts = {}; | |
$(document).add(node).off('.'+this.namespace); | |
self.trigger = function() { } | |
node.children().remove(); | |
node.append(prevContent.contents()).removeClass(this.cssClass).attr('style', prevStyle); | |
node[0].elfinder = null; | |
} | |
} | |
/** | |
* Start or stop auto sync | |
* | |
* @param String|Bool stop | |
* @return void | |
*/ | |
this.autoSync = function(stop) { | |
var sync; | |
if (self.options.sync >= 1000) { | |
if (syncInterval) { | |
clearTimeout(syncInterval); | |
syncInterval = null; | |
self.trigger('autosync', {action : 'stop'}); | |
} | |
if (stop || !self.options.syncStart) { | |
return; | |
} | |
// run interval sync | |
sync = function(start){ | |
var timeout; | |
if (cwdOptions.syncMinMs && (start || syncInterval)) { | |
start && self.trigger('autosync', {action : 'start'}); | |
timeout = Math.max(self.options.sync, cwdOptions.syncMinMs); | |
syncInterval && clearTimeout(syncInterval); | |
syncInterval = setTimeout(function() { | |
var dosync = true, hash = cwd, cts; | |
if (cwdOptions.syncChkAsTs && (cts = files[hash].ts)) { | |
self.request({ | |
data : {cmd : 'info', targets : [hash], compare : cts, reload : 1}, | |
preventDefault : true | |
}) | |
.done(function(data){ | |
var ts; | |
dosync = true; | |
if (data.compare) { | |
ts = data.compare; | |
if (ts == cts) { | |
dosync = false; | |
} | |
} | |
if (dosync) { | |
self.sync(hash).always(function(){ | |
if (ts) { | |
// update ts for cache clear etc. | |
files[hash].ts = ts; | |
} | |
sync(); | |
}); | |
} else { | |
sync(); | |
} | |
}) | |
.fail(function(error){ | |
if (error && error != 'errConnect') { | |
self.error(error); | |
} else { | |
syncInterval = setTimeout(function() { | |
sync(); | |
}, timeout); | |
} | |
}); | |
} else { | |
self.sync(cwd, true).always(function(){ | |
sync(); | |
}); | |
} | |
}, timeout); | |
} | |
}; | |
sync(true); | |
} | |
}; | |
/************* init stuffs ****************/ | |
// check jquery ui | |
if (!($.fn.selectable && $.fn.draggable && $.fn.droppable)) { | |
return alert(this.i18n('errJqui')); | |
} | |
// check node | |
if (!node.length) { | |
return alert(this.i18n('errNode')); | |
} | |
// check connector url | |
if (!this.options.url) { | |
return alert(this.i18n('errURL')); | |
} | |
$.extend($.ui.keyCode, { | |
'F1' : 112, | |
'F2' : 113, | |
'F3' : 114, | |
'F4' : 115, | |
'F5' : 116, | |
'F6' : 117, | |
'F7' : 118, | |
'F8' : 119, | |
'F9' : 120 | |
}); | |
this.dragUpload = false; | |
this.xhrUpload = (typeof XMLHttpRequestUpload != 'undefined' || typeof XMLHttpRequestEventTarget != 'undefined') && typeof File != 'undefined' && typeof FormData != 'undefined'; | |
// configure transport object | |
this.transport = {} | |
if (typeof(this.options.transport) == 'object') { | |
this.transport = this.options.transport; | |
if (typeof(this.transport.init) == 'function') { | |
this.transport.init(this) | |
} | |
} | |
if (typeof(this.transport.send) != 'function') { | |
this.transport.send = function(opts) { return $.ajax(opts); } | |
} | |
if (this.transport.upload == 'iframe') { | |
this.transport.upload = $.proxy(this.uploads.iframe, this); | |
} else if (typeof(this.transport.upload) == 'function') { | |
this.dragUpload = !!this.options.dragUploadAllow; | |
} else if (this.xhrUpload && !!this.options.dragUploadAllow) { | |
this.transport.upload = $.proxy(this.uploads.xhr, this); | |
this.dragUpload = true; | |
} else { | |
this.transport.upload = $.proxy(this.uploads.iframe, this); | |
} | |
/** | |
* Alias for this.trigger('error', {error : 'message'}) | |
* | |
* @param String error message | |
* @return elFinder | |
**/ | |
this.error = function() { | |
var arg = arguments[0]; | |
return arguments.length == 1 && typeof(arg) == 'function' | |
? self.bind('error', arg) | |
: self.trigger('error', {error : arg}); | |
} | |
// create bind/trigger aliases for build-in events | |
$.each(['enable', 'disable', 'load', 'open', 'reload', 'select', 'add', 'remove', 'change', 'dblclick', 'getfile', 'lockfiles', 'unlockfiles', 'selectfiles', 'unselectfiles', '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; | |
document.activeElement && document.activeElement.blur(); | |
node.removeClass('elfinder-disabled'); | |
} | |
}) | |
.disable(function() { | |
prevEnabled = enabled; | |
enabled = false; | |
node.addClass('elfinder-disabled'); | |
}) | |
.open(function() { | |
selected = []; | |
}) | |
.select(function(e) { | |
selected = $.map(e.data.selected || e.data.value|| [], function(hash) { return files[hash] ? hash : null; }); | |
}) | |
.error(function(e) { | |
var opts = { | |
cssClass : 'elfinder-dialog-error', | |
title : self.i18n(self.i18n('error')), | |
resizable : false, | |
destroyOnClose : true, | |
buttons : {} | |
}; | |
opts.buttons[self.i18n(self.i18n('btnClose'))] = function() { $(this).elfinderdialog('close'); }; | |
self.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-error"/>'+self.i18n(e.data.error), opts); | |
}) | |
.bind('tree parents', function(e) { | |
cache(e.data.tree || []); | |
}) | |
.bind('tmb', function(e) { | |
$.each(e.data.images||[], function(hash, tmb) { | |
if (files[hash]) { | |
files[hash].tmb = tmb; | |
} | |
}) | |
}) | |
.add(function(e) { | |
cache(e.data.added||[]); | |
}) | |
.change(function(e) { | |
$.each(e.data.changed||[], function(i, file) { | |
var hash = file.hash; | |
if (files[hash]) { | |
$.each(['locked', 'hidden', 'width', 'height'], function(i, v){ | |
if (files[hash][v] && !file[v]) { | |
delete files[hash][v]; | |
} | |
}); | |
} | |
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('searchstart', function(e) { | |
$.extend(self.searchStatus, e.data); | |
self.searchStatus.state = 1; | |
}) | |
.bind('search', function(e) { | |
self.searchStatus.state = 2; | |
cache(e.data.files); | |
}) | |
.bind('searchend', function() { | |
self.searchStatus.state = 0; | |
}) | |
.bind('rm', function(e) { | |
var play = beeper.canPlayType && beeper.canPlayType('audio/wav; codecs="1"'); | |
play && play != '' && play != 'no' && $(beeper).html('<source src="' + soundPath + 'rm.wav" type="audio/wav">')[0].play() | |
}) | |
; | |
// bind external event handlers | |
$.each(this.options.handlers, function(event, callback) { | |
self.bind(event, callback); | |
}); | |
/** | |
* History object. Store visited folders | |
* | |
* @type Object | |
**/ | |
this.history = new this.history(this); | |
// in getFileCallback set - change default actions on double click/enter/ctrl+enter | |
if (this.commands.getfile) { | |
if (typeof(this.options.getFileCallback) == 'function') { | |
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') } | |
}); | |
} else { | |
delete this.commands.getfile; | |
} | |
} | |
/** | |
* Root hashed | |
* | |
* @type Object | |
*/ | |
this.roots = {}, | |
/** | |
* 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]||{}); | |
// setup linked commands | |
if (self._commands[name].linkedCmds.length) { | |
$.each(self._commands[name].linkedCmds, function(i, n) { | |
var lcmd = self.commands[n]; | |
if ($.isFunction(lcmd) && !self._commands[n]) { | |
lcmd.prototype = base; | |
self._commands[n] = new lcmd(); | |
self._commands[n].setup(n, self.options.commandsOptions[n]||{}); | |
} | |
}); | |
} | |
} | |
}); | |
/** | |
* UI command map of cwd volume ( That volume driver option `uiCmdMap` ) | |
* | |
* @type Object | |
**/ | |
this.commandMap = {}; | |
/** | |
* Disabled commands Array of each volume | |
* | |
* @type Object | |
*/ | |
this.disabledCmds = {}; | |
/** | |
* tmbUrls Array of each volume | |
* | |
* @type Object | |
*/ | |
this.tmbUrls = {}; | |
// prepare node | |
node.addClass(this.cssClass) | |
.on(mousedown, function() { | |
!enabled && self.enable(); | |
}); | |
/** | |
* UI nodes | |
* | |
* @type Object | |
**/ | |
this.ui = { | |
// container for nav panel and current folder container | |
workzone : $('<div/>').appendTo(node).elfinderworkzone(this), | |
// container for folders tree / places | |
navbar : $('<div/>').appendTo(node).elfindernavbar(this, this.options.uiOptions.navbar || {}), | |
// contextmenu | |
contextmenu : $('<div/>').appendTo(node).elfindercontextmenu(this), | |
// overlay | |
overlay : $('<div/>').appendTo(node).elfinderoverlay({ | |
show : function() { self.disable(); }, | |
hide : function() { prevEnabled && self.enable(); } | |
}), | |
// current folder container | |
cwd : $('<div/>').appendTo(node).elfindercwd(this, this.options.uiOptions.cwd || {}), | |
// notification dialog window | |
notify : this.dialog('', { | |
cssClass : 'elfinder-dialog-notify', | |
position : this.options.notifyDialog.position, | |
resizable : false, | |
autoOpen : false, | |
closeOnEscape : false, | |
title : ' ', | |
width : parseInt(this.options.notifyDialog.width) | |
}), | |
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 | |
&& !this.UA.Touch | |
&& $.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); | |
} | |
if (this.options.soundPath) { | |
soundPath = this.options.soundPath.replace(/\/+$/, '') + '/'; | |
} | |
// update size | |
self.resize(width, height); | |
// attach events to document | |
$(document) | |
// disable elfinder on click outside elfinder | |
.on('click.'+this.namespace, function(e) { enabled && !$(e.target).closest(node).length && self.disable(); }) | |
// exec shortcuts | |
.on(keydown+' '+keypress, execShortcut); | |
// attach events to window | |
self.options.useBrowserHistory && $(window) | |
.on('popstate', function(ev) { | |
var target = ev.originalEvent.state && ev.originalEvent.state.thash; | |
target && !$.isEmptyObject(self.files()) && self.request({ | |
data : {cmd : 'open', target : target, onhistory : 1}, | |
notify : {type : 'open', cnt : 1, hideCnt : true}, | |
syncOnFail : true | |
}); | |
}); | |
// send initial request and start to pray >_< | |
this.trigger('init') | |
.request({ | |
data : {cmd : 'open', target : self.startDir(), init : 1, tree : this.ui.tree ? 1 : 0}, | |
preventDone : true, | |
notify : {type : 'open', cnt : 1, hideCnt : true}, | |
freeze : true | |
}) | |
.fail(function() { | |
self.trigger('fail').disable().lastDir(''); | |
listeners = {}; | |
shortcuts = {}; | |
$(document).add(node).off('.'+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'); | |
}); | |
(function(){ | |
var tm; | |
$(window).on('resize', function(){ | |
tm && clearTimeout(tm); | |
tm = setTimeout(function() { | |
self.trigger('resize', {width : node.width(), height : node.height()}); | |
}, 200); | |
}) | |
.on('beforeunload',function(){ | |
if (self.ui.notify.children().length) { | |
return self.i18n('ntfsmth'); | |
} | |
self.trigger('unload'); | |
}); | |
})(); | |
// bind window onmessage for CORS | |
$(window).on('message', function(e){ | |
var res = e.originalEvent || null, | |
obj, data; | |
if (res && self.uploadURL.indexOf(res.origin) === 0) { | |
try { | |
obj = JSON.parse(res.data); | |
data = obj.data || null; | |
if (data) { | |
if (data.error) { | |
self.error(data.error); | |
} else { | |
data.warning && self.error(data.warning); | |
data.removed && data.removed.length && self.remove(data); | |
data.added && data.added.length && self.add(data); | |
data.changed && data.changed.length && self.change(data); | |
if (obj.bind) { | |
self.trigger(obj.bind, data); | |
} | |
data.sync && self.sync(); | |
} | |
} | |
} catch (e) { | |
self.sync(); | |
} | |
} | |
}); | |
if (self.dragUpload) { | |
(function(){ | |
var isin = function(e) { | |
return (e.target.nodeName !== 'TEXTAREA' && e.target.nodeName !== 'INPUT' && $(e.target).closest('div.ui-dialog-content').length === 0); | |
} | |
node[0].addEventListener('dragenter', function(e) { | |
if (isin(e)) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
} | |
}, false); | |
node[0].addEventListener('dragleave', function(e) { | |
if (isin(e)) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
} | |
}, false); | |
node[0].addEventListener('dragover', function(e) { | |
if (isin(e)) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
e.dataTransfer.dropEffect = 'none'; | |
} | |
}, false); | |
node[0].addEventListener('drop', function(e) { | |
if (isin(e)) { | |
e.stopPropagation(); | |
e.preventDefault(); | |
} | |
}, false); | |
})(); | |
// add event listener for HTML5 DnD upload | |
(function() { | |
var ent = 'native-drag-enter', | |
disable = 'native-drag-disable', | |
c = 'class', | |
navdir = self.res(c, 'navdir'), | |
droppable = self.res(c, 'droppable'), | |
collapsed = self.res(c, 'navcollapse'), | |
expanded = self.res(c, 'navexpand'), | |
dropover = self.res(c, 'adroppable'), | |
arrow = self.res(c, 'navarrow'), | |
clDropActive = self.res(c, 'adroppable'); | |
node.on('dragenter', '.native-droppable', function(e){ | |
if (e.originalEvent.dataTransfer) { | |
var $elm = $(e.currentTarget), | |
id = e.currentTarget.id || null, | |
cwd = null, | |
elfFrom; | |
if (!id) { // target is cwd | |
cwd = self.cwd(); | |
$elm.data(disable, false); | |
try { | |
$.each(e.originalEvent.dataTransfer.types, function(i, v){ | |
if (v.substr(0, 13) === 'elfinderfrom:') { | |
elfFrom = v.substr(13).toLowerCase(); | |
} | |
}); | |
} catch(e) {} | |
} else { | |
if (!$elm.data(ent) && $elm.hasClass(navdir) && $elm.is('.'+collapsed+':not(.'+expanded+')')) { | |
setTimeout(function() { | |
$elm.is('.'+collapsed+'.'+dropover) && $elm.children('.'+arrow).click(); | |
}, 500); | |
} | |
} | |
if (!cwd || (cwd.write && (!elfFrom || elfFrom !== (window.location.href + cwd.hash).toLowerCase()))) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
$elm.data(ent, true); | |
$elm.addClass(clDropActive); | |
} else { | |
$elm.data(disable, true); | |
} | |
} | |
}) | |
.on('dragleave', '.native-droppable', function(e){ | |
if (e.originalEvent.dataTransfer) { | |
var $elm = $(e.currentTarget); | |
e.preventDefault(); | |
e.stopPropagation(); | |
if ($elm.data(ent)) { | |
$elm.data(ent, false); | |
} else { | |
$elm.removeClass(clDropActive); | |
} | |
} | |
}) | |
.on('dragover', '.native-droppable', function(e){ | |
if (e.originalEvent.dataTransfer) { | |
var $elm = $(e.currentTarget); | |
e.preventDefault(); | |
e.stopPropagation(); | |
e.originalEvent.dataTransfer.dropEffect = $elm.data(disable)? 'none' : 'copy'; | |
$elm.data(ent, false); | |
} | |
}) | |
.on('drop', '.native-droppable', function(e){ | |
if (e.originalEvent.dataTransfer) { | |
var $elm = $(e.currentTarget) | |
id; | |
e.preventDefault(); | |
e.stopPropagation(); | |
$elm.removeClass(clDropActive); | |
if (e.currentTarget.id) { | |
id = $elm.hasClass(navdir)? self.navId2Hash(e.currentTarget.id) : self.cwdId2Hash(e.currentTarget.id); | |
} else { | |
id = self.cwd().hash; | |
} | |
e.originalEvent._target = id; | |
self.directUploadTarget = id; | |
self.exec('upload', {dropEvt: e.originalEvent, target: id}); | |
self.directUploadTarget = null; | |
} | |
}); | |
})(); | |
} | |
// self.timeEnd('load'); | |
} | |
/** | |
* Prototype | |
* | |
* @type Object | |
*/ | |
elFinder.prototype = { | |
uniqueid : 0, | |
res : function(type, id) { | |
return this.resources[type] && this.resources[type][id]; | |
}, | |
/** | |
* Current request command | |
* | |
* @type String | |
*/ | |
currentReqCmd : '', | |
/** | |
* Internationalization object | |
* | |
* @type Object | |
*/ | |
i18 : { | |
en : { | |
translator : '', | |
language : 'English', | |
direction : 'ltr', | |
dateFormat : 'd.m.Y H:i', | |
fancyDateFormat : '$1 H:i', | |
messages : {} | |
}, | |
months : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], | |
monthsShort : ['msJan', 'msFeb', 'msMar', 'msApr', 'msMay', 'msJun', 'msJul', 'msAug', 'msSep', 'msOct', 'msNov', 'msDec'], | |
days : ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], | |
daysShort : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] | |
}, | |
/** | |
* File mimetype to kind mapping | |
* | |
* @type Object | |
*/ | |
kinds : { | |
'unknown' : 'Unknown', | |
'directory' : 'Folder', | |
'symlink' : 'Alias', | |
'symlink-broken' : 'AliasBroken', | |
'application/x-empty' : 'TextPlain', | |
'application/postscript' : 'Postscript', | |
'application/vnd.ms-office' : 'MsOffice', | |
'application/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' | |
}, | |
/** | |
* Ajax request data validation rules | |
* | |
* @type Object | |
*/ | |
rules : { | |
defaults : function(data) { | |
if (!data | |
|| (data.added && !$.isArray(data.added)) | |
|| (data.removed && !$.isArray(data.removed)) | |
|| (data.changed && !$.isArray(data.changed))) { | |
return false; | |
} | |
return true; | |
}, | |
open : function(data) { return data && data.cwd && data.files && $.isPlainObject(data.cwd) && $.isArray(data.files); }, | |
tree : function(data) { return data && data.tree && $.isArray(data.tree); }, | |
parents : function(data) { return data && data.tree && $.isArray(data.tree); }, | |
tmb : function(data) { return data && data.images && ($.isPlainObject(data.images) || $.isArray(data.images)); }, | |
upload : function(data) { return data && ($.isPlainObject(data.added) || $.isArray(data.added));}, | |
search : function(data) { return data && data.files && $.isArray(data.files)} | |
}, | |
/** | |
* Commands costructors | |
* | |
* @type Object | |
*/ | |
commands : {}, | |
/** | |
* Commands to add the item (space delimited) | |
* | |
* @type String | |
*/ | |
cmdsToAdd : 'archive duplicate extract mkdir mkfile paste rm upload', | |
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 = $.merge((data.removed || []), $.map(data.added||[], function(f) { return f.hash; })); | |
return data; | |
}, | |
iframeCnt : 0, | |
uploads : { | |
// xhr muiti uploading flag | |
xhrUploading: false, | |
// check file/dir exists | |
checkExists: function(files, target, fm) { | |
var dfrd = $.Deferred(), | |
names, name, | |
cancel = function() { | |
var i = files.length; | |
while (--i > -1) { | |
files[i]._remove = true; | |
} | |
}, | |
check = function() { | |
var renames = [], hashes = {}, existed = [], exists = [], i, c; | |
var confirm = function(ndx) { | |
var last = ndx == exists.length-1, | |
opts = { | |
title : fm.i18n('cmdupload'), | |
text : ['errExists', exists[ndx].name, 'confirmRepl'], | |
all : !last, | |
accept : { | |
label : 'btnYes', | |
callback : function(all) { | |
!last && !all | |
? confirm(++ndx) | |
: dfrd.resolve(renames, hashes); | |
} | |
}, | |
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) | |
: dfrd.resolve(renames, hashes); | |
} | |
}, | |
cancel : { | |
label : 'btnCancel', | |
callback : function() { | |
cancel(); | |
dfrd.resolve(renames, hashes); | |
} | |
}, | |
buttons : [ | |
{ | |
label : 'btnBackup', | |
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) | |
: dfrd.resolve(renames, hashes); | |
} | |
} | |
] | |
}; | |
if (fm.iframeCnt > 0) { | |
delete opts.reject; | |
} | |
fm.confirm(opts); | |
}; | |
names = $.map(files, function(file, i) { return file.name? {i: i, name: file.name} : null ;}); | |
name = $.map(names, function(item) { return item.name;}); | |
fm.request({ | |
data : {cmd : 'ls', target : target, intersect : name}, | |
notify : {type : 'preupload', cnt : 1, hideCnt : true}, | |
preventFail : true | |
}) | |
.done(function(data) { | |
if (data) { | |
if (data.error) { | |
cancel(); | |
} else { | |
if (fm.option('uploadOverwrite')) { | |
if (data.list) { | |
if ($.isArray(data.list)) { | |
existed = data.list || []; | |
} else { | |
existed = $.map(data.list, function(n) { return n; }); | |
hashes = data.list; | |
} | |
exists = $.map(names, function(name){ return $.inArray(name.name, existed) !== -1 ? name : null ;}); | |
if (target == fm.cwd().hash && | |
$($.map(fm.files(), function(file) { return (file.phash == target) ? file.name : null; } )) | |
.filter(existed).length < 1 | |
) { | |
fm.sync(); | |
} | |
} | |
} | |
} | |
} | |
if (exists.length > 0) { | |
confirm(0); | |
} else { | |
dfrd.resolve([]); | |
} | |
}) | |
.fail(function(error) { | |
cancel(); | |
dfrd.resolve([]); | |
error && fm.error(error); | |
}); | |
}; | |
if (fm.api >= 2.1 && typeof files[0] == 'object') { | |
check(); | |
return dfrd; | |
} else { | |
return dfrd.resolve([]); | |
} | |
}, | |
// check droped contents | |
checkFile : function(data, fm, target) { | |
if (!!data.checked || data.type == 'files') { | |
return data.files; | |
} else if (data.type == 'data') { | |
var dfrd = $.Deferred(), | |
files = [], | |
paths = [], | |
dirctorys = [], | |
entries = [], | |
processing = 0, | |
items, | |
readEntries = function(dirReader) { | |
var toArray = function(list) { | |
return Array.prototype.slice.call(list || []); | |
}; | |
var readFile = function(fileEntry, callback) { | |
var dfrd = $.Deferred(); | |
if (typeof fileEntry == 'undefined') { | |
dfrd.reject('empty'); | |
} else if (fileEntry.isFile) { | |
fileEntry.file(function (file) { | |
dfrd.resolve(file); | |
}, function(e){ | |
dfrd.reject(); | |
}); | |
} else { | |
dfrd.reject('dirctory'); | |
} | |
return dfrd.promise(); | |
}; | |
dirReader.readEntries(function (results) { | |
if (!results.length) { | |
var len = entries.length - 1; | |
var read = function(i) { | |
readFile(entries[i]).done(function(file){ | |
if (! (fm.OS == 'win' && file.name.match(/^(?:desktop\.ini|thumbs\.db)$/i)) | |
&& | |
! (fm.OS == 'mac' && file.name.match(/^\.ds_store$/i))) { | |
paths.push(entries[i].fullPath); | |
files.push(file); | |
} | |
}).fail(function(e){ | |
if (e == 'dirctory') { | |
// dirctory | |
dirctorys.push(entries[i]); | |
} else if (e == 'empty') { | |
// dirctory is empty | |
} else { | |
// why fail? | |
} | |
}).always(function(){ | |
processing--; | |
if (i < len) { | |
processing++; | |
read(++i); | |
} | |
}); | |
}; | |
processing++; | |
read(0); | |
processing--; | |
} else { | |
entries = entries.concat(toArray(results)); | |
readEntries(dirReader); | |
} | |
}); | |
}, | |
doScan = function(items, isEntry) { | |
var dirReader, entry; | |
entries = []; | |
var length = items.length; | |
for (var i = 0; i < length; i++) { | |
if (! isEntry) { | |
entry = !!items[i].getAsEntry? items[i].getAsEntry() : items[i].webkitGetAsEntry(); | |
} else { | |
entry = items[i]; | |
} | |
if (entry) { | |
if (entry.isFile) { | |
processing++; | |
entry.file(function (file) { | |
paths.push(''); | |
files.push(file); | |
processing--; | |
}); | |
} else if (entry.isDirectory) { | |
if (fm.api >= 2.1) { | |
if (processing > 0) { | |
dirctorys.push(entry); | |
} else { | |
processing = 0; | |
dirReader = entry.createReader(); | |
processing++; | |
readEntries(dirReader); | |
} | |
} | |
} | |
} | |
} | |
}; | |
items = $.map(data.files.items, function(item){ | |
return item.getAsEntry? item.getAsEntry() : item.webkitGetAsEntry(); | |
}); | |
if (items.length > 0) { | |
fm.uploads.checkExists(items, target, fm).done(function(renames, hashes){ | |
var notifyto, dfds = []; | |
if (fm.option('uploadOverwrite')) { | |
items = $.map(items, function(item){ | |
var i, bak, hash, dfd, hi; | |
if (item.isDirectory) { | |
i = $.inArray(item.name, renames); | |
if (i !== -1) { | |
renames.splice(i, 1); | |
bak = fm.uniqueName(item.name + fm.options.backupSuffix , null, ''); | |
$.each(hashes, function(h, name) { | |
if (item.name == name) { | |
hash = h; | |
return false; | |
} | |
}); | |
if (! hash) { | |
hash = fm.fileByName(item.name, target).hash; | |
} | |
fm.lockfiles({files : [hash]}); | |
dfd = fm.request({ | |
data : {cmd : 'rename', target : hash, name : bak}, | |
notify : {type : 'rename', cnt : 1} | |
}) | |
.fail(function(error) { | |
item._remove = true; | |
fm.sync(); | |
}) | |
.always(function() { | |
fm.unlockfiles({files : [hash]}) | |
}); | |
dfds.push(dfd); | |
} | |
} | |
return !item._remove? item : null; | |
}); | |
} | |
$.when.apply($, dfds).done(function(){ | |
if (items.length > 0) { | |
notifyto = setTimeout(function() { | |
fm.notify({type : 'readdir', cnt : 1, hideCnt: true}); | |
}, fm.options.notifyDelay); | |
doScan(items, true); | |
setTimeout(function wait() { | |
if (processing > 0) { | |
setTimeout(wait, 10); | |
} else { | |
if (dirctorys.length > 0) { | |
doScan([dirctorys.shift()], true); | |
setTimeout(wait, 10); | |
} else { | |
notifyto && clearTimeout(notifyto); | |
fm.notify({type : 'readdir', cnt : -1}); | |
dfrd.resolve([files, paths, renames, hashes]); | |
} | |
} | |
}, 10); | |
} 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 = $("<html/>").append($.parseHTML(str)), | |
atag; | |
$('img[src]', tmp).each(function(){ | |
var url, purl, | |
self = $(this), | |
pa = self.closest('a'); | |
if (pa && pa.attr('href') && pa.attr('href').match(/\.(?:jpe?g|gif|bmp|png)/i)) { | |
purl = pa.attr('href'); | |
} | |
url = self.attr('src'); | |
if (url) { | |
if (purl) { | |
$.inArray(purl, ret) == -1 && ret.push(purl); | |
$.inArray(url, check) == -1 && check.push(url); | |
} else { | |
$.inArray(url, ret) == -1 && ret.push(url); | |
} | |
} | |
}); | |
atag = $('a[href]', tmp); | |
atag.each(function(){ | |
var loc, | |
parseUrl = function(url) { | |
var a = document.createElement('a'); | |
a.href = url; | |
return a; | |
}; | |
if ($(this).text()) { | |
loc = parseUrl($(this).attr('href')); | |
if (loc.href && (atag.length === 1 || ! loc.pathname.match(/(?:\.html?|\/[^\/.]*)$/i))) { | |
if ($.inArray(loc.href, ret) == -1 && $.inArray(loc.href, check) == -1) ret.push(loc.href); | |
} | |
} | |
}); | |
} else { | |
var regex, m, url; | |
regex = /(http[^<>"{}|\\^\[\]`\s]+)/ig; | |
while (m = regex.exec(str)) { | |
url = m[1].replace(/&/g, '&'); | |
if ($.inArray(url, ret) == -1) ret.push(url); | |
} | |
} | |
return ret; | |
} | |
}, | |
// upload transport using XMLHttpRequest | |
xhr : function(data, fm) { | |
var self = fm ? fm : this, | |
node = self.getUI(), | |
xhr = new XMLHttpRequest(), | |
notifyto = null, notifyto2 = null, | |
dataChecked = data.checked, | |
isDataType = (data.isDataType || data.type == 'data'), | |
retry = 0, | |
dfrd = $.Deferred() | |
.fail(function(error) { | |
if (self.uploads.xhrUploading) { | |
setTimeout(function() { self.sync(); }, 5000); | |
self.uploads.xhrUploading = false; | |
var file = files.length? (isDataType? files[0][0] : files[0]) : {}; | |
if (file._cid) { | |
formData = new FormData(); | |
files = [{_chunkfail: true}]; | |
formData.append('chunk', file._chunk); | |
formData.append('cid' , file._cid); | |
isDataType = false; | |
send(files); | |
} | |
} | |
files = null; | |
error && self.error(error); | |
}) | |
.done(function(data) { | |
xhr = null; | |
files = null; | |
if (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() { | |
// unregist fnAbort function | |
node.off('uploadabort', fnAbort); | |
$(window).off('unload', fnAbort); | |
notifyto && clearTimeout(notifyto); | |
notifyto2 && clearTimeout(notifyto2); | |
dataChecked && !data.multiupload && checkNotify() && self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0}); | |
chunkMerge && self.ui.notify.children('.elfinder-notify-chunkmerge').length && self.notify({type : 'chunkmerge', cnt : -1}); | |
}), | |
formData = new FormData(), | |
target = (data.target || self.cwd().hash), | |
files = data.input ? data.input.files : self.uploads.checkFile(data, self, target), | |
cnt = data.checked? (isDataType? files[0].length : files.length) : files.length, | |
loaded = 0, prev, | |
filesize = 0, | |
notify = false, | |
abort = false, | |
checkNotify = function() { | |
return notify = (notify || self.ui.notify.children('.elfinder-notify-upload').length); | |
}, | |
fnAbort = function() { | |
abort = true; | |
if (xhr) { | |
xhr.quiet = true; | |
xhr.abort(); | |
} | |
}, | |
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'); | |
} | |
}); | |
prev = loaded; | |
}, self.options.notifyDelay); | |
}, | |
renames = (data.renames || null), | |
hashes = (data.hashes || null), | |
chunkMerge = false; | |
// regist fnAbort function | |
node.one('uploadabort', fnAbort); | |
$(window).one('unload', fnAbort); | |
!chunkMerge && (prev = loaded); | |
if (!isDataType && !cnt) { | |
return dfrd.reject(['errUploadNoFiles']); | |
} | |
xhr.addEventListener('error', function() { | |
dfrd.reject('errConnect'); | |
}, false); | |
xhr.addEventListener('abort', function() { | |
dfrd.reject(['errConnect', 'errAbort']); | |
}, false); | |
xhr.addEventListener('load', function(e) { | |
var status = xhr.status, res, curr = 0, error = ''; | |
if (status >= 400) { | |
if (status > 500) { | |
error = 'errResponse'; | |
} else { | |
error = 'errConnect'; | |
} | |
} else { | |
if (xhr.readyState != 4) { | |
error = ['errConnect', 'errTimeout']; // am i right? | |
} | |
if (!xhr.responseText) { | |
error = ['errResponse', 'errDataEmpty']; | |
} | |
} | |
if (error) { | |
if (chunkMerge || retry++ > 3) { | |
var file = isDataType? files[0][0] : files[0]; | |
return dfrd.reject(file._cid? null : error); | |
} else { | |
// do retry | |
filesize = 0; | |
xhr.open('POST', self.uploadURL, true); | |
xhr.send(formData); | |
return; | |
} | |
} | |
loaded = filesize; | |
if (checkNotify() && (curr = loaded - prev)) { | |
self.notify({type : 'upload', cnt : 0, progress : curr, size : 0}); | |
} | |
res = self.parseUploadData(xhr.responseText); | |
// chunked upload commit | |
if (res._chunkmerged) { | |
formData = new FormData(); | |
var _file = [{_chunkmerged: res._chunkmerged, _name: res._name}]; | |
chunkMerge = true; | |
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) { | |
if (res._chunkfailure) { | |
abort = true; | |
self.uploads.xhrUploading = false; | |
notifyto && clearTimeout(notifyto); | |
if (self.ui.notify.children('.elfinder-notify-upload').length) { | |
self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0}); | |
dfrd.reject(res.error); | |
} else { | |
// for multi connection | |
dfrd.reject(); | |
} | |
} else { | |
dfrd.reject(res.error); | |
} | |
} 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; | |
} | |
} | |
}, false); | |
xhr.upload.addEventListener('progress', function(e) { | |
var curr; | |
if (e.lengthComputable && !chunkMerge) { | |
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) { | |
retry && (loaded = 0); | |
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; | |
} | |
} | |
}, false); | |
var send = function(files, paths){ | |
var size = 0, | |
fcnt = 1, | |
sfiles = [], | |
c = 0, | |
total = cnt, | |
maxFileSize, | |
totalSize = 0, | |
chunked = [], | |
chunkID = new Date().getTime(), | |
BYTES_PER_CHUNK = Math.min((fm.uplMaxSize? fm.uplMaxSize : 2097152) - 8190, fm.options.uploadMaxChunkSize), // uplMaxSize margin 8kb or options.uploadMaxChunkSize | |
blobSlice = false, | |
blobSize, i, start, end, chunks, blob, chunk, added, done, last, failChunk, | |
multi = function(files, num){ | |
var sfiles = [], cid; | |
if (!abort) { | |
while(files.length && sfiles.length < num) { | |
sfiles.push(files.shift()); | |
} | |
if (sfiles.length) { | |
for (var i=0; i < sfiles.length; i++) { | |
if (abort) { | |
break; | |
} | |
cid = isDataType? (sfiles[i][0][0]._cid || null) : (sfiles[i][0]._cid || null); | |
if (!!failChunk[cid]) { | |
last--; | |
continue; | |
} | |
fm.exec('upload', { | |
type: data.type, | |
isDataType: isDataType, | |
files: sfiles[i], | |
checked: true, | |
target: target, | |
renames: renames, | |
hashes: hashes, | |
multiupload: true}) | |
.fail(function(error) { | |
if (cid) { | |
failChunk[cid] = true; | |
} | |
}) | |
.always(function(e) { | |
if (e && e.added) added = $.merge(added, e.added); | |
if (last <= ++done) { | |
fm.trigger('multiupload', {added: added}); | |
notifyto && clearTimeout(notifyto); | |
if (checkNotify()) { | |
self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0}); | |
} | |
} | |
multi(files, 1); // Next one | |
}); | |
} | |
} | |
} | |
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, 3); // Max connection: 3 | |
} else { | |
setTimeout(function(){ check(); }, 100); | |
} | |
}; | |
if (! dataChecked && (isDataType || data.type == 'files')) { | |
maxFileSize = fm.option('uploadMaxSize')? fm.option('uploadMaxSize') : 0; | |
for (i=0; i < files.length; i++) { | |
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'; | |
} | |
} | |
} | |
if ((maxFileSize && blobSize > maxFileSize) || (!blobSlice && fm.uplMaxSize && blobSize > fm.uplMaxSize)) { | |
self.error(self.i18n('errUploadFile', blob.name) + ' ' + self.i18n('errUploadFileSize')); | |
cnt--; | |
total--; | |
continue; | |
} | |
if (blobSlice && blobSize > BYTES_PER_CHUNK) { | |
start = 0; | |
end = BYTES_PER_CHUNK; | |
chunks = -1; | |
total = Math.floor(blobSize / BYTES_PER_CHUNK); | |
totalSize += blobSize; | |
chunked[chunkID] = 0; | |
while(start <= blobSize) { | |
chunk = blob[blobSlice](start, end); | |
chunk._chunk = blob.name + '.' + (++chunks) + '_' + total + '.part'; | |
chunk._cid = chunkID; | |
chunk._range = start + ',' + chunk.size + ',' + blobSize; | |
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) { | |
self.error(self.i18n('errUploadFile', blob.name) + ' ' + self.i18n('errUploadFileSize')); | |
cnt--; | |
total--; | |
} else { | |
total += chunks; | |
} | |
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 (sfiles.length == 0) { | |
// no data | |
data.checked = true; | |
return false; | |
} | |
if (sfiles.length > 1) { | |
// multi upload | |
notifyto = startNotify(totalSize); | |
added = []; | |
done = 0; | |
last = sfiles.length; | |
failChunk = []; | |
check(); | |
return true; | |
} | |
// single upload | |
if (isDataType) { | |
files = sfiles[0][0]; | |
paths = sfiles[0][1]; | |
} else { | |
files = sfiles[0]; | |
} | |
} | |
if (!dataChecked) { | |
if (!fm.UA.Safari || !data.files) { | |
notifyto = startNotify(totalSize); | |
} else { | |
xhr._totalSize = totalSize; | |
} | |
} | |
dataChecked = true; | |
if (! files.length) { | |
dfrd.reject(['errUploadNoFiles']); | |
} | |
xhr.open('POST', self.uploadURL, true); | |
// set request headers | |
if (fm.customHeaders) { | |
$.each(fm.customHeaders, function(key) { | |
xhr.setRequestHeader(key, this); | |
}); | |
} | |
// set xhrFields | |
if (fm.xhrFields) { | |
$.each(fm.xhrFields, function(key) { | |
if (key in xhr) { | |
xhr[key] = this; | |
} | |
}); | |
} | |
formData.append('cmd', 'upload'); | |
formData.append(self.newAPI ? 'target' : 'current', target); | |
if (renames && renames.length) { | |
$.each(renames, function(i, v) { | |
formData.append('renames[]', v); | |
}); | |
formData.append('suffix', fm.options.backupSuffix); | |
} | |
if (hashes) { | |
$.each(hashes, function(i, v) { | |
formData.append('hashes['+ i +']', v); | |
}); | |
} | |
$.each(self.options.customData, function(key, val) { | |
formData.append(key, val); | |
}); | |
$.each(self.options.onlyMimes, function(i, mime) { | |
formData.append('mimes['+i+']', mime); | |
}); | |
$.each(files, function(i, file) { | |
if (file._chunkmerged) { | |
formData.append('chunk', file._chunkmerged); | |
formData.append('upload[]', file._name); | |
} else { | |
if (file._chunkfail) { | |
formData.append('upload[]', 'chunkfail'); | |
formData.append('mimes', 'chunkfail'); | |
} else { | |
formData.append('upload[]', file); | |
} | |
if (file._chunk) { | |
formData.append('chunk', file._chunk); | |
formData.append('cid' , file._cid); | |
formData.append('range', file._range); | |
} | |
} | |
}); | |
if (isDataType) { | |
$.each(paths, function(i, path) { | |
formData.append('upload_path[]', path); | |
}); | |
} | |
xhr.onreadystatechange = function() { | |
if (xhr.readyState == 4 && xhr.status == 0) { | |
if (abort) { | |
dfrd.reject(); | |
} else { | |
var errors = ['errAbort']; | |
// ff bug while send zero sized file | |
// for safari - send directory | |
if (!isDataType && data.files && $.map(data.files, function(f){return f.size === 0? f : null;}).length) { | |
errors.push('errFolderUpload'); | |
} | |
dfrd.reject(errors); | |
} | |
} | |
}; | |
xhr.send(formData); | |
return true; | |
}; | |
if (! isDataType) { | |
if (files.length > 0) { | |
if (renames == null) { | |
renames = []; | |
hashes = {}; | |
self.uploads.checkExists(files, target, fm).done( | |
function(res, res2){ | |
if (fm.option('uploadOverwrite')) { | |
renames = res; | |
hashes = res2; | |
files = $.map(files, function(file){return !file._remove? file : null ;}); | |
} | |
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){ | |
renames = []; | |
hashes = {}; | |
cnt = result[0].length; | |
if (cnt) { | |
renames = result[2]; | |
hashes = result[3]; | |
send(result[0], result[1]); | |
} else { | |
dfrd.reject(['errUploadNoFiles']); | |
} | |
}).fail(function(){ | |
dfrd.reject(); | |
}); | |
} | |
} | |
return dfrd; | |
}, | |
// upload transport using iframe | |
iframe : function(data, fm) { | |
var self = fm ? fm : this, | |
input = data.input? data.input : false, | |
files = !input ? self.uploads.checkFile(data, self) : false, | |
dfrd = $.Deferred() | |
.fail(function(error) { | |
error && self.error(error); | |
}) | |
.done(function(data) { | |
data.warning && self.error(data.warning); | |
data.removed && self.remove(data); | |
data.added && self.add(data); | |
data.changed && self.change(data); | |
self.trigger('upload', data); | |
data.sync && self.sync(); | |
}), | |
name = 'iframe-'+self.namespace+(++self.iframeCnt), | |
form = $('<form action="'+self.uploadURL+'" method="post" enctype="multipart/form-data" encoding="multipart/form-data" target="'+name+'" style="display:none"><input type="hidden" name="cmd" value="upload" /></form>'), | |
msie = this.UA.IE, | |
// clear timeouts, close notification dialog, remove form/iframe | |
onload = function() { | |
abortto && clearTimeout(abortto); | |
notifyto && clearTimeout(notifyto); | |
notify && self.notify({type : 'upload', cnt : -cnt}); | |
setTimeout(function() { | |
msie && $('<iframe src="javascript:false;"/>').appendTo(form); | |
form.remove(); | |
iframe.remove(); | |
}, 100); | |
}, | |
iframe = $('<iframe src="'+(msie ? 'javascript:false;' : 'about:blank')+'" name="'+name+'" style="position:absolute;left:-1000px;top:-1000px" />') | |
.on('load', function() { | |
iframe.off('load') | |
.on('load', function() { | |
//var data = self.parseUploadData(iframe.contents().text()); | |
onload(); | |
dfrd.reject(); | |
//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(); | |
}), | |
target = (data.target || self.cwd().hash), | |
names = [], | |
dfds = [], | |
renames = [], | |
hashes = {}, | |
cnt, notify, notifyto, abortto; | |
if (files && files.length) { | |
$.each(files, function(i, val) { | |
form.append('<input type="hidden" name="upload[]" value="'+val+'"/>'); | |
}); | |
cnt = 1; | |
} else if (input && $(input).is(':file') && $(input).val()) { | |
if (fm.option('uploadOverwrite')) { | |
names = input.files? input.files : [{ name: $(input).val().replace(/^(?:.+[\\\/])?([^\\\/]+)$/, '$1') }]; | |
//names = $.map(names, function(file){return file.name? { name: file.name } : null ;}); | |
dfds.push(self.uploads.checkExists(names, target, self).done( | |
function(res, res2){ | |
renames = res; | |
hashes = res2; | |
cnt = $.map(names, function(file){return !file._remove? file : null ;}).length; | |
if (cnt != names.length) { | |
cnt = 0; | |
} | |
} | |
)); | |
} | |
cnt = input.files ? input.files.length : 1; | |
form.append(input); | |
} else { | |
return dfrd.reject(); | |
} | |
$.when.apply($, dfds).done(function() { | |
if (cnt < 1) { | |
return dfrd.reject(); | |
} | |
form.append('<input type="hidden" name="'+(self.newAPI ? 'target' : 'current')+'" value="'+target+'"/>') | |
.append('<input type="hidden" name="html" value="1"/>') | |
.append('<input type="hidden" name="node" value="'+self.id+'"/>') | |
.append($(input).attr('name', 'upload[]')); | |
if (renames.length > 0) { | |
$.each(renames, function(i, rename) { | |
form.append('<input type="hidden" name="renames[]" value="'+self.escape(rename)+'"/>'); | |
}); | |
form.append('<input type="hidden" name="suffix" value="'+fm.options.backupSuffix+'"/>'); | |
} | |
if (hashes) { | |
$.each(renames, function(i, v) { | |
form.append('<input type="hidden" name="['+i+']" value="'+self.escape(v)+'"/>'); | |
}); | |
} | |
$.each(self.options.onlyMimes||[], function(i, mime) { | |
form.append('<input type="hidden" name="mimes[]" value="'+self.escape(mime)+'"/>'); | |
}); | |
$.each(self.options.customData, function(key, val) { | |
form.append('<input type="hidden" name="'+key+'" value="'+self.escape(val)+'"/>'); | |
}); | |
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 | |
* @return elFinder | |
*/ | |
one : function(event, callback) { | |
var self = this, | |
h = function(e, f) { | |
setTimeout(function() {self.unbind(event, h);}, 3); | |
return callback.apply(self.getListeners(e.type), arguments); | |
}; | |
return this.bind(event, h); | |
}, | |
/** | |
* Set/get data into/from localStorage | |
* | |
* @param String key | |
* @param String|void value | |
* @return String | |
*/ | |
localStorage : function(key, val) { | |
var s = window.localStorage, | |
oldkey = 'elfinder-'+key+this.id, // old key of elFinder < 2.1.6 | |
retval, oldval; | |
// new key of elFinder >= 2.1.6 | |
key = window.location.pathname+'-elfinder-'+key+this.id; | |
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)) { | |
try { | |
s.setItem(key, val); | |
} catch (e) { | |
s.clear(); | |
s.setItem(key, val); | |
} | |
retval = s.getItem(key); | |
} | |
return retval; | |
}, | |
/** | |
* Get/set cookie | |
* | |
* @param String cookie name | |
* @param String|void cookie value | |
* @return String | |
*/ | |
cookie : function(name, value) { | |
var d, o, c, i; | |
name = 'elfinder-'+name+this.id; | |
if (value === void(0)) { | |
if (document.cookie && document.cookie != '') { | |
c = document.cookie.split(';'); | |
name += '='; | |
for (i=0; i<c.length; i++) { | |
c[i] = $.trim(c[i]); | |
if (c[i].substring(0, name.length) == name) { | |
return decodeURIComponent(c[i].substring(name.length)); | |
} | |
} | |
} | |
return ''; | |
} | |
o = $.extend({}, this.options.cookie); | |
if (value === null) { | |
value = ''; | |
o.expires = -1; | |
} | |
if (typeof(o.expires) == 'number') { | |
d = new Date(); | |
d.setTime(d.getTime()+(o.expires * 86400000)); | |
o.expires = d; | |
} | |
document.cookie = name+'='+encodeURIComponent(value)+'; expires='+o.expires.toUTCString()+(o.path ? '; path='+o.path : '')+(o.domain ? '; domain='+o.domain : '')+(o.secure ? '; secure' : ''); | |
return value; | |
}, | |
/** | |
* Get start directory (by location.hash or last opened directory) | |
* | |
* @return String | |
*/ | |
startDir : function() { | |
var locHash = window.location.hash; | |
if (locHash && locHash.match(/^#elf_/)) { | |
return locHash.replace(/^#elf_/, ''); | |
} else if (this.options.startPathHash) { | |
return this.options.startPathHash; | |
} else { | |
return this.lastDir(); | |
} | |
}, | |
/** | |
* Get/set last opened directory | |
* | |
* @param String|undefined dir hash | |
* @return String | |
*/ | |
lastDir : function(hash) { | |
return this.options.rememberLastDir ? this.storage('lastdir', hash) : ''; | |
}, | |
/** | |
* Node for escape html entities in texts | |
* | |
* @type jQuery | |
*/ | |
_node : $('<span/>'), | |
/** | |
* Replace not html-safe symbols to html entities | |
* | |
* @param String text to escape | |
* @return String | |
*/ | |
escape : function(name) { | |
return this._node.text(name).html().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 filter = function(file) { | |
if (file && file.hash && file.name && file.mime) { | |
if (file.mime == 'application/x-empty') { | |
file.mime = 'text/plain'; | |
} | |
return file; | |
} | |
return null; | |
}; | |
if (data.files) { | |
data.files = $.map(data.files, filter); | |
} | |
if (data.tree) { | |
data.tree = $.map(data.tree, filter); | |
} | |
if (data.added) { | |
data.added = $.map(data.added, filter); | |
} | |
if (data.changed) { | |
data.changed = $.map(data.changed, filter); | |
} | |
if (data.api) { | |
data.init = true; | |
} | |
return data; | |
}, | |
/** | |
* Update sort options | |
* | |
* @param {String} sort type | |
* @param {String} sort order | |
* @param {Boolean} show folder first | |
*/ | |
setSort : function(type, order, stickFolders) { | |
this.storage('sortType', (this.sortType = this.sortRules[type] ? type : 'name')); | |
this.storage('sortOrder', (this.sortOrder = /asc|desc/.test(order) ? order : 'asc')); | |
this.storage('sortStickFolders', (this.sortStickFolders = !!stickFolders) ? 1 : ''); | |
this.trigger('sortchange'); | |
}, | |
_sortRules : { | |
name : function(file1, file2) { | |
var n1 = file1.name.toLowerCase(), | |
n2 = file2.name.toLowerCase(), | |
e1 = '', | |
e2 = '', | |
so = elFinder.prototype.naturalCompare, | |
m, ret; | |
if (m = n1.match(/^(.+)(\.[0-9a-z.]+)$/)) { | |
n1 = m[1]; | |
e1 = m[2]; | |
} | |
if (m = n2.match(/^(.+)(\.[0-9a-z.]+)$/)) { | |
n2 = m[1]; | |
e2 = m[2]; | |
} | |
ret = so(n1, n2); | |
if (ret == 0 && (e1 || e2) && e1 != e2) { | |
ret = so(e1, e2); | |
} | |
return ret; | |
}, | |
size : function(file1, file2) { | |
var size1 = parseInt(file1.size) || 0, | |
size2 = parseInt(file2.size) || 0; | |
return size1 == size2 ? 0 : size1 > size2 ? 1 : -1; | |
}, | |
kind : function(file1, file2) { | |
return elFinder.prototype.naturalCompare(file1.mime, file2.mime); | |
}, | |
date : function(file1, file2) { | |
var date1 = file1.ts || file1.date, | |
date2 = file2.ts || file2.date; | |
return date1 == date2 ? 0 : date1 > date2 ? 1 : -1 | |
} | |
}, | |
/** | |
* Compare 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 type = opts.type, | |
msg = this.messages['ntf'+type] ? this.i18n('ntf'+type) : this.i18n('ntfsmth'), | |
ndialog = this.ui.notify, | |
notify = ndialog.children('.elfinder-notify-'+type), | |
button = notify.children('div.elfinder-notify-cancel').children('button'), | |
ntpl = '<div class="elfinder-notify elfinder-notify-{type}"><span class="elfinder-dialog-icon elfinder-dialog-icon-{type}"/><span class="elfinder-notify-msg">{msg}</span> <span class="elfinder-notify-cnt"/><div class="elfinder-notify-progressbar"><div class="elfinder-notify-progress"/></div><div class="elfinder-notify-cancel"/></div>', | |
delta = opts.cnt, | |
size = (typeof opts.size != 'undefined')? parseInt(opts.size) : null, | |
progress = (typeof opts.progress != 'undefined' && opts.progress >= 0) ? opts.progress : null, | |
cancel = opts.cancel, | |
clhover = 'ui-state-hover', | |
close = function() { | |
notify._esc && $(document).off('keydown', notify._esc); | |
notify.remove(); | |
!ndialog.children().length && ndialog.elfinderdialog('close'); | |
}, | |
cnt, total, prc; | |
if (!type) { | |
return this; | |
} | |
if (!notify.length) { | |
notify = $(ntpl.replace(/\{type\}/g, type).replace(/\{msg\}/g, msg)) | |
.appendTo(ndialog) | |
.data('cnt', 0); | |
if (progress != null) { | |
notify.data({progress : 0, total : 0}); | |
} | |
if (cancel) { | |
button = $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">'+this.i18n('btnCancel')+'</span></button>') | |
.hover(function(e) { | |
$(this).toggleClass(clhover, e.type == 'mouseenter'); | |
}); | |
notify.children('div.elfinder-notify-cancel').append(button); | |
} | |
} | |
cnt = delta + parseInt(notify.data('cnt')); | |
if (cnt > 0) { | |
if (cancel && button.length) { | |
if ($.isFunction(cancel) || (typeof cancel === 'object' && cancel.promise)) { | |
notify._esc = function(e) { | |
if (e.type == 'keydown' && e.keyCode != $.ui.keyCode.ESCAPE) { | |
return; | |
} | |
e.preventDefault(); | |
e.stopPropagation(); | |
close(); | |
if (cancel.promise) { | |
if (cancel.xhr) { | |
cancel.xhr.quiet = true; | |
cancel.xhr.abort(); | |
} | |
cancel.reject(); | |
} else { | |
cancel(e); | |
} | |
}; | |
button.on('click', function(e) { | |
notify._esc(e); | |
}); | |
$(document).on('keydown', notify._esc); | |
} | |
} | |
!opts.hideCnt && notify.children('.elfinder-notify-cnt').text('('+cnt+')'); | |
ndialog.is(':hidden') && ndialog.elfinderdialog('open'); | |
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 = parseInt(prc/total); | |
notify.find('.elfinder-notify-progress') | |
.animate({ | |
width : (progress < 100 ? progress : 100)+'%' | |
}, 20); | |
} | |
} else { | |
close(); | |
} | |
return this; | |
}, | |
/** | |
* Open confirmation dialog | |
* | |
* @param Object options | |
* @example | |
* this.confirm({ | |
* title : 'Remove files', | |
* text : 'Here is question text', | |
* accept : { // accept callback - required | |
* label : 'Continue', | |
* callback : function(applyToAll) { fm.log('Ok') } | |
* }, | |
* cancel : { // cancel callback - required | |
* label : 'Cancel', | |
* callback : function() { fm.log('Cancel')} | |
* }, | |
* reject : { // reject callback - optionally | |
* label : 'No', | |
* callback : function(applyToAll) { fm.log('No')} | |
* }, | |
* buttons : [ // additional buttons callback - optionally | |
* { | |
* label : 'Btn1', | |
* callback : function(applyToAll) { fm.log('Btn1')} | |
* } | |
* ], | |
* all : true // display checkbox "Apply to all" | |
* }) | |
* @return elFinder | |
*/ | |
confirm : function(opts) { | |
var self = this, | |
complete = false, | |
options = { | |
cssClass : 'elfinder-dialog-confirm', | |
modal : true, | |
resizable : false, | |
title : this.i18n(opts.title || 'confirmReq'), | |
buttons : {}, | |
close : function() { | |
!complete && opts.cancel.callback(); | |
$(this).elfinderdialog('destroy'); | |
} | |
}, | |
apply = this.i18n('apllyAll'), | |
label, checkbox; | |
options.buttons[this.i18n(opts.accept.label)] = function() { | |
opts.accept.callback(!!(checkbox && checkbox.prop('checked'))) | |
complete = true; | |
$(this).elfinderdialog('close') | |
}; | |
if (opts.reject) { | |
options.buttons[this.i18n(opts.reject.label)] = function() { | |
opts.reject.callback(!!(checkbox && checkbox.prop('checked'))) | |
complete = true; | |
$(this).elfinderdialog('close') | |
}; | |
} | |
if (opts.buttons && opts.buttons.length > 0) { | |
$.each(opts.buttons, function(i, v){ | |
options.buttons[self.i18n(v.label)] = function() { | |
v.callback(!!(checkbox && checkbox.prop('checked'))) | |
complete = true; | |
$(this).elfinderdialog('close'); | |
}; | |
}); | |
} | |
options.buttons[this.i18n(opts.cancel.label)] = function() { | |
$(this).elfinderdialog('close') | |
}; | |
if (opts.all) { | |
options.create = function() { | |
var base = $('<div class="elfinder-dialog-confirm-applyall"/>'); | |
checkbox = $('<input type="checkbox" />'); | |
$(this).next().find('.ui-dialog-buttonset') | |
.prepend(base.append($('<label>'+apply+'</label>').prepend(checkbox))); | |
} | |
} | |
return this.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-confirm"/>' + this.i18n(opts.text), options); | |
}, | |
/** | |
* Create unique file name in required dir | |
* | |
* @param String file name | |
* @param String parent dir hash | |
* @param String glue | |
* @return String | |
*/ | |
uniqueName : function(prefix, phash, glue) { | |
var i = 0, ext = '', p, name; | |
prefix = this.i18n(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; | |
for (i = 0; i< arguments.length; i++) { | |
m = arguments[i]; | |
if (typeof m == 'string') { | |
input.push(message(m)); | |
} else if ($.isArray(m)) { | |
for (j = 0; j < m.length; j++) { | |
if (typeof m[j] == 'string') { | |
input.push(message(m[j])); | |
} else if (m[j] instanceof jQuery) { | |
// jQuery object is HTML element | |
input.push(m[j]); | |
} | |
} | |
} else if (m instanceof jQuery) { | |
// jQuery object is HTML element | |
input.push(m[j]); | |
} | |
} | |
for (i = 0; i < input.length; i++) { | |
// dont translate placeholders | |
if ($.inArray(i, ignore) !== -1) { | |
continue; | |
} | |
m = input[i]; | |
if (typeof m == 'string') { | |
// translate message | |
m = messages[m] || self.escape(m); | |
// replace placeholders in message | |
m = m.replace(/\$(\d+)/g, function(match, placeholder) { | |
placeholder = i + parseInt(placeholder); | |
if (placeholder > 0 && input[placeholder]) { | |
ignore.push(placeholder) | |
} | |
return self.escape(input[placeholder]) || ''; | |
}); | |
} else { | |
// get HTML from jQuery object | |
m = m.get(0).outerHTML; | |
} | |
input[i] = m; | |
} | |
return $.map(input, function(m, i) { return $.inArray(i, ignore) === -1 ? m : null; }).join('<br>'); | |
}, | |
/** | |
* Convert mimetype into css classes | |
* | |
* @param String file mimetype | |
* @return String | |
*/ | |
mime2class : function(mime) { | |
var prefix = 'elfinder-cwd-icon-'; | |
mime = mime.split('/'); | |
return prefix+mime[0]+(mime[0] != 'image' && mime[1] ? ' '+prefix+mime[1].replace(/(\.|\+)/g, '-') : ''); | |
}, | |
/** | |
* Return localized kind of file | |
* | |
* @param Object|String file or file mimetype | |
* @return String | |
*/ | |
mime2kind : function(f) { | |
var mime = typeof(f) == 'object' ? f.mime : f, kind; | |
if (f.alias && f.mime != 'symlink-broken') { | |
kind = 'Alias'; | |
} else if (this.kinds[mime]) { | |
kind = this.kinds[mime]; | |
} else { | |
if (mime.indexOf('text') === 0) { | |
kind = 'Text'; | |
} else if (mime.indexOf('image') === 0) { | |
kind = 'Image'; | |
} else if (mime.indexOf('audio') === 0) { | |
kind = 'Audio'; | |
} else if (mime.indexOf('video') === 0) { | |
kind = 'Video'; | |
} else if (mime.indexOf('application') === 0) { | |
kind = 'App'; | |
} else { | |
kind = mime; | |
} | |
} | |
return this.messages['kind'+kind] ? this.i18n('kind'+kind) : mime; | |
}, | |
/** | |
* Return localized date | |
* | |
* @param Object file object | |
* @return String | |
*/ | |
formatDate : function(file, ts) { | |
var self = this, | |
ts = ts || file.ts, | |
i18 = self.i18, | |
date, format, output, d, dw, m, y, h, g, i, s; | |
if (self.options.clientFormatDate && ts > 0) { | |
date = new Date(ts*1000); | |
h = date[self.getHours](); | |
g = h > 12 ? h - 12 : h; | |
i = date[self.getMinutes](); | |
s = date[self.getSeconds](); | |
d = date[self.getDate](); | |
dw = date[self.getDay](); | |
m = date[self.getMonth]() + 1; | |
y = date[self.getFullYear](); | |
format = ts >= this.yesterday | |
? this.fancyFormat | |
: this.dateFormat; | |
output = format.replace(/[a-z]/gi, function(val) { | |
switch (val) { | |
case 'd': return d > 9 ? d : '0'+d; | |
case 'j': return d; | |
case 'D': return self.i18n(i18.daysShort[dw]); | |
case 'l': return self.i18n(i18.days[dw]); | |
case 'm': return m > 9 ? m : '0'+m; | |
case 'n': return m; | |
case 'M': return self.i18n(i18.monthsShort[m-1]); | |
case 'F': return self.i18n(i18.months[m-1]); | |
case 'Y': return y; | |
case 'y': return (''+y).substr(2); | |
case 'H': return h > 9 ? h : '0'+h; | |
case 'G': return h; | |
case 'g': return g; | |
case 'h': return g > 9 ? g : '0'+g; | |
case 'a': return h > 12 ? 'pm' : 'am'; | |
case 'A': return h > 12 ? 'PM' : 'AM'; | |
case 'i': return i > 9 ? i : '0'+i; | |
case 's': return s > 9 ? s : '0'+s; | |
} | |
return val; | |
}); | |
return ts >= this.yesterday | |
? output.replace('$1', this.i18n(ts >= this.today ? 'Today' : 'Yesterday')) | |
: output; | |
} else if (file.date) { | |
return file.date.replace(/([a-z]+)\s/i, function(a1, a2) { return self.i18n(a2)+' '; }); | |
} | |
return self.i18n('dateUnknown'); | |
}, | |
/** | |
* Return css class marks file permissions | |
* | |
* @param Object file | |
* @return String | |
*/ | |
perms2class : function(o) { | |
var c = ''; | |
if (!o.read && !o.write) { | |
c = 'elfinder-na'; | |
} else if (!o.read) { | |
c = 'elfinder-wo'; | |
} else if (!o.write) { | |
c = 'elfinder-ro'; | |
} | |
return c; | |
}, | |
/** | |
* Return localized string with file permissions | |
* | |
* @param Object file | |
* @return String | |
*/ | |
formatPermissions : function(f) { | |
var p = []; | |
f.read && p.push(this.i18n('read')); | |
f.write && p.push(this.i18n('write')); | |
return p.length ? p.join(' '+this.i18n('and')+' ') : this.i18n('noaccess'); | |
}, | |
/** | |
* Return formated file size | |
* | |
* @param Number file size | |
* @return String | |
*/ | |
formatSize : function(s) { | |
var n = 1, u = 'b'; | |
if (s == 'unknown') { | |
return this.i18n('unknown'); | |
} | |
if (s > 1073741824) { | |
n = 1073741824; | |
u = 'GB'; | |
} else if (s > 1048576) { | |
n = 1048576; | |
u = 'MB'; | |
} else if (s > 1024) { | |
n = 1024; | |
u = 'KB'; | |
} | |
s = s/n; | |
return (s > 0 ? n >= 1048576 ? s.toFixed(2) : Math.round(s) : 0) +' '+u; | |
}, | |
/** | |
* Return formated file mode by options.fileModeStyle | |
* | |
* @param String file mode | |
* @param String format style | |
* @return String | |
*/ | |
formatFileMode : function(p, style) { | |
var i, o, s, b, sticy, suid, sgid, str, oct; | |
if (!style) { | |
style = this.options.fileModeStyle.toLowerCase(); | |
} | |
p = $.trim(p); | |
if (p.match(/[rwxs-]{9}$/i)) { | |
str = p = p.substr(-9); | |
if (style == 'string') { | |
return str;; | |
} | |
oct = ''; | |
s = 0; | |
for (i=0; i<7; i=i+3) { | |
o = p.substr(i, 3); | |
b = 0; | |
if (o.match(/[r]/i)) { | |
b += 4; | |
} | |
if (o.match(/[w]/i)) { | |
b += 2; | |
} | |
if (o.match(/[xs]/i)) { | |
if (o.match(/[xs]/)) { | |
b += 1; | |
} | |
if (o.match(/[s]/i)) { | |
if (i == 0) { | |
s += 4; | |
} else if (i == 3) { | |
s += 2; | |
} | |
} | |
} | |
oct += b.toString(8); | |
} | |
if (s) { | |
oct = s.toString(8) + oct; | |
} | |
} else { | |
p = parseInt(p, 8); | |
oct = p? p.toString(8) : ''; | |
if (!p || style == 'octal') { | |
return oct; | |
} | |
o = p.toString(8); | |
s = 0; | |
if (o.length > 3) { | |
o = o.substr(-4); | |
s = parseInt(o.substr(0, 1), 8); | |
o = o.substr(1); | |
} | |
sticy = ((s & 1) == 1); // not support | |
sgid = ((s & 2) == 2); | |
suid = ((s & 4) == 4); | |
str = ''; | |
for(i=0; i<3; i++) { | |
if ((parseInt(o.substr(i, 1), 8) & 4) == 4) { | |
str += 'r'; | |
} else { | |
str += '-'; | |
} | |
if ((parseInt(o.substr(i, 1), 8) & 2) == 2) { | |
str += 'w'; | |
} else { | |
str += '-'; | |
} | |
if ((parseInt(o.substr(i, 1), 8) & 1) == 1) { | |
str += ((i==0 && suid)||(i==1 && sgid))? 's' : 'x'; | |
} else { | |
str += '-'; | |
} | |
} | |
} | |
if (style == 'both') { | |
return str + ' (' + oct + ')'; | |
} else if (style == 'string') { | |
return str; | |
} else { | |
return oct; | |
} | |
}, | |
navHash2Id : function(hash) { | |
return this.navPrefix + hash; | |
}, | |
navId2Hash : function(id) { | |
return typeof(id) == 'string' ? id.substr(this.navPrefix.length) : false; | |
}, | |
cwdHash2Id : function(hash) { | |
return this.cwdPrefix + hash; | |
}, | |
cwdId2Hash : function(id) { | |
return typeof(id) == 'string' ? id.substr(this.cwdPrefix.length) : false; | |
}, | |
log : function(m) { window.console && window.console.log && window.console.log(m); return this; }, | |
debug : function(type, m) { | |
var d = this.options.debug; | |
if (d == 'all' || d === true || ($.isArray(d) && $.inArray(type, d) != -1)) { | |
window.console && window.console.log && window.console.log('elfinder debug: ['+type+'] ['+this.id+']', m); | |
} | |
return this; | |
}, | |
time : function(l) { window.console && window.console.time && window.console.time(l); }, | |
timeEnd : function(l) { window.console && window.console.timeEnd && window.console.timeEnd(l); } | |
} | |
/** | |
* for conpat ex. ie8... | |
* | |
* Object.keys() - JavaScript | MDN | |
* https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys | |
*/ | |
if (!Object.keys) { | |
Object.keys = (function () { | |
var hasOwnProperty = Object.prototype.hasOwnProperty, | |
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), | |
dontEnums = [ | |
'toString', | |
'toLocaleString', | |
'valueOf', | |
'hasOwnProperty', | |
'isPrototypeOf', | |
'propertyIsEnumerable', | |
'constructor' | |
], | |
dontEnumsLength = dontEnums.length | |
return function (obj) { | |
if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object') | |
var result = [] | |
for (var prop in obj) { | |
if (hasOwnProperty.call(obj, prop)) result.push(prop) | |
} | |
if (hasDontEnumBug) { | |
for (var i=0; i < dontEnumsLength; i++) { | |
if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]) | |
} | |
} | |
return result | |
} | |
})() | |
}; | |
/* | |
* File: /js/elFinder.version.js | |
*/ | |
/** | |
* Application version | |
* | |
* @type String | |
**/ | |
elFinder.prototype.version = '2.1.9 (2.1-src Nightly: 0878b27)'; | |
/* | |
* File: /js/jquery.elfinder.js | |
*/ | |
/*** jQuery UI droppable performance tune for elFinder ***/ | |
(function(){ | |
if ($.ui && $.ui.ddmanager) { | |
var origin = $.ui.ddmanager.prepareOffsets; | |
$.ui.ddmanager.prepareOffsets = function( t, event ) { | |
var isOutView = function(elem) { | |
if (elem.is(':hidden')) { | |
return true; | |
} | |
var rect = elem[0].getBoundingClientRect(); | |
return document.elementFromPoint(rect.left, rect.top)? false : true; | |
} | |
if (event.type === 'mousedown') { | |
var i, d, | |
m = $.ui.ddmanager.droppables[ t.options.scope ] || [], | |
l = m.length; | |
for ( i = 0; i < l; i++ ) { | |
d = m[ i ]; | |
if (d.options.autoDisable && (!d.options.disabled || d.options.autoDisable > 1)) { | |
d.options.disabled = isOutView(d.element); | |
d.options.autoDisable = d.options.disabled? 2 : 1; | |
} | |
} | |
} | |
// call origin function | |
return origin( t, event ); | |
}; | |
} | |
})(); | |
$.fn.elfinder = function(o) { | |
if (o == 'instance') { | |
return this.getElFinder(); | |
} | |
return this.each(function() { | |
var cmd = typeof(o) == 'string' ? o : ''; | |
if (!this.elfinder) { | |
new elFinder(this, typeof(o) == 'object' ? o : {}) | |
} | |
switch(cmd) { | |
case 'close': | |
case 'hide': | |
this.elfinder.hide(); | |
break; | |
case 'open': | |
case 'show': | |
this.elfinder.show(); | |
break; | |
case'destroy': | |
this.elfinder.destroy(); | |
break; | |
} | |
}) | |
} | |
$.fn.getElFinder = function() { | |
var instance; | |
this.each(function() { | |
if (this.elfinder) { | |
instance = this.elfinder; | |
return false; | |
} | |
}); | |
return instance; | |
} | |
/* | |
* File: /js/elFinder.options.js | |
*/ | |
/** | |
* Default elFinder config | |
* | |
* @type Object | |
* @autor Dmitry (dio) Levashov | |
*/ | |
elFinder.prototype._options = { | |
/** | |
* Connector url. Required! | |
* | |
* @type String | |
*/ | |
url : '', | |
/** | |
* Ajax request type. | |
* | |
* @type String | |
* @default "get" | |
*/ | |
requestType : 'get', | |
/** | |
* Transport to send request to backend. | |
* Required for future extensions using websockets/webdav etc. | |
* Must be an object with "send" method. | |
* transport.send must return $.Deferred() object | |
* | |
* @type Object | |
* @default null | |
* @example | |
* transport : { | |
* init : function(elfinderInstance) { }, | |
* send : function(options) { | |
* var dfrd = $.Deferred(); | |
* // connect to backend ... | |
* return dfrd; | |
* }, | |
* upload : function(data) { | |
* var dfrd = $.Deferred(); | |
* // upload ... | |
* return dfrd; | |
* } | |
* | |
* } | |
**/ | |
transport : {}, | |
/** | |
* URL to upload file to. | |
* If not set - connector URL will be used | |
* | |
* @type String | |
* @default '' | |
*/ | |
urlUpload : '', | |
/** | |
* Allow to drag and drop to upload files | |
* | |
* @type Boolean|String | |
* @default 'auto' | |
*/ | |
dragUploadAllow : 'auto', | |
/** | |
* Max size of chunked data of file upload | |
* | |
* @type Number | |
* @default 10485760(10MB) | |
*/ | |
uploadMaxChunkSize : 10485760, | |
/** | |
* Timeout for upload using iframe | |
* | |
* @type Number | |
* @default 0 - no timeout | |
*/ | |
iframeTimeout : 0, | |
/** | |
* Data to append to all requests and to upload files | |
* | |
* @type Object | |
* @default {} | |
*/ | |
customData : {}, | |
/** | |
* Event listeners to bind on elFinder init | |
* | |
* @type Object | |
* @default {} | |
*/ | |
handlers : {}, | |
/** | |
* Any custom headers to send across every ajax request | |
* | |
* @type Object | |
* @default {} | |
*/ | |
customHeaders : {}, | |
/** | |
* Any custom xhrFields to send across every ajax request | |
* | |
* @type Object | |
* @default {} | |
*/ | |
xhrFields : {}, | |
/** | |
* Interface language | |
* | |
* @type String | |
* @default "en" | |
*/ | |
lang : 'en', | |
/** | |
* Additional css class for filemanager node. | |
* | |
* @type String | |
*/ | |
cssClass : '', | |
/** | |
* Active commands list | |
* If some required commands will be missed here, elFinder will add its | |
* | |
* @type Array | |
*/ | |
commands : [ | |
'open', 'opendir', 'reload', 'home', 'up', 'back', 'forward', 'getfile', 'quicklook', | |
'download', 'rm', 'duplicate', 'rename', 'mkdir', 'mkfile', 'upload', 'copy', | |
'cut', 'paste', 'edit', 'extract', 'archive', 'search', 'info', 'view', 'help', | |
'resize', 'sort', 'netmount', 'netunmount', 'places', 'chmod' | |
], | |
/** | |
* Commands options. | |
* | |
* @type Object | |
**/ | |
commandsOptions : { | |
// // configure shortcuts of any command | |
// // add `shortcuts` property into each command | |
// any_command_name : { | |
// shortcuts : [] // for disable this command's shortcuts | |
// }, | |
// any_command_name : { | |
// shortcuts : function(fm, shortcuts) { | |
// // for add `CTRL + E` for this command action | |
// shortcuts[0]['pattern'] += ' ctrl+e'; | |
// return shortcuts; | |
// } | |
// }, | |
// any_command_name : { | |
// shortcuts : function(fm, shortcuts) { | |
// // for full customize of this command's shortcuts | |
// return [ { pattern: 'ctrl+e ctrl+down numpad_enter' + (fm.OS != 'mac' && ' enter') } ]; | |
// } | |
// }, | |
// "getfile" command options. | |
getfile : { | |
onlyURL : false, | |
// allow to return multiple files info | |
multiple : false, | |
// allow to return filers info | |
folders : false, | |
// action after callback (""/"close"/"destroy") | |
oncomplete : '' | |
}, | |
// "upload" command options. | |
upload : { | |
ui : 'uploadbutton' | |
}, | |
// "download" command options. | |
download : { | |
maxRequests : 10 | |
}, | |
// "quicklook" command options. | |
quicklook : { | |
autoplay : true, | |
jplayer : 'extensions/jplayer', | |
// MIME types to use Google Docs online viewer | |
// Example ['application/pdf', 'image/tiff', 'application/msword', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'] | |
googleDocsMimes : [] | |
}, | |
// "quicklook" command options. | |
edit : { | |
// list of allowed mimetypes to edit | |
// if empty - any text files can be edited | |
mimes : [], | |
// edit files in wysisyg's | |
editors : [ | |
// { | |
// /** | |
// * files mimetypes allowed to edit in current wysisyg | |
// * @type Array | |
// */ | |
// mimes : ['text/html'], | |
// /** | |
// * Called when "edit" dialog loaded. | |
// * Place to init wysisyg. | |
// * Can return wysisyg instance | |
// * | |
// * @param DOMElement textarea node | |
// * @return Object | |
// */ | |
// load : function(textarea) { }, | |
// /** | |
// * Called before "edit" dialog closed. | |
// * Place to destroy wysisyg instance. | |
// * | |
// * @param DOMElement textarea node | |
// * @param Object wysisyg instance (if was returned by "load" callback) | |
// * @return void | |
// */ | |
// close : function(textarea, instance) { }, | |
// /** | |
// * Called before file content send to backend. | |
// * Place to update textarea content if needed. | |
// * | |
// * @param DOMElement textarea node | |
// * @param Object wysisyg instance (if was returned by "load" callback) | |
// * @return void | |
// */ | |
// save : function(textarea, instance) {}, | |
// /** | |
// * Called after load() or save(). | |
// * Set focus to wysisyg editor. | |
// * | |
// * @param DOMElement textarea node | |
// * @param Object wysisyg instance (if was returned by "load" callback) | |
// * @return void | |
// */ | |
// focus : function(textarea, instance) {} | |
// | |
// } | |
] | |
}, | |
// "info" command options. | |
info : { | |
nullUrlDirLinkSelf : true, | |
custom : { | |
// /** | |
// * Example of custom info `desc` | |
// */ | |
// desc : { | |
// /** | |
// * Lable (require) | |
// * It is filtered by the `fm.i18n()` | |
// * | |
// * @type String | |
// */ | |
// label : 'Description', | |
// | |
// /** | |
// * Template (require) | |
// * `{id}` is replaced in dialog.id | |
// * | |
// * @type String | |
// */ | |
// tpl : '<div class="elfinder-info-desc"><span class="elfinder-info-spinner"></span></div>', | |
// | |
// /** | |
// * Restricts to mimetypes (optional) | |
// * Exact match or category match | |
// * | |
// * @type Array | |
// */ | |
// mimes : ['text', 'image/jpeg', 'directory'], | |
// | |
// /** | |
// * Restricts to file.hash (optional) | |
// * | |
// * @ type Regex | |
// */ | |
// hashRegex : /^l\d+_/, | |
// | |
// /** | |
// * Request that asks for the description and sets the field (optional) | |
// * | |
// * @type Function | |
// */ | |
// action : function(file, fm, dialog) { | |
// fm.request({ | |
// data : { cmd : 'desc', target: file.hash }, | |
// preventDefault: true, | |
// }) | |
// .fail(function() { | |
// dialog.find('div.elfinder-info-desc').html(fm.i18n('unknown')); | |
// }) | |
// .done(function(data) { | |
// dialog.find('div.elfinder-info-desc').html(data.desc); | |
// }); | |
// } | |
// } | |
} | |
}, | |
netmount: { | |
ftp: { | |
inputs: { | |
host : $('<input type="text"/>'), | |
port : $('<input type="text" placeholder="21"/>'), | |
path : $('<input type="text" value="/"/>'), | |
user : $('<input type="text"/>'), | |
pass : $('<input type="password"/>'), | |
encoding : $('<input type="text" placeholder="Optional"/>'), | |
locale : $('<input type="text" placeholder="Optional"/>') | |
} | |
}, | |
dropbox: { | |
inputs: { | |
host : $('<span><span class="elfinder-info-spinner"/></span></span><input type="hidden"/>'), | |
path : $('<input type="text" value="/"/>'), | |
user : $('<input type="hidden"/>'), | |
pass : $('<input type="hidden"/>') | |
}, | |
select: function(fm){ | |
var self = this; | |
if (self.inputs.host.find('span').length) { | |
fm.request({ | |
data : {cmd : 'netmount', protocol: 'dropbox', host: 'dropbox.com', user: 'init', pass: 'init', options: {url: fm.uploadURL, id: fm.id}}, | |
preventDefault : true | |
}).done(function(data){ | |
self.inputs.host.find('span').removeClass("elfinder-info-spinner"); | |
self.inputs.host.find('span').html(data.body.replace(/\{msg:([^}]+)\}/g, function(whole,s1){return fm.i18n(s1,'Dropbox.com');})); | |
}).fail(function(){}); | |
} | |
}, | |
done: function(fm, data){ | |
var self = this; | |
if (data.mode == 'makebtn') { | |
self.inputs.host.find('span').removeClass("elfinder-info-spinner"); | |
self.inputs.host.find('input').hover(function(){$(this).toggleClass("ui-state-hover");}); | |
self.inputs.host[1].value = ""; | |
} else { | |
self.inputs.host.find('span').removeClass("elfinder-info-spinner"); | |
self.inputs.host.find('span').html("Dropbox.com"); | |
self.inputs.host[1].value = "dropbox"; | |
self.inputs.user.val("done"); | |
self.inputs.pass.val("done"); | |
} | |
} | |
} | |
}, | |
help : {view : ['about', 'shortcuts', 'help']} | |
}, | |
/** | |
* Callback for "getfile" commands. | |
* Required to use elFinder with WYSIWYG editors etc.. | |
* | |
* @type Function | |
* @default null (command not active) | |
*/ | |
getFileCallback : null, | |
/** | |
* Default directory view. icons/list | |
* | |
* @type String | |
* @default "icons" | |
*/ | |
defaultView : 'icons', | |
/** | |
* Hash of default directory path to open | |
* | |
* @type String | |
* @default "" | |
*/ | |
startPathHash : '', | |
/** | |
* UI plugins to load. | |
* Current dir ui and dialogs loads always. | |
* Here set not required plugins as folders tree/toolbar/statusbar etc. | |
* | |
* @type Array | |
* @default ['toolbar', 'tree', 'path', 'stat'] | |
* @full ['toolbar', 'places', 'tree', 'path', 'stat'] | |
*/ | |
ui : ['toolbar', 'tree', 'path', 'stat'], | |
/** | |
* Some UI plugins options. | |
* @type Object | |
*/ | |
uiOptions : { | |
// toolbar configuration | |
toolbar : [ | |
['back', 'forward'], | |
['netmount'], | |
// ['reload'], | |
// ['home', 'up'], | |
['mkdir', 'mkfile', 'upload'], | |
['open', 'download', 'getfile'], | |
['info', 'chmod'], | |
['quicklook'], | |
['copy', 'cut', 'paste'], | |
['rm'], | |
['duplicate', 'rename', 'edit', 'resize'], | |
['extract', 'archive'], | |
['search'], | |
['view', 'sort'], | |
['help'] | |
], | |
// directories tree options | |
tree : { | |
// expand current root on init | |
openRootOnLoad : true, | |
// expand current work directory on open | |
openCwdOnOpen : true, | |
// auto load current dir parents | |
syncTree : true | |
// , | |
// /** | |
// * Add CSS class name to navbar directories (optional) | |
// * see: https://github.com/Studio-42/elFinder/pull/1061 | |
// * | |
// * @type Function | |
// */ | |
// getClass: function(dir) { | |
// // ex. This adds the directory's name (lowercase) with prefix as a CSS class | |
// return 'elfinder-tree-' + dir.name.replace(/[ "]/g, '').toLowerCase(); | |
// } | |
}, | |
// navbar options | |
navbar : { | |
minWidth : 150, | |
maxWidth : 500 | |
}, | |
cwd : { | |
// display parent folder with ".." name :) | |
oldSchool : false, | |
// file info columns displayed | |
listView : { | |
// name is always displayed, cols are ordered | |
// ex. ['perm', 'date', 'size', 'kind', 'owner', 'group', 'mode'] | |
// mode: 'mode'(by `fileModeStyle` setting), 'modestr'(rwxr-xr-x) , 'modeoct'(755), 'modeboth'(rwxr-xr-x (755)) | |
// 'owner', 'group' and 'mode', It's necessary set volume driver option "statOwner" to `true` | |
columns : ['perm', 'date', 'size', 'kind'], | |
// override this if you want custom columns name | |
// example | |
// columnsCustomName : { | |
// date : 'Last modification', | |
// kind : 'Mime type' | |
// } | |
columnsCustomName : {} | |
} | |
} | |
}, | |
/** | |
* Display only required files by types | |
* | |
* @type Array | |
* @default [] | |
* @example | |
* onlyMimes : ["image"] - display all images | |
* onlyMimes : ["image/png", "application/x-shockwave-flash"] - display png and flash | |
*/ | |
onlyMimes : [], | |
/** | |
* Custom files sort rules. | |
* All default rules (name/size/kind/date) set in elFinder._sortRules | |
* | |
* @type {Object} | |
* @example | |
* sortRules : { | |
* name : function(file1, file2) { return file1.name.toLowerCase().localeCompare(file2.name.toLowerCase()); } | |
* } | |
*/ | |
sortRules : {}, | |
/** | |
* Default sort type. | |
* | |
* @type {String} | |
*/ | |
sortType : 'name', | |
/** | |
* Default sort order. | |
* | |
* @type {String} | |
* @default "asc" | |
*/ | |
sortOrder : 'asc', | |
/** | |
* Display folders first? | |
* | |
* @type {Boolean} | |
* @default true | |
*/ | |
sortStickFolders : true, | |
/** | |
* If true - elFinder will formating dates itself, | |
* otherwise - backend date will be used. | |
* | |
* @type Boolean | |
*/ | |
clientFormatDate : true, | |
/** | |
* Show UTC dates. | |
* Required set clientFormatDate to true | |
* | |
* @type Boolean | |
*/ | |
UTCDate : false, | |
/** | |
* File modification datetime format. | |
* Value from selected language data is used by default. | |
* Set format here to overwrite it. | |
* | |
* @type String | |
* @default "" | |
*/ | |
dateFormat : '', | |
/** | |
* File modification datetime format in form "Yesterday 12:23:01". | |
* Value from selected language data is used by default. | |
* Set format here to overwrite it. | |
* Use $1 for "Today"/"Yesterday" placeholder | |
* | |
* @type String | |
* @default "" | |
* @example "$1 H:m:i" | |
*/ | |
fancyDateFormat : '', | |
/** | |
* Style of file mode at cwd-list, info dialog | |
* 'string' (ex. rwxr-xr-x) or 'octal' (ex. 755) or 'both' (ex. rwxr-xr-x (755)) | |
* | |
* @type {String} | |
* @default 'both' | |
*/ | |
fileModeStyle : 'both', | |
/** | |
* elFinder width | |
* | |
* @type String|Number | |
* @default "auto" | |
*/ | |
width : 'auto', | |
/** | |
* elFinder height | |
* | |
* @type Number | |
* @default "auto" | |
*/ | |
height : 400, | |
/** | |
* Make elFinder resizable if jquery ui resizable available | |
* | |
* @type Boolean | |
* @default true | |
*/ | |
resizable : true, | |
/** | |
* Timeout before open notifications dialogs | |
* | |
* @type Number | |
* @default 500 (.5 sec) | |
*/ | |
notifyDelay : 500, | |
/** | |
* Position CSS, Width of notifications dialogs | |
* | |
* @type Object | |
* @default {position: {top : '12px', right : '12px'}, width : 280} | |
* position: CSS object | null (null: position center & middle) | |
*/ | |
notifyDialog : {position: {top : '12px', right : '12px'}, width : 280}, | |
/** | |
* Allow shortcuts | |
* | |
* @type Boolean | |
* @default true | |
*/ | |
allowShortcuts : true, | |
/** | |
* Remeber last opened dir to open it after reload or in next session | |
* | |
* @type Boolean | |
* @default true | |
*/ | |
rememberLastDir : true, | |
/** | |
* Clear historys(elFinder) on reload(not browser) function | |
* Historys was cleared on Reload function on elFinder 2.0 (value is true) | |
* | |
* @type Boolean | |
* @default false | |
*/ | |
reloadClearHistory : false, | |
/** | |
* Use browser native history with supported browsers | |
* | |
* @type Boolean | |
* @default true | |
*/ | |
useBrowserHistory : true, | |
/** | |
* Lazy load config. | |
* How many files display at once? | |
* | |
* @type Number | |
* @default 50 | |
*/ | |
showFiles : 30, | |
/** | |
* Lazy load config. | |
* Distance in px to cwd bottom edge to start display files | |
* | |
* @type Number | |
* @default 50 | |
*/ | |
showThreshold : 50, | |
/** | |
* Additional rule to valid new file name. | |
* By default not allowed empty names or '..' | |
* This setting does not have a sense of security. | |
* | |
* @type false|RegExp|function | |
* @default false | |
* @example | |
* disable names with spaces: | |
* validName : /^[^\s]+$/, | |
*/ | |
validName : false, | |
/** | |
* Backup name suffix. | |
* | |
* @type String | |
* @default "~" | |
*/ | |
backupSuffix : '~', | |
/** | |
* Sync content interval | |
* | |
* @type Number | |
* @default 0 (do not sync) | |
*/ | |
sync : 0, | |
/** | |
* Sync start on load if sync value >= 1000 | |
* | |
* @type Bool | |
* @default true | |
*/ | |
syncStart : true, | |
/** | |
* How many thumbnails create in one request | |
* | |
* @type Number | |
* @default 5 | |
*/ | |
loadTmbs : 5, | |
/** | |
* Cookie option for browsersdoes not suppot localStorage | |
* | |
* @type Object | |
*/ | |
cookie : { | |
expires : 30, | |
domain : '', | |
path : '/', | |
secure : false | |
}, | |
/** | |
* Contextmenu config | |
* | |
* @type Object | |
*/ | |
contextmenu : { | |
// navbarfolder menu | |
navbar : ['open', 'download', '|', 'upload', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', '|', 'rename', '|', 'places', 'info', 'chmod', 'netunmount'], | |
// current directory menu | |
cwd : ['reload', 'back', '|', 'upload', 'mkdir', 'mkfile', 'paste', '|', 'sort', '|', 'info'], | |
// current directory file menu | |
files : ['getfile', '|' ,'open', 'download', 'opendir', 'quicklook', '|', 'upload', 'mkdir', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', '|', 'edit', 'rename', 'resize', '|', 'archive', 'extract', '|', 'places', 'info', 'chmod'] | |
}, | |
/** | |
* Debug config | |
* | |
* @type Array|Boolean | |
*/ | |
// debug : true | |
debug : ['error', 'warning', 'event-destroy'] | |
} | |
/* | |
* File: /js/elFinder.history.js | |
*/ | |
/** | |
* @class elFinder.history | |
* Store visited folders | |
* and provide "back" and "forward" methods | |
* | |
* @author Dmitry (dio) Levashov | |
*/ | |
elFinder.prototype.history = function(fm) { | |
var self = this, | |
/** | |
* Update history on "open" event? | |
* | |
* @type Boolean | |
*/ | |
update = true, | |
/** | |
* Directories hashes storage | |
* | |
* @type Array | |
*/ | |
history = [], | |
/** | |
* Current directory index in history | |
* | |
* @type Number | |
*/ | |
current, | |
/** | |
* Clear history | |
* | |
* @return void | |
*/ | |
reset = function() { | |
history = [fm.cwd().hash]; | |
current = 0; | |
update = true; | |
}, | |
/** | |
* Browser native history object | |
*/ | |
nativeHistory = (fm.options.useBrowserHistory && window.history && window.history.pushState)? window.history : null, | |
/** | |
* Open prev/next folder | |
* | |
* @Boolen open next folder? | |
* @return jQuery.Deferred | |
*/ | |
go = function(fwd) { | |
if ((fwd && self.canForward()) || (!fwd && self.canBack())) { | |
update = false; | |
return fm.exec('open', history[fwd ? ++current : --current]).fail(reset); | |
} | |
return $.Deferred().reject(); | |
}; | |
/** | |
* Return true if there is previous visited directories | |
* | |
* @return Boolen | |
*/ | |
this.canBack = function() { | |
return current > 0; | |
} | |
/** | |
* Return true if can go forward | |
* | |
* @return Boolen | |
*/ | |
this.canForward = function() { | |
return current < history.length - 1; | |
} | |
/** | |
* Go back | |
* | |
* @return void | |
*/ | |
this.back = go; | |
/** | |
* Go forward | |
* | |
* @return void | |
*/ | |
this.forward = function() { | |
return go(true); | |
} | |
// bind to elfinder events | |
fm.open(function() { | |
var l = history.length, | |
cwd = fm.cwd().hash; | |
if (update) { | |
current >= 0 && l > current + 1 && history.splice(current+1); | |
history[history.length-1] != cwd && history.push(cwd); | |
current = history.length - 1; | |
} | |
update = true; | |
if (nativeHistory) { | |
if (! nativeHistory.state) { | |
nativeHistory.replaceState({thash: cwd}, null, location.pathname + location.search + '#elf_' + cwd); | |
} else { | |
nativeHistory.state.thash != cwd && nativeHistory.pushState({thash: cwd}, null, location.pathname + location.search + '#elf_' + cwd); | |
} | |
} | |
}) | |
.reload(fm.options.reloadClearHistory && reset); | |
} | |
/* | |
* File: /js/elFinder.command.js | |
*/ | |
/** | |
* elFinder command prototype | |
* | |
* @type elFinder.command | |
* @author Dmitry (dio) Levashov | |
*/ | |
elFinder.prototype.command = function(fm) { | |
/** | |
* elFinder instance | |
* | |
* @type elFinder | |
*/ | |
this.fm = fm; | |
/** | |
* Command name, same as class name | |
* | |
* @type String | |
*/ | |
this.name = ''; | |
/** | |
* Short command description | |
* | |
* @type String | |
*/ | |
this.title = ''; | |
/** | |
* Linked(Child) commands name | |
* They are loaded together when tthis command is loaded. | |
* | |
* @type Array | |
*/ | |
this.linkedCmds = []; | |
/** | |
* Current command state | |
* | |
* @example | |
* this.state = -1; // command disabled | |
* this.state = 0; // command enabled | |
* this.state = 1; // command active (for example "fullscreen" command while elfinder in fullscreen mode) | |
* @default -1 | |
* @type Number | |
*/ | |
this.state = -1; | |
/** | |
* If true, command can not be disabled by connector. | |
* @see this.update() | |
* | |
* @type Boolen | |
*/ | |
this.alwaysEnabled = false; | |
/** | |
* If true, this means command was disabled by connector. | |
* @see this.update() | |
* | |
* @type Boolen | |
*/ | |
this._disabled = false; | |
this.disableOnSearch = false; | |
this.updateOnSelect = true; | |
/** | |
* elFinder events defaults handlers. | |
* Inside handlers "this" is current command object | |
* | |
* @type Object | |
*/ | |
this._handlers = { | |
enable : function() { this.update(void(0), this.value); }, | |
disable : function() { this.update(-1, this.value); }, | |
'open reload load' : function() { | |
this._disabled = !(this.alwaysEnabled || this.fm.isCommandEnabled(this.name)); | |
this.update(void(0), this.value) | |
this.change(); | |
} | |
}; | |
/** | |
* elFinder events handlers. | |
* Inside handlers "this" is current command object | |
* | |
* @type Object | |
*/ | |
this.handlers = {} | |
/** | |
* Shortcuts | |
* | |
* @type Array | |
*/ | |
this.shortcuts = []; | |
/** | |
* Command options | |
* | |
* @type Object | |
*/ | |
this.options = {ui : 'button'}; | |
/** | |
* Prepare object - | |
* bind events and shortcuts | |
* | |
* @return void | |
*/ | |
this.setup = function(name, opts) { | |
var self = this, | |
fm = this.fm, i, s, sc; | |
this.name = name; | |
this.title = fm.messages['cmd'+name] ? fm.i18n('cmd'+name) : name, | |
this.options = $.extend({}, this.options, opts); | |
this.listeners = []; | |
if (opts.shortcuts) { | |
if (typeof opts.shortcuts === 'function') { | |
sc = opts.shortcuts(this.fm, this.shortcuts); | |
} else if ($.isArray(opts.shortcuts)) { | |
sc = opts.shortcuts; | |
} | |
this.shortcuts = sc || []; | |
} | |
if (this.updateOnSelect) { | |
this._handlers.select = function() { this.update(void(0), this.value); } | |
} | |
$.each($.extend({}, self._handlers, self.handlers), function(cmd, handler) { | |
fm.bind(cmd, $.proxy(handler, self)); | |
}); | |
for (i = 0; i < this.shortcuts.length; i++) { | |
s = this.shortcuts[i]; | |
s.callback = $.proxy(s.callback || function() { this.exec() }, this); | |
!s.description && (s.description = this.title); | |
fm.shortcut(s); | |
} | |
if (this.disableOnSearch) { | |
fm.bind('search searchend', function(e) { | |
self._disabled = e.type == 'search'; | |
self.update(void(0), self.value); | |
}); | |
} | |
this.init(); | |
} | |
/** | |
* Command specific init stuffs | |
* | |
* @return void | |
*/ | |
this.init = function() { } | |
/** | |
* Exec command | |
* | |
* @param Array target files hashes | |
* @param Array|Object command value | |
* @return $.Deferred | |
*/ | |
this.exec = function(files, opts) { | |
return $.Deferred().reject(); | |
} | |
/** | |
* Return true if command disabled. | |
* | |
* @return Boolen | |
*/ | |
this.disabled = function() { | |
return this.state < 0; | |
} | |
/** | |
* Return true if command enabled. | |
* | |
* @return Boolen | |
*/ | |
this.enabled = function() { | |
return this.state > -1; | |
} | |
/** | |
* Return true if command active. | |
* | |
* @return Boolen | |
*/ | |
this.active = function() { | |
return this.state > 0; | |
} | |
/** | |
* Return current command state. | |
* Must be overloaded in most commands | |
* | |
* @return Number | |
*/ | |
this.getstate = function() { | |
return -1; | |
} | |
/** | |
* Update command state/value | |
* and rize 'change' event if smth changed | |
* | |
* @param Number new state or undefined to auto update state | |
* @param mixed new value | |
* @return void | |
*/ | |
this.update = function(s, v) { | |
var state = this.state, | |
value = this.value; | |
if (this._disabled) { | |
this.state = -1; | |
} else { | |
this.state = s !== void(0) ? s : this.getstate(); | |
} | |
this.value = v; | |
if (state != this.state || value != this.value) { | |
this.change(); | |
} | |
} | |
/** | |
* Bind handler / fire 'change' event. | |
* | |
* @param Function|undefined event callback | |
* @return void | |
*/ | |
this.change = function(c) { | |
var cmd, i; | |
if (typeof(c) === 'function') { | |
this.listeners.push(c); | |
} else { | |
for (i = 0; i < this.listeners.length; i++) { | |
cmd = this.listeners[i]; | |
try { | |
cmd(this.state, this.value); | |
} catch (e) { | |
this.fm.debug('error', e) | |
} | |
} | |
} | |
return this; | |
} | |
/** | |
* With argument check given files hashes and return list of existed files hashes. | |
* Without argument return selected files hashes. | |
* | |
* @param Array|String|void hashes | |
* @return Array | |
*/ | |
this.hashes = function(hashes) { | |
return hashes | |
? $.map($.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) ? hash : null; }) | |
: fm.selected(); | |
} | |
/** | |
* Return only existed files from given fils hashes | selected files | |
* | |
* @param Array|String|void hashes | |
* @return Array | |
*/ | |
this.files = function(hashes) { | |
var fm = this.fm; | |
return hashes | |
? $.map($.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) || null }) | |
: fm.selectedFiles(); | |
} | |
} | |
/* | |
* File: /js/elFinder.resources.js | |
*/ | |
/** | |
* elFinder resources registry. | |
* Store shared data | |
* | |
* @type Object | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.resources = { | |
'class' : { | |
hover : 'ui-state-hover', | |
active : 'ui-state-active', | |
disabled : 'ui-state-disabled', | |
draggable : 'ui-draggable', | |
droppable : 'ui-droppable', | |
adroppable : 'elfinder-droppable-active', | |
cwdfile : 'elfinder-cwd-file', | |
cwd : 'elfinder-cwd', | |
tree : 'elfinder-tree', | |
treeroot : 'elfinder-navbar-root', | |
navdir : 'elfinder-navbar-dir', | |
navdirwrap : 'elfinder-navbar-dir-wrapper', | |
navarrow : 'elfinder-navbar-arrow', | |
navsubtree : 'elfinder-navbar-subtree', | |
navcollapse : 'elfinder-navbar-collapsed', | |
navexpand : 'elfinder-navbar-expanded', | |
treedir : 'elfinder-tree-dir', | |
placedir : 'elfinder-place-dir', | |
searchbtn : 'elfinder-button-search' | |
}, | |
tpl : { | |
perms : '<span class="elfinder-perms"/>', | |
lock : '<span class="elfinder-lock"/>', | |
symlink : '<span class="elfinder-symlink"/>', | |
navicon : '<span class="elfinder-nav-icon"/>', | |
navspinner : '<span class="elfinder-navbar-spinner"/>', | |
navdir : '<div class="elfinder-navbar-wrapper"><span id="{id}" class="ui-corner-all elfinder-navbar-dir {cssclass}"><span class="elfinder-navbar-arrow"/><span class="elfinder-navbar-icon" {style}/>{symlink}{permissions}{name}</span><div class="elfinder-navbar-subtree"/></div>', | |
placedir : '<div class="elfinder-navbar-wrapper"><span id="{id}" class="ui-corner-all elfinder-navbar-dir {cssclass}" title="{title}"><span class="elfinder-navbar-arrow"/><span class="elfinder-navbar-icon" {style}/>{symlink}{permissions}{name}</span><div class="elfinder-navbar-subtree"/></div>' | |
}, | |
mimes : { | |
text : [ | |
'application/x-empty', | |
'application/javascript', | |
'application/xhtml+xml', | |
'audio/x-mp3-playlist', | |
'application/x-web-config', | |
'application/docbook+xml', | |
'application/x-php', | |
'application/x-perl', | |
'application/x-awk', | |
'application/x-config', | |
'application/x-csh', | |
'application/xml' | |
] | |
}, | |
mixin : { | |
make : function() { | |
var fm = this.fm, | |
cmd = this.name, | |
cwd = fm.getUI('cwd'), | |
tarea= (fm.storage('view') != 'list'), | |
sel = fm.selected(), | |
rest = function(){ | |
if (!overlay.is(':hidden')) { | |
overlay.addClass('ui-front') | |
.elfinderoverlay('hide') | |
.off('click', cancel); | |
} | |
node.removeClass('ui-front').css('position', ''); | |
if (tarea) { | |
nnode.css('max-height', ''); | |
} else { | |
pnode.css('width', '') | |
.parent('td').css('overflow', ''); | |
} | |
}, colwidth, | |
dfrd = $.Deferred() | |
.fail(function(error) { | |
if (sel) { | |
fm.trigger('unlockfiles', {files: sel}); | |
fm.clipboard([]); | |
} | |
cwd.trigger('unselectall'); | |
error && fm.error(error); | |
}) | |
.always(function() { | |
rest(); | |
input.remove(); | |
node.remove(); | |
fm.enable(); | |
}), | |
id = 'tmp_'+parseInt(Math.random()*100000), | |
phash = fm.cwd().hash, | |
date = new Date(), | |
file = { | |
hash : id, | |
name : fm.uniqueName(this.prefix), | |
mime : this.mime, | |
read : true, | |
write : true, | |
date : 'Today '+date.getHours()+':'+date.getMinutes() | |
}, | |
data = this.data || {}, | |
node = cwd.trigger('create.'+fm.namespace, file).find('#'+fm.cwdHash2Id(id)), | |
nnode, pnode, | |
overlay = fm.getUI().children('.elfinder-overlay'), | |
cancel = function(e) { | |
e.stopPropagation(); | |
dfrd.reject(); | |
}, | |
input = $(tarea? '<textarea/>' : '<input type="text"/>') | |
.on('keyup text', function(){ | |
if (tarea) { | |
this.style.height = '1px'; | |
this.style.height = this.scrollHeight + 'px'; | |
} else if (colwidth) { | |
this.style.width = colwidth + 'px'; | |
if (this.scrollWidth > colwidth) { | |
this.style.width = this.scrollWidth + 10 + 'px'; | |
} | |
} | |
}) | |
.keydown(function(e) { | |
e.stopImmediatePropagation(); | |
if (e.keyCode == $.ui.keyCode.ESCAPE) { | |
dfrd.reject(); | |
} else if (e.keyCode == $.ui.keyCode.ENTER) { | |
input.blur(); | |
} | |
}) | |
.mousedown(function(e) { | |
e.stopPropagation(); | |
}) | |
.blur(function() { | |
var name = $.trim(input.val()), | |
parent = input.parent(), | |
valid = true, | |
cut; | |
if (parent.length) { | |
if (fm.options.validName && fm.options.validName.test) { | |
try { | |
valid = fm.options.validName.test(name); | |
} catch(e) { | |
valid = false; | |
} | |
} | |
if (!name || name === '..' || !valid) { | |
fm.error('errInvName'); | |
return false; | |
} | |
if (fm.fileByName(name, phash)) { | |
fm.error(['errExists', name]); | |
return false; | |
} | |
cut = sel? fm.exec('cut', sel) : null; | |
$.when(cut) | |
.done(function() { | |
rest(); | |
parent.html(fm.escape(name)); | |
fm.lockfiles({files : [id]}); | |
fm.request({ | |
data : $.extend({cmd : cmd, name : name, target : phash}, data || {}), | |
notify : {type : cmd, cnt : 1}, | |
preventFail : true, | |
syncOnFail : true | |
}) | |
.fail(function(error) { | |
dfrd.reject(error); | |
}) | |
.done(function(data) { | |
dfrd.resolve(data); | |
if (data.added && data.added[0]) { | |
var dirhash = data.added[0].hash, | |
newItem = cwd.find('#'+fm.cwdHash2Id(dirhash)); | |
if (sel) { | |
fm.exec('paste', dirhash); | |
} | |
if (newItem.length) { | |
newItem.trigger('scrolltoview'); | |
} | |
} | |
}); | |
}) | |
.fail(function() { | |
dfrd.reject(); | |
}); | |
} | |
}); | |
if (this.disabled() || !node.length) { | |
return dfrd.reject(); | |
} | |
fm.disable(); | |
nnode = node.find('.elfinder-cwd-filename'); | |
pnode = nnode.parent(); | |
node.css('position', 'relative').addClass('ui-front'); | |
if (tarea) { | |
nnode.css('max-height', 'none'); | |
} else { | |
colwidth = pnode.width(); | |
pnode.width(colwidth - 15) | |
.parent('td').css('overflow', 'visible'); | |
} | |
nnode.empty('').append(input.val(file.name)); | |
if (fm.UA.Mobile) { | |
overlay.on('click', cancel) | |
.removeClass('ui-front').elfinderoverlay('show'); | |
} | |
input.trigger('keyup'); | |
input.select().focus(); | |
input[0].setSelectionRange && input[0].setSelectionRange(0, file.name.replace(/\..+$/, '').length); | |
return dfrd; | |
} | |
} | |
} | |
/* | |
* File: /js/jquery.dialogelfinder.js | |
*/ | |
/** | |
* @class dialogelfinder - open elFinder in dialog window | |
* | |
* @param Object elFinder options with dialog options | |
* @example | |
* $(selector).dialogelfinder({ | |
* // some elfinder options | |
* title : 'My files', // dialog title, default = "Files" | |
* width : 850, // dialog width, default 840 | |
* autoOpen : false, // if false - dialog will not be opened after init, default = true | |
* destroyOnClose : true // destroy elFinder on close dialog, default = false | |
* }) | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.dialogelfinder = function(opts) { | |
var position = 'elfinderPosition', | |
destroy = 'elfinderDestroyOnClose'; | |
this.not('.elfinder').each(function() { | |
var doc = $(document), | |
toolbar = $('<div class="ui-widget-header dialogelfinder-drag ui-corner-top">'+(opts.title || 'Files')+'</div>'), | |
button = $('<a href="#" class="dialogelfinder-drag-close ui-corner-all"><span class="ui-icon ui-icon-closethick"> </span></a>') | |
.appendTo(toolbar) | |
.click(function(e) { | |
e.preventDefault(); | |
node.dialogelfinder('close'); | |
}), | |
node = $(this).addClass('dialogelfinder') | |
.css('position', 'absolute') | |
.hide() | |
.appendTo('body') | |
.draggable({ handle : '.dialogelfinder-drag', | |
containment : 'window' }) | |
.elfinder(opts) | |
.prepend(toolbar), | |
elfinder = node.elfinder('instance'); | |
node.width(parseInt(node.width()) || 840) // fix width if set to "auto" | |
.data(destroy, !!opts.destroyOnClose) | |
.find('.elfinder-toolbar').removeClass('ui-corner-top'); | |
opts.position && node.data(position, opts.position); | |
opts.autoOpen !== false && $(this).dialogelfinder('open'); | |
}); | |
if (opts == 'open') { | |
var node = $(this), | |
pos = node.data(position) || { | |
top : parseInt($(document).scrollTop() + ($(window).height() < node.height() ? 2 : ($(window).height() - node.height())/2)), | |
left : parseInt($(document).scrollLeft() + ($(window).width() < node.width() ? 2 : ($(window).width() - node.width())/2)) | |
}; | |
if (node.is(':hidden')) { | |
node.addClass('ui-front').css(pos).show().trigger('resize'); | |
setTimeout(function() { | |
// fix resize icon position and make elfinder active | |
node.trigger('resize').mousedown(); | |
}, 200); | |
} | |
} else if (opts == 'close') { | |
var node = $(this).removeClass('ui-front'); | |
if (node.is(':visible')) { | |
!!node.data(destroy) | |
? node.elfinder('destroy').remove() | |
: node.elfinder('close'); | |
} | |
} else if (opts == 'instance') { | |
return $(this).getElFinder(); | |
} | |
return this; | |
}; | |
/* | |
* File: /js/i18n/elfinder.en.js | |
*/ | |
/** | |
* English translation | |
* @author Troex Nevelin <troex@fury.scancode.ru> | |
* @version 2016-02-19 | |
*/ | |
if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object') { | |
elFinder.prototype.i18.en = { | |
translator : 'Troex Nevelin <troex@fury.scancode.ru>', | |
language : 'English', | |
direction : 'ltr', | |
dateFormat : 'M d, Y h:i A', // Mar 13, 2012 05:27 PM | |
fancyDateFormat : '$1 h:i A', // will produce smth like: Today 12:25 PM | |
messages : { | |
/********************************** errors **********************************/ | |
'error' : 'Error', | |
'errUnknown' : 'Unknown error.', | |
'errUnknownCmd' : 'Unknown command.', | |
'errJqui' : 'Invalid jQuery UI configuration. Selectable, draggable and droppable components must be included.', | |
'errNode' : 'elFinder requires DOM Element to be created.', | |
'errURL' : 'Invalid elFinder configuration! URL option is not set.', | |
'errAccess' : 'Access denied.', | |
'errConnect' : 'Unable to connect to backend.', | |
'errAbort' : 'Connection aborted.', | |
'errTimeout' : 'Connection timeout.', | |
'errNotFound' : 'Backend not found.', | |
'errResponse' : 'Invalid backend response.', | |
'errConf' : 'Invalid backend configuration.', | |
'errJSON' : 'PHP JSON module not installed.', | |
'errNoVolumes' : 'Readable volumes not available.', | |
'errCmdParams' : 'Invalid parameters for command "$1".', | |
'errDataNotJSON' : 'Data is not JSON.', | |
'errDataEmpty' : 'Data is empty.', | |
'errCmdReq' : 'Backend request requires command name.', | |
'errOpen' : 'Unable to open "$1".', | |
'errNotFolder' : 'Object is not a folder.', | |
'errNotFile' : 'Object is not a file.', | |
'errRead' : 'Unable to read "$1".', | |
'errWrite' : 'Unable to write into "$1".', | |
'errPerm' : 'Permission denied.', | |
'errLocked' : '"$1" is locked and can not be renamed, moved or removed.', | |
'errExists' : 'File named "$1" already exists.', | |
'errInvName' : 'Invalid file name.', | |
'errFolderNotFound' : 'Folder not found.', | |
'errFileNotFound' : 'File not found.', | |
'errTrgFolderNotFound' : 'Target folder "$1" not found.', | |
'errPopup' : 'Browser prevented opening popup window. To open file enable it in browser options.', | |
'errMkdir' : 'Unable to create folder "$1".', | |
'errMkfile' : 'Unable to create file "$1".', | |
'errRename' : 'Unable to rename "$1".', | |
'errCopyFrom' : 'Copying files from volume "$1" not allowed.', | |
'errCopyTo' : 'Copying files to volume "$1" not allowed.', | |
'errMkOutLink' : 'Unable to create a link to outside the volume root.', // from v2.1 added 03.10.2015 | |
'errUpload' : 'Upload error.', // old name - errUploadCommon | |
'errUploadFile' : 'Unable to upload "$1".', // old name - errUpload | |
'errUploadNoFiles' : 'No files found for upload.', | |
'errUploadTotalSize' : 'Data exceeds the maximum allowed size.', // old name - errMaxSize | |
'errUploadFileSize' : 'File exceeds maximum allowed size.', // old name - errFileMaxSize | |
'errUploadMime' : 'File type not allowed.', | |
'errUploadTransfer' : '"$1" transfer error.', | |
'errUploadTemp' : 'Unable to make temporary file for upload.', // from v2.1 added 26.09.2015 | |
'errNotReplace' : 'Object "$1" already exists at this location and can not be replaced by object with another type.', // new | |
'errReplace' : 'Unable to replace "$1".', | |
'errSave' : 'Unable to save "$1".', | |
'errCopy' : 'Unable to copy "$1".', | |
'errMove' : 'Unable to move "$1".', | |
'errCopyInItself' : 'Unable to copy "$1" into itself.', | |
'errRm' : 'Unable to remove "$1".', | |
'errRmSrc' : 'Unable remove source file(s).', | |
'errExtract' : 'Unable to extract files from "$1".', | |
'errArchive' : 'Unable to create archive.', | |
'errArcType' : 'Unsupported archive type.', | |
'errNoArchive' : 'File is not archive or has unsupported archive type.', | |
'errCmdNoSupport' : 'Backend does not support this command.', | |
'errReplByChild' : 'The folder "$1" can\'t be replaced by an item it contains.', | |
'errArcSymlinks' : 'For security reason denied to unpack archives contains symlinks or files with not allowed names.', // edited 24.06.2012 | |
'errArcMaxSize' : 'Archive files exceeds maximum allowed size.', | |
'errResize' : 'Unable to resize "$1".', | |
'errResizeDegree' : 'Invalid rotate degree.', // added 7.3.2013 | |
'errResizeRotate' : 'Unable to rotate image.', // added 7.3.2013 | |
'errResizeSize' : 'Invalid image size.', // added 7.3.2013 | |
'errResizeNoChange' : 'Image size not changed.', // added 7.3.2013 | |
'errUsupportType' : 'Unsupported file type.', | |
'errNotUTF8Content' : 'File "$1" is not in UTF-8 and cannot be edited.', // added 9.11.2011 | |
'errNetMount' : 'Unable to mount "$1".', // added 17.04.2012 | |
'errNetMountNoDriver' : 'Unsupported protocol.', // added 17.04.2012 | |
'errNetMountFailed' : 'Mount failed.', // added 17.04.2012 | |
'errNetMountHostReq' : 'Host required.', // added 18.04.2012 | |
'errSessionExpires' : 'Your session has expired due to inactivity.', | |
'errCreatingTempDir' : 'Unable to create temporary directory: "$1"', | |
'errFtpDownloadFile' : 'Unable to download file from FTP: "$1"', | |
'errFtpUploadFile' : 'Unable to upload file to FTP: "$1"', | |
'errFtpMkdir' : 'Unable to create remote directory on FTP: "$1"', | |
'errArchiveExec' : 'Error while archiving files: "$1"', | |
'errExtractExec' : 'Error while extracting files: "$1"', | |
'errNetUnMount' : 'Unable to unmount', // from v2.1 added 30.04.2012 | |
'errConvUTF8' : 'Not convertible to UTF-8', // from v2.1 added 08.04.2014 | |
'errFolderUpload' : 'Try Google Chrome, If you\'d like to upload the folder.', // from v2.1 added 26.6.2015 | |
'errSearchTimeout' : 'Timed out while searching "$1". Search result is partial.', // from v2.1 added 12.1.2016 | |
/******************************* commands names ********************************/ | |
'cmdarchive' : 'Create archive', | |
'cmdback' : 'Back', | |
'cmdcopy' : 'Copy', | |
'cmdcut' : 'Cut', | |
'cmddownload' : 'Download', | |
'cmdduplicate' : 'Duplicate', | |
'cmdedit' : 'Edit file', | |
'cmdextract' : 'Extract files from archive', | |
'cmdforward' : 'Forward', | |
'cmdgetfile' : 'Select files', | |
'cmdhelp' : 'About this software', | |
'cmdhome' : 'Home', | |
'cmdinfo' : 'Get info', | |
'cmdmkdir' : 'New folder', | |
'cmdmkdirin' : 'Into New Folder', // from v2.1.7 added 19.2.2016 | |
'cmdmkfile' : 'New text file', | |
'cmdopen' : 'Open', | |
'cmdpaste' : 'Paste', | |
'cmdquicklook' : 'Preview', | |
'cmdreload' : 'Reload', | |
'cmdrename' : 'Rename', | |
'cmdrm' : 'Delete', | |
'cmdsearch' : 'Find files', | |
'cmdup' : 'Go to parent directory', | |
'cmdupload' : 'Upload files', | |
'cmdview' : 'View', | |
'cmdresize' : 'Resize & Rotate', | |
'cmdsort' : 'Sort', | |
'cmdnetmount' : 'Mount network volume', // added 18.04.2012 | |
'cmdnetunmount': 'Unmount', // from v2.1 added 30.04.2012 | |
'cmdplaces' : 'To Places', // added 28.12.2014 | |
'cmdchmod' : 'Change mode', // from v2.1 added 20.6.2015 | |
'cmdopendir' : 'Open a folder', // from v2.1 added 13.1.2016 | |
/*********************************** buttons ***********************************/ | |
'btnClose' : 'Close', | |
'btnSave' : 'Save', | |
'btnRm' : 'Remove', | |
'btnApply' : 'Apply', | |
'btnCancel' : 'Cancel', | |
'btnNo' : 'No', | |
'btnYes' : 'Yes', | |
'btnMount' : 'Mount', // added 18.04.2012 | |
'btnApprove': 'Goto $1 & approve', // from v2.1 added 26.04.2012 | |
'btnUnmount': 'Unmount', // from v2.1 added 30.04.2012 | |
'btnConv' : 'Convert', // from v2.1 added 08.04.2014 | |
'btnCwd' : 'Here', // from v2.1 added 22.5.2015 | |
'btnVolume' : 'Volume', // from v2.1 added 22.5.2015 | |
'btnAll' : 'All', // from v2.1 added 22.5.2015 | |
'btnMime' : 'MIME Type', // from v2.1 added 22.5.2015 | |
'btnFileName':'Filename', // from v2.1 added 22.5.2015 | |
'btnSaveClose': 'Save & Close', // from v2.1 added 12.6.2015 | |
'btnBackup' : 'Backup', // fromv2.1 added 28.11.2015 | |
/******************************** notifications ********************************/ | |
'ntfopen' : 'Open folder', | |
'ntffile' : 'Open file', | |
'ntfreload' : 'Reload folder content', | |
'ntfmkdir' : 'Creating directory', | |
'ntfmkfile' : 'Creating files', | |
'ntfrm' : 'Delete files', | |
'ntfcopy' : 'Copy files', | |
'ntfmove' : 'Move files', | |
'ntfprepare' : 'Prepare to copy files', | |
'ntfrename' : 'Rename files', | |
'ntfupload' : 'Uploading files', | |
'ntfdownload' : 'Downloading files', | |
'ntfsave' : 'Save files', | |
'ntfarchive' : 'Creating archive', | |
'ntfextract' : 'Extracting files from archive', | |
'ntfsearch' : 'Searching files', | |
'ntfresize' : 'Resizing images', | |
'ntfsmth' : 'Doing something', | |
'ntfloadimg' : 'Loading image', | |
'ntfnetmount' : 'Mounting network volume', // added 18.04.2012 | |
'ntfnetunmount': 'Unmounting network volume', // from v2.1 added 30.04.2012 | |
'ntfdim' : 'Acquiring image dimension', // added 20.05.2013 | |
'ntfreaddir' : 'Reading folder infomation', // from v2.1 added 01.07.2013 | |
'ntfurl' : 'Getting URL of link', // from v2.1 added 11.03.2014 | |
'ntfchmod' : 'Changing file mode', // from v2.1 added 20.6.2015 | |
'ntfpreupload': 'Verifying upload file name', // from v2.1 added 31.11.2015 | |
'ntfzipdl' : 'Creating a file for download', // from v2.1.7 added 23.1.2016 | |
/************************************ dates **********************************/ | |
'dateUnknown' : 'unknown', | |
'Today' : 'Today', | |
'Yesterday' : 'Yesterday', | |
'msJan' : 'Jan', | |
'msFeb' : 'Feb', | |
'msMar' : 'Mar', | |
'msApr' : 'Apr', | |
'msMay' : 'May', | |
'msJun' : 'Jun', | |
'msJul' : 'Jul', | |
'msAug' : 'Aug', | |
'msSep' : 'Sep', | |
'msOct' : 'Oct', | |
'msNov' : 'Nov', | |
'msDec' : 'Dec', | |
'January' : 'January', | |
'February' : 'February', | |
'March' : 'March', | |
'April' : 'April', | |
'May' : 'May', | |
'June' : 'June', | |
'July' : 'July', | |
'August' : 'August', | |
'September' : 'September', | |
'October' : 'October', | |
'November' : 'November', | |
'December' : 'December', | |
'Sunday' : 'Sunday', | |
'Monday' : 'Monday', | |
'Tuesday' : 'Tuesday', | |
'Wednesday' : 'Wednesday', | |
'Thursday' : 'Thursday', | |
'Friday' : 'Friday', | |
'Saturday' : 'Saturday', | |
'Sun' : 'Sun', | |
'Mon' : 'Mon', | |
'Tue' : 'Tue', | |
'Wed' : 'Wed', | |
'Thu' : 'Thu', | |
'Fri' : 'Fri', | |
'Sat' : 'Sat', | |
/******************************** sort variants ********************************/ | |
'sortname' : 'by name', | |
'sortkind' : 'by kind', | |
'sortsize' : 'by size', | |
'sortdate' : 'by date', | |
'sortFoldersFirst' : 'Folders first', | |
/********************************** new items **********************************/ | |
'untitled file.txt' : 'NewFile.txt', // added 10.11.2015 | |
'untitled folder' : 'NewFolder', // added 10.11.2015 | |
'Archive' : 'NewArchive', // from v2.1 added 10.11.2015 | |
/********************************** messages **********************************/ | |
'confirmReq' : 'Confirmation required', | |
'confirmRm' : 'Are you sure you want to remove files?<br/>This cannot be undone!', | |
'confirmRepl' : 'Replace old file with new one?', | |
'confirmConvUTF8' : 'Not in UTF-8<br/>Convert to UTF-8?<br/>Contents become UTF-8 by saving after conversion.', // from v2.1 added 08.04.2014 | |
'confirmNotSave' : 'It has been modified.<br/>Losing work if you do not save changes.', // from v2.1 added 15.7.2015 | |
'apllyAll' : 'Apply to all', | |
'name' : 'Name', | |
'size' : 'Size', | |
'perms' : 'Permissions', | |
'modify' : 'Modified', | |
'kind' : 'Kind', | |
'read' : 'read', | |
'write' : 'write', | |
'noaccess' : 'no access', | |
'and' : 'and', | |
'unknown' : 'unknown', | |
'selectall' : 'Select all files', | |
'selectfiles' : 'Select file(s)', | |
'selectffile' : 'Select first file', | |
'selectlfile' : 'Select last file', | |
'viewlist' : 'List view', | |
'viewicons' : 'Icons view', | |
'places' : 'Places', | |
'calc' : 'Calculate', | |
'path' : 'Path', | |
'aliasfor' : 'Alias for', | |
'locked' : 'Locked', | |
'dim' : 'Dimensions', | |
'files' : 'Files', | |
'folders' : 'Folders', | |
'items' : 'Items', | |
'yes' : 'yes', | |
'no' : 'no', | |
'link' : 'Link', | |
'searcresult' : 'Search results', | |
'selected' : 'selected items', | |
'about' : 'About', | |
'shortcuts' : 'Shortcuts', | |
'help' : 'Help', | |
'webfm' : 'Web file manager', | |
'ver' : 'Version', | |
'protocolver' : 'protocol version', | |
'homepage' : 'Project home', | |
'docs' : 'Documentation', | |
'github' : 'Fork us on Github', | |
'twitter' : 'Follow us on twitter', | |
'facebook' : 'Join us on facebook', | |
'team' : 'Team', | |
'chiefdev' : 'chief developer', | |
'developer' : 'developer', | |
'contributor' : 'contributor', | |
'maintainer' : 'maintainer', | |
'translator' : 'translator', | |
'icons' : 'Icons', | |
'dontforget' : 'and don\'t forget to take your towel', | |
'shortcutsof' : 'Shortcuts disabled', | |
'dropFiles' : 'Drop files here', | |
'or' : 'or', | |
'selectForUpload' : 'Select files to upload', | |
'moveFiles' : 'Move files', | |
'copyFiles' : 'Copy files', | |
'rmFromPlaces' : 'Remove from places', | |
'aspectRatio' : 'Aspect ratio', | |
'scale' : 'Scale', | |
'width' : 'Width', | |
'height' : 'Height', | |
'resize' : 'Resize', | |
'crop' : 'Crop', | |
'rotate' : 'Rotate', | |
'rotate-cw' : 'Rotate 90 degrees CW', | |
'rotate-ccw' : 'Rotate 90 degrees CCW', | |
'degree' : '°', | |
'netMountDialogTitle' : 'Mount network volume', // added 18.04.2012 | |
'protocol' : 'Protocol', // added 18.04.2012 | |
'host' : 'Host', // added 18.04.2012 | |
'port' : 'Port', // added 18.04.2012 | |
'user' : 'User', // added 18.04.2012 | |
'pass' : 'Password', // added 18.04.2012 | |
'confirmUnmount' : 'Are you unmount $1?', // from v2.1 added 30.04.2012 | |
'dropFilesBrowser': 'Drop or Paste files from browser', // from v2.1 added 30.05.2012 | |
'dropPasteFiles' : 'Drop or Paste files here', // from v2.1 added 07.04.2014 | |
'encoding' : 'Encoding', // from v2.1 added 19.12.2014 | |
'locale' : 'Locale', // from v2.1 added 19.12.2014 | |
'searchTarget' : 'Target: $1', // from v2.1 added 22.5.2015 | |
'searchMime' : 'Search by input MIME Type', // from v2.1 added 22.5.2015 | |
'owner' : 'Owner', // from v2.1 added 20.6.2015 | |
'group' : 'Group', // from v2.1 added 20.6.2015 | |
'other' : 'Other', // from v2.1 added 20.6.2015 | |
'execute' : 'Execute', // from v2.1 added 20.6.2015 | |
'perm' : 'Permission', // from v2.1 added 20.6.2015 | |
'mode' : 'Mode', // from v2.1 added 20.6.2015 | |
'emptyFolder' : 'Folder is empty', // from v2.1.6 added 30.12.2015 | |
'emptyFolderDrop' : 'Folder is empty\\A Drop to add items', // from v2.1.6 added 30.12.2015 | |
'emptyFolderLTap' : 'Folder is empty\\A Long tap to add items', // from v2.1.6 added 30.12.2015 | |
'quality' : 'Quality', // from v2.1.6 added 5.1.2016 | |
'autoSync' : 'Auto sync', // from v2.1.6 added 10.1.2016 | |
'moveUp' : 'Move up', // from v2.1.6 added 18.1.2016 | |
'getLink' : 'Get URL link', // from v2.1.7 added 9.2.2016 | |
'selectedItems' : 'Selected items ($1)', // from v2.1.7 added 2.19.2016 | |
/********************************** mimetypes **********************************/ | |
'kindUnknown' : 'Unknown', | |
'kindFolder' : 'Folder', | |
'kindAlias' : 'Alias', | |
'kindAliasBroken' : 'Broken alias', | |
// applications | |
'kindApp' : 'Application', | |
'kindPostscript' : 'Postscript document', | |
'kindMsOffice' : 'Microsoft Office document', | |
'kindMsWord' : 'Microsoft Word document', | |
'kindMsExcel' : 'Microsoft Excel document', | |
'kindMsPP' : 'Microsoft Powerpoint presentation', | |
'kindOO' : 'Open Office document', | |
'kindAppFlash' : 'Flash application', | |
'kindPDF' : 'Portable Document Format (PDF)', | |
'kindTorrent' : 'Bittorrent file', | |
'kind7z' : '7z archive', | |
'kindTAR' : 'TAR archive', | |
'kindGZIP' : 'GZIP archive', | |
'kindBZIP' : 'BZIP archive', | |
'kindXZ' : 'XZ archive', | |
'kindZIP' : 'ZIP archive', | |
'kindRAR' : 'RAR archive', | |
'kindJAR' : 'Java JAR file', | |
'kindTTF' : 'True Type font', | |
'kindOTF' : 'Open Type font', | |
'kindRPM' : 'RPM package', | |
// texts | |
'kindText' : 'Text document', | |
'kindTextPlain' : 'Plain text', | |
'kindPHP' : 'PHP source', | |
'kindCSS' : 'Cascading style sheet', | |
'kindHTML' : 'HTML document', | |
'kindJS' : 'Javascript source', | |
'kindRTF' : 'Rich Text Format', | |
'kindC' : 'C source', | |
'kindCHeader' : 'C header source', | |
'kindCPP' : 'C++ source', | |
'kindCPPHeader' : 'C++ header source', | |
'kindShell' : 'Unix shell script', | |
'kindPython' : 'Python source', | |
'kindJava' : 'Java source', | |
'kindRuby' : 'Ruby source', | |
'kindPerl' : 'Perl script', | |
'kindSQL' : 'SQL source', | |
'kindXML' : 'XML document', | |
'kindAWK' : 'AWK source', | |
'kindCSV' : 'Comma separated values', | |
'kindDOCBOOK' : 'Docbook XML document', | |
'kindMarkdown' : 'Markdown text', // added 20.7.2015 | |
// images | |
'kindImage' : 'Image', | |
'kindBMP' : 'BMP image', | |
'kindJPEG' : 'JPEG image', | |
'kindGIF' : 'GIF Image', | |
'kindPNG' : 'PNG Image', | |
'kindTIFF' : 'TIFF image', | |
'kindTGA' : 'TGA image', | |
'kindPSD' : 'Adobe Photoshop image', | |
'kindXBITMAP' : 'X bitmap image', | |
'kindPXM' : 'Pixelmator image', | |
// media | |
'kindAudio' : 'Audio media', | |
'kindAudioMPEG' : 'MPEG audio', | |
'kindAudioMPEG4' : 'MPEG-4 audio', | |
'kindAudioMIDI' : 'MIDI audio', | |
'kindAudioOGG' : 'Ogg Vorbis audio', | |
'kindAudioWAV' : 'WAV audio', | |
'AudioPlaylist' : 'MP3 playlist', | |
'kindVideo' : 'Video media', | |
'kindVideoDV' : 'DV movie', | |
'kindVideoMPEG' : 'MPEG movie', | |
'kindVideoMPEG4' : 'MPEG-4 movie', | |
'kindVideoAVI' : 'AVI movie', | |
'kindVideoMOV' : 'Quick Time movie', | |
'kindVideoWM' : 'Windows Media movie', | |
'kindVideoFlash' : 'Flash movie', | |
'kindVideoMKV' : 'Matroska movie', | |
'kindVideoOGG' : 'Ogg movie' | |
} | |
}; | |
} | |
/* | |
* File: /js/ui/button.js | |
*/ | |
/** | |
* @class elFinder toolbar button widget. | |
* If command has variants - create menu | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfinderbutton = function(cmd) { | |
return this.each(function() { | |
var c = 'class', | |
fm = cmd.fm, | |
disabled = fm.res(c, 'disabled'), | |
active = fm.res(c, 'active'), | |
hover = fm.res(c, 'hover'), | |
item = 'elfinder-button-menu-item', | |
selected = 'elfinder-button-menu-item-selected', | |
menu, | |
button = $(this).addClass('ui-state-default elfinder-button') | |
.attr('title', cmd.title) | |
.append('<span class="elfinder-button-icon elfinder-button-icon-'+cmd.name+'"/>') | |
.hover(function(e) { !button.hasClass(disabled) && button[e.type == 'mouseleave' ? 'removeClass' : 'addClass'](hover) /**button.toggleClass(hover);*/ }) | |
.click(function(e) { | |
if (!button.hasClass(disabled)) { | |
if (menu && cmd.variants.length > 1) { | |
// close other menus | |
menu.is(':hidden') && cmd.fm.getUI().click(); | |
e.stopPropagation(); | |
menu.slideToggle(100); | |
} else { | |
cmd.exec(); | |
} | |
} | |
}), | |
hideMenu = function() { | |
menu.hide(); | |
}; | |
// if command has variants create menu | |
if ($.isArray(cmd.variants)) { | |
button.addClass('elfinder-menubutton'); | |
menu = $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>') | |
.hide() | |
.appendTo(button) | |
.on('mouseenter mouseleave', '.'+item, function() { $(this).toggleClass(hover) }) | |
.on('click', '.'+item, function(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
button.removeClass(hover); | |
menu.hide(); | |
cmd.exec(cmd.fm.selected(), $(this).data('value')); | |
}); | |
cmd.fm.bind('disable select', hideMenu).getUI().click(hideMenu); | |
cmd.change(function() { | |
menu.html(''); | |
$.each(cmd.variants, function(i, variant) { | |
menu.append($('<div class="'+item+'">'+variant[1]+'</div>').data('value', variant[0]).addClass(variant[0] == cmd.value ? selected : '')); | |
}); | |
}); | |
} | |
cmd.change(function() { | |
if (cmd.disabled()) { | |
button.removeClass(active+' '+hover).addClass(disabled); | |
} else { | |
button.removeClass(disabled); | |
button[cmd.active() ? 'addClass' : 'removeClass'](active); | |
} | |
}) | |
.change(); | |
}); | |
} | |
/* | |
* File: /js/ui/contextmenu.js | |
*/ | |
/** | |
* @class elFinder contextmenu | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfindercontextmenu = function(fm) { | |
return this.each(function() { | |
var self = $(this), | |
cmItem = 'elfinder-contextmenu-item', | |
smItem = 'elfinder-contextsubmenu-item', | |
exIcon = 'elfinder-contextmenu-extra-icon', | |
menu = self.addClass('ui-helper-reset ui-front ui-widget ui-state-default ui-corner-all elfinder-contextmenu elfinder-contextmenu-'+fm.direction) | |
.hide() | |
.appendTo('body') | |
.on('mouseenter mouseleave', '.'+cmItem, function(e) { | |
$(this).toggleClass('ui-state-hover', e.type === 'mouseenter'); | |
}) | |
.on('mouseenter mouseleave', '.'+exIcon, function(e) { | |
$(this).parent().toggleClass('ui-state-hover', e.type === 'mouseleave'); | |
}) | |
.on('contextmenu', function(){return false;}), | |
subpos = fm.direction == 'ltr' ? 'left' : 'right', | |
types = $.extend({}, fm.options.contextmenu), | |
clItem = cmItem + (fm.UA.Touch ? ' elfinder-touch' : ''), | |
tpl = '<div class="'+clItem+'"><span class="elfinder-button-icon {icon} elfinder-contextmenu-icon"/><span>{label}</span></div>', | |
item = function(label, icon, callback) { | |
return $(tpl.replace('{icon}', icon ? 'elfinder-button-icon-'+icon : '').replace('{label}', label)) | |
.click(function(e) { | |
e.stopPropagation(); | |
e.preventDefault(); | |
callback(); | |
}) | |
}, | |
open = function(x, y) { | |
var win = $(window), | |
width = menu.outerWidth(), | |
height = menu.outerHeight(), | |
wwidth = win.width(), | |
wheight = win.height(), | |
scrolltop = win.scrollTop(), | |
scrollleft = win.scrollLeft(), | |
mw = fm.UA.Touch? 30 : 0, | |
mh = fm.UA.Touch? 20 : 0, | |
css = { | |
top : Math.max(0, y - scrolltop + mh + height < wheight ? y + mh : (y - mh - height > 0 ? y - mh - height : y + mh)), | |
left : Math.max(0, x - scrollleft + mw + width < wwidth ? x + mw : x - mw - width) | |
}; | |
menu.css(css) | |
.show(); | |
//alert(navigator.platform); | |
css[subpos] = parseInt(menu.width()); | |
menu.find('.elfinder-contextmenu-sub').css(css); | |
if (fm.UA.iOS) { | |
$('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'auto'); | |
} | |
}, | |
close = function() { | |
menu.hide().empty(); | |
fm.trigger('closecontextmenu'); | |
if (fm.UA.iOS) { | |
$('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'touch'); | |
} | |
}, | |
create = function(type, targets) { | |
var sep = false, | |
cmdMap = {}, disabled = [], isCwd = (targets[0].indexOf(fm.cwd().volumeid, 0) === 0), | |
selcnt = 0; | |
if (self.data('cmdMaps')) { | |
$.each(self.data('cmdMaps'), function(i, v){ | |
if (targets[0].indexOf(i, 0) == 0) { | |
cmdMap = v; | |
return false; | |
} | |
}); | |
} | |
if (!isCwd) { | |
if (fm.disabledCmds) { | |
$.each(fm.disabledCmds, function(i, v){ | |
if (targets[0].indexOf(i, 0) == 0) { | |
disabled = v; | |
return false; | |
} | |
}); | |
} | |
} | |
selcnt = fm.selected().length; | |
if (selcnt > 1) { | |
menu.append('<div class="ui-corner-top ui-widget-header elfinder-contextmenu-header"><span>' | |
+ fm.i18n('selectedItems', ''+selcnt) | |
+ '</span></div>'); | |
} | |
$.each(types[type]||[], function(i, name) { | |
var cmd, node, submenu, hover; | |
if (name == '|' && sep) { | |
menu.append('<div class="elfinder-contextmenu-separator"/>'); | |
sep = false; | |
return; | |
} | |
if (cmdMap[name]) { | |
name = cmdMap[name]; | |
} | |
cmd = fm.command(name); | |
if (cmd && !isCwd && (!fm.searchStatus.state || !cmd.disableOnSearch)) { | |
cmd.__disabled = cmd._disabled; | |
cmd._disabled = !(cmd.alwaysEnabled || (fm._commands[name] ? $.inArray(name, disabled) === -1 : false)); | |
$.each(cmd.linkedCmds, function(i, n) { | |
var c; | |
if (c = fm.command(n)) { | |
c.__disabled = c._disabled; | |
c._disabled = !(c.alwaysEnabled || (fm._commands[n] ? $.inArray(n, disabled) === -1 : false)); | |
} | |
}); | |
} | |
if (cmd && cmd.getstate(targets) != -1) { | |
//targets._type = type; | |
if (cmd.variants) { | |
if (!cmd.variants.length) { | |
return; | |
} | |
node = item(cmd.title, cmd.name, function(){}) | |
.on('touchend', function(e){ | |
node.data('touching', true); | |
setTimeout(function(){node.data('touching', false);}, 50); | |
}) | |
.on('click touchend', '.'+smItem, function(e){ | |
e.stopPropagation(); | |
if (node.data('touching')) { | |
node.data('touching', false); | |
$(this).removeClass('ui-state-hover'); | |
e.preventDefault(); | |
} else if (e.type == 'click') { | |
menu.hide(); | |
cmd.exec(targets, $(this).data('exec')); | |
} | |
}); | |
submenu = $('<div class="ui-front ui-corner-all elfinder-contextmenu-sub"/>') | |
.appendTo(node.append('<span class="elfinder-contextmenu-arrow"/>')); | |
hover = function(){ | |
var win = $(window), | |
baseleft = $(node).offset().left, | |
basetop = $(node).offset().top, | |
basewidth = $(node).outerWidth(), | |
width = submenu.outerWidth(), | |
height = submenu.outerHeight(), | |
wwidth = win.scrollLeft() + win.width(), | |
wheight = win.scrollTop() + win.height(), | |
margin = 5, x, y, over; | |
over = (baseleft + basewidth + width + margin) - wwidth; | |
x = (over > 0)? basewidth - over : basewidth; | |
over = (basetop + 5 + height + margin) - wheight; | |
y = (over > 0)? 5 - over : 5; | |
var css = { | |
left : x, | |
top : y | |
}; | |
submenu.css(css).toggle(); | |
}; | |
node.addClass('elfinder-contextmenu-group').hover(function(){ hover(); }); | |
$.each(cmd.variants, function(i, variant) { | |
submenu.append( | |
$('<div class="'+clItem+' '+smItem+'"><span>'+variant[1]+'</span></div>').data('exec', variant[0]) | |
); | |
}); | |
} else { | |
node = item(cmd.title, cmd.name, function() { | |
close(); | |
cmd.exec(targets); | |
}); | |
if (cmd.extra && cmd.extra.node) { | |
node.append( | |
$('<span class="elfinder-button-icon elfinder-button-icon-'+(cmd.extra.icon || '')+' elfinder-contextmenu-extra-icon"/>') | |
.append(cmd.extra.node) | |
); | |
} else { | |
node.remove('.elfinder-contextmenu-extra-icon'); | |
} | |
} | |
menu.append(node) | |
sep = true; | |
} | |
if (cmd && typeof cmd.__disabled !== 'undefined') { | |
cmd._disabled = cmd.__disabled; | |
delete cmd.__disabled; | |
$.each(cmd.linkedCmds, function(i, n) { | |
var c; | |
if (c = fm.command(n)) { | |
c._disabled = c.__disabled; | |
delete c.__disabled; | |
} | |
}); | |
} | |
}); | |
}, | |
createFromRaw = function(raw) { | |
$.each(raw, function(i, data) { | |
var node; | |
if (data === '|') { | |
menu.append('<div class="elfinder-contextmenu-separator"/>'); | |
} else if (data.label && typeof data.callback == 'function') { | |
node = item(data.label, data.icon, function() { | |
!data.remain && close(); | |
data.callback(); | |
}); | |
menu.append(node); | |
} | |
}); | |
}; | |
fm.one('load', function() { | |
var uiCwd = fm.getUI('cwd'); | |
fm.bind('contextmenu', function(e) { | |
var data = e.data; | |
if (!data.type || data.type !== 'files') { | |
uiCwd.trigger('unselectall'); | |
} | |
close(); | |
if (data.type && data.targets) { | |
create(data.type, data.targets); | |
} else if (data.raw) { | |
createFromRaw(data.raw); | |
} | |
menu.children().length && open(data.x, data.y); | |
}) | |
.one('destroy', function() { menu.remove(); }) | |
.bind('disable select', function(){ | |
// 'mouseEvInternal' for Firefox's bug (maybe) | |
!self.data('mouseEvInternal') && close(); | |
self.data('mouseEvInternal', false); | |
}) | |
.getUI().click(close); | |
}); | |
}); | |
} | |
/* | |
* File: /js/ui/cwd.js | |
*/ | |
/** | |
* elFinder current working directory ui. | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfindercwd = function(fm, options) { | |
this.not('.elfinder-cwd').each(function() { | |
// fm.time('cwdLoad'); | |
var mobile = fm.UA.Mobile, | |
list = fm.viewType == 'list', | |
undef = 'undefined', | |
/** | |
* Select event full name | |
* | |
* @type String | |
**/ | |
evtSelect = 'select.'+fm.namespace, | |
/** | |
* Unselect event full name | |
* | |
* @type String | |
**/ | |
evtUnselect = 'unselect.'+fm.namespace, | |
/** | |
* Disable event full name | |
* | |
* @type String | |
**/ | |
evtDisable = 'disable.'+fm.namespace, | |
/** | |
* Disable event full name | |
* | |
* @type String | |
**/ | |
evtEnable = 'enable.'+fm.namespace, | |
c = 'class', | |
/** | |
* File css class | |
* | |
* @type String | |
**/ | |
clFile = fm.res(c, 'cwdfile'), | |
/** | |
* Selected css class | |
* | |
* @type String | |
**/ | |
fileSelector = '.'+clFile, | |
/** | |
* Selected css class | |
* | |
* @type String | |
**/ | |
clSelected = 'ui-selected', | |
/** | |
* Disabled css class | |
* | |
* @type String | |
**/ | |
clDisabled = fm.res(c, 'disabled'), | |
/** | |
* Draggable css class | |
* | |
* @type String | |
**/ | |
clDraggable = fm.res(c, 'draggable'), | |
/** | |
* Droppable css class | |
* | |
* @type String | |
**/ | |
clDroppable = fm.res(c, 'droppable'), | |
/** | |
* Hover css class | |
* | |
* @type String | |
**/ | |
clHover = fm.res(c, 'hover'), | |
/** | |
* Hover css class | |
* | |
* @type String | |
**/ | |
clDropActive = fm.res(c, 'adroppable'), | |
/** | |
* Css class for temporary nodes (for mkdir/mkfile) commands | |
* | |
* @type String | |
**/ | |
clTmp = clFile+'-tmp', | |
/** | |
* Number of thumbnails to load in one request (new api only) | |
* | |
* @type Number | |
**/ | |
tmbNum = fm.options.loadTmbs > 0 ? fm.options.loadTmbs : 5, | |
/** | |
* Current search query. | |
* | |
* @type String | |
*/ | |
query = '', | |
lastSearch = [], | |
/** | |
* Currect clipboard(cut) hashes as object key | |
* | |
* @type Object | |
*/ | |
clipCuts = {}, | |
/** | |
* Parents hashes of cwd | |
* | |
* @type Array | |
*/ | |
cwdParents = [], | |
customColsBuild = function() { | |
var customCols = ''; | |
var columns = fm.options.uiOptions.cwd.listView.columns; | |
for (var i = 0; i < columns.length; i++) { | |
customCols += '<td>{' + columns[i] + '}</td>'; | |
} | |
return customCols; | |
}, | |
/** | |
* File templates | |
* | |
* @type Object | |
**/ | |
templates = { | |
icon : '<div id="{id}" class="'+clFile+(fm.UA.Touch ? ' '+'elfinder-touch' : '')+' {permsclass} {dirclass} ui-corner-all" title="{tooltip}"><div class="elfinder-cwd-file-wrapper ui-corner-all"><div class="elfinder-cwd-icon {mime} ui-corner-all" unselectable="on" {style}/>{marker}</div><div class="elfinder-cwd-filename" title="{nametitle}">{name}</div></div>', | |
row : '<tr id="{id}" class="'+clFile+(fm.UA.Touch ? ' '+'elfinder-touch' : '')+' {permsclass} {dirclass}" title="{tooltip}"><td><div class="elfinder-cwd-file-wrapper"><span class="elfinder-cwd-icon {mime}"/>{marker}<span class="elfinder-cwd-filename">{name}</span></div></td>'+customColsBuild()+'</tr>', | |
}, | |
permsTpl = fm.res('tpl', 'perms'), | |
lockTpl = fm.res('tpl', 'lock'), | |
symlinkTpl = fm.res('tpl', 'symlink'), | |
/** | |
* Template placeholders replacement rules | |
* | |
* @type Object | |
**/ | |
replacement = { | |
id : function(f) { | |
return fm.cwdHash2Id(f.hash); | |
}, | |
name : function(f) { | |
var name = fm.escape(f.name); | |
!list && (name = name.replace(/([_.])/g, '​$1')); | |
return name; | |
}, | |
nametitle : function(f) { | |
return fm.escape(f.name); | |
}, | |
permsclass : function(f) { | |
return fm.perms2class(f); | |
}, | |
perm : function(f) { | |
return fm.formatPermissions(f); | |
}, | |
dirclass : function(f) { | |
return f.mime == 'directory' ? 'directory' : ''; | |
}, | |
mime : function(f) { | |
return fm.mime2class(f.mime); | |
}, | |
size : function(f) { | |
return fm.formatSize(f.size); | |
}, | |
date : function(f) { | |
return fm.formatDate(f); | |
}, | |
kind : function(f) { | |
return fm.mime2kind(f); | |
}, | |
mode : function(f) { | |
return f.perm? fm.formatFileMode(f.perm) : ''; | |
}, | |
modestr : function(f) { | |
return f.perm? fm.formatFileMode(f.perm, 'string') : ''; | |
}, | |
modeoct : function(f) { | |
return f.perm? fm.formatFileMode(f.perm, 'octal') : ''; | |
}, | |
modeboth : function(f) { | |
return f.perm? fm.formatFileMode(f.perm, 'both') : ''; | |
}, | |
marker : function(f) { | |
return (f.alias || f.mime == 'symlink-broken' ? symlinkTpl : '')+(!f.read || !f.write ? permsTpl : '')+(f.locked ? lockTpl : ''); | |
}, | |
tooltip : function(f) { | |
var title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''), | |
info = ''; | |
if (query && f.path) { | |
info = fm.escape(f.path.replace(/\/[^\/]*$/, '')); | |
} else { | |
info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, ' ') : ''; | |
} | |
return info? info + ' ' + title : title; | |
} | |
}, | |
/** | |
* Return file html | |
* | |
* @param Object file info | |
* @return String | |
**/ | |
itemhtml = function(f) { | |
return templates[list ? 'row' : 'icon'] | |
.replace(/\{([a-z]+)\}/g, function(s, e) { | |
return replacement[e] ? replacement[e](f) : (f[e] ? f[e] : ''); | |
}); | |
}, | |
/** | |
* Flag. Required for msie to avoid unselect files on dragstart | |
* | |
* @type Boolean | |
**/ | |
selectLock = false, | |
/** | |
* Move selection to prev/next file | |
* | |
* @param String move direction | |
* @param Boolean append to current selection | |
* @return void | |
* @rise select | |
*/ | |
select = function(keyCode, append) { | |
var code = $.ui.keyCode, | |
prev = keyCode == code.LEFT || keyCode == code.UP, | |
sel = cwd.find('[id].'+clSelected), | |
selector = prev ? 'first:' : 'last', | |
s, n, sib, top, left; | |
function sibling(n, direction) { | |
return n[direction+'All']('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):first'); | |
} | |
if (sel.length) { | |
s = sel.filter(prev ? ':first' : ':last'); | |
sib = sibling(s, prev ? 'prev' : 'next'); | |
if (!sib.length) { | |
// there is no sibling on required side - do not move selection | |
n = s; | |
} else if (list || keyCode == code.LEFT || keyCode == code.RIGHT) { | |
// find real prevoius file | |
n = sib; | |
} else { | |
// find up/down side file in icons view | |
top = s.position().top; | |
left = s.position().left; | |
n = s; | |
if (prev) { | |
do { | |
n = n.prev('[id]'); | |
} while (n.length && !(n.position().top < top && n.position().left <= left)); | |
if (n.hasClass(clDisabled)) { | |
n = sibling(n, 'next'); | |
} | |
} else { | |
do { | |
n = n.next('[id]'); | |
} while (n.length && !(n.position().top > top && n.position().left >= left)); | |
if (n.hasClass(clDisabled)) { | |
n = sibling(n, 'prev'); | |
} | |
// there is row before last one - select last file | |
if (!n.length) { | |
sib = cwd.find('[id]:not(.'+clDisabled+'):last'); | |
if (sib.position().top > top) { | |
n = sib; | |
} | |
} | |
} | |
} | |
// !append && unselectAll(); | |
} else { | |
// there are no selected file - select first/last one | |
n = cwd.find('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):'+(prev ? 'last' : 'first')); | |
} | |
if (n && n.length && !n.hasClass('elfinder-cwd-parent')) { | |
if (append) { | |
// append new files to selected | |
n = s.add(s[prev ? 'prevUntil' : 'nextUntil']('#'+n.attr('id'))).add(n); | |
} else { | |
// unselect selected files | |
sel.trigger(evtUnselect); | |
} | |
// select file(s) | |
n.trigger(evtSelect); | |
// set its visible | |
scrollToView(n.filter(prev ? ':first' : ':last')); | |
// update cache/view | |
trigger(); | |
} | |
}, | |
selectedFiles = [], | |
selectFile = function(hash) { | |
$('#'+fm.cwdHash2Id(hash)).trigger(evtSelect); | |
}, | |
selectAll = function() { | |
var phash = fm.cwd().hash; | |
cwd.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').trigger(evtSelect); | |
if (lastSearch.length) { | |
selectedFiles = $.map(lastSearch, function(f) { return f.hash; }); | |
} else { | |
selectedFiles = $.map(fm.files(), function(f) { return f.phash == phash ? f.hash : null ;}); | |
} | |
trigger(); | |
}, | |
/** | |
* Unselect all files | |
* | |
* @return void | |
*/ | |
unselectAll = function() { | |
if (selectedFiles.length) { | |
selectLock = false; | |
selectedFiles = []; | |
cwd.find('[id].'+clSelected).trigger(evtUnselect); | |
trigger(); | |
} | |
}, | |
/** | |
* Return selected files hashes list | |
* | |
* @return Array | |
*/ | |
selected = function() { | |
return selectedFiles; | |
}, | |
/** | |
* Fire elfinder "select" event and pass selected files to it | |
* | |
* @return void | |
*/ | |
trigger = function() { | |
fm.trigger('select', {selected : selectedFiles}); | |
}, | |
/** | |
* Scroll file to set it visible | |
* | |
* @param DOMElement file/dir node | |
* @return void | |
*/ | |
scrollToView = function(o) { | |
var ftop = o.position().top, | |
fheight = o.outerHeight(true), | |
wtop = wrapper.scrollTop(), | |
wheight = wrapper.innerHeight(); | |
if (ftop + fheight > wtop + wheight) { | |
wrapper.scrollTop(parseInt(ftop + fheight - wheight)); | |
} else if (ftop < wtop) { | |
wrapper.scrollTop(ftop); | |
} | |
}, | |
/** | |
* Files we get from server but not show yet | |
* | |
* @type Array | |
**/ | |
buffer = [], | |
/** | |
* Return index of elements with required hash in buffer | |
* | |
* @param String file hash | |
* @return Number | |
*/ | |
index = function(hash) { | |
var l = buffer.length; | |
while (l--) { | |
if (buffer[l].hash == hash) { | |
return l; | |
} | |
} | |
return -1; | |
}, | |
/** | |
* Scroll event name | |
* | |
* @type String | |
**/ | |
scrollEvent = 'scroll.'+fm.namespace, | |
/** | |
* jQuery UI selectable option | |
* | |
* @type Object | |
*/ | |
selectableOption = { | |
filter : fileSelector, | |
stop : trigger, | |
delay : 250, | |
selected : function(e, ui) { $(ui.selected).trigger(evtSelect); }, | |
unselected : function(e, ui) { $(ui.unselected).trigger(evtUnselect); } | |
}, | |
/** | |
* Cwd scroll event handler. | |
* Lazy load - append to cwd not shown files | |
* | |
* @return void | |
*/ | |
render = function() { | |
var go = function(){ | |
var html = [], | |
dirs = false, | |
ltmb = [], | |
atmb = {}, | |
last = buffer._last || cwd.find('[id]:last'), | |
top = !last.length, | |
place = buffer._place || (list ? cwd.children('table').children('tbody') : cwd), | |
chk, files, locks; | |
// check draging scroll bar | |
top && (wrapper._top = 0); | |
if (!!wrapper._mousedown && wrapper._top != wrapper.scrollTop()) { | |
wrapper._top = wrapper.scrollTop(); | |
setTimeout(function(){ | |
go(); | |
}, 50); | |
return; | |
} | |
delete buffer._timer; | |
if (!buffer.length) { | |
bottomMarker.hide(); | |
return wrapper.off(scrollEvent); | |
} | |
//progress.show(); | |
while ((!last.length || (chk = last.position().top - (wrapper.height() + wrapper.scrollTop() + fm.options.showThreshold)) <= 0) | |
&& (files = buffer.splice(0, fm.options.showFiles - (chk || 0) / (buffer._hpi || 1))).length) { | |
locks = []; | |
html = $.map(files, function(f) { | |
if (f.hash && f.name) { | |
if (f.mime == 'directory') { | |
dirs = true; | |
} | |
if (f.tmb) { | |
f.tmb === 1 ? ltmb.push(f.hash) : (atmb[f.hash] = f.tmb); | |
} | |
clipCuts[f.hash] && locks.push(f.hash); | |
return itemhtml(f); | |
} | |
return null; | |
}); | |
(top || !buffer.length) && bottomMarker.hide(); | |
place.append(html.join('')); | |
locks.length && fm.trigger('lockfiles', {files: locks}); | |
last = cwd.find('[id]:last'); | |
// scroll top on dir load to avoid scroll after page reload | |
top && wrapper.scrollTop(0); | |
(top || !buffer._hpi) && bottomMarkerShow(place, files.length); | |
if (top) { break; } | |
} | |
// cache last | |
buffer._last = last; | |
// load/attach thumbnails | |
attachThumbnails(atmb); | |
ltmb.length && loadThumbnails(ltmb); | |
// make directory droppable | |
dirs && !mobile && makeDroppable(); | |
if (selectedFiles.length) { | |
place.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').each(function() { | |
var id = fm.cwdId2Hash(this.id); | |
$.inArray(id, selectedFiles) !== -1 && $(this).trigger(evtSelect); | |
}); | |
} | |
}; | |
// stop while scrolling | |
buffer._timer && clearTimeout(buffer._timer); | |
// first time to go() | |
!buffer._timer && go(); | |
// regist next go() | |
buffer._timer = setTimeout(function(){ | |
go(); | |
}, 100); | |
}, | |
/** | |
* Droppable options for cwd. | |
* Drop target is `wrapper` | |
* Do not add class on childs file over | |
* | |
* @type Object | |
*/ | |
droppable = $.extend({}, fm.droppable, { | |
over : function(e, ui) { | |
var dst = $(this), | |
helper = ui.helper, | |
ctr = (e.shiftKey || e.ctrlKey || e.metaKey), | |
hash, status, inParent; | |
e.stopPropagation(); | |
helper.data('dropover', helper.data('dropover') + 1); | |
dst.data('dropover', true); | |
if (helper.data('namespace') !== fm.namespace) { | |
dst.removeClass(clDropActive); | |
return false; | |
} | |
if (dst.hasClass(fm.res(c, 'cwdfile'))) { | |
hash = fm.cwdId2Hash(dst.attr('id')); | |
dst.data('dropover', hash); | |
} else { | |
hash = fm.cwd().hash; | |
fm.cwd().write && dst.data('dropover', hash); | |
} | |
inParent = (fm.file(helper.data('files')[0]).phash === hash); | |
if (dst.data('dropover') === hash) { | |
$.each(helper.data('files'), function(i, h) { | |
if (h === hash || (inParent && !ctr && !helper.hasClass('elfinder-drag-helper-plus'))) { | |
dst.removeClass(clDropActive); | |
return false; // break $.each | |
} | |
}); | |
} else { | |
dst.removeClass(clDropActive); | |
} | |
if (helper.data('locked') || inParent) { | |
status = 'elfinder-drag-helper-plus'; | |
} else { | |
status = 'elfinder-drag-helper-move'; | |
if (ctr) { | |
status += ' elfinder-drag-helper-plus'; | |
} | |
} | |
dst.hasClass(clDropActive) && helper.addClass(status); | |
setTimeout(function(){ dst.hasClass(clDropActive) && helper.addClass(status); }, 20); | |
}, | |
out : function(e, ui) { | |
var helper = ui.helper; | |
e.stopPropagation(); | |
helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0)); | |
$(this).removeData('dropover') | |
.removeClass(clDropActive); | |
}, | |
deactivate : function() { | |
$(this).removeData('dropover') | |
.removeClass(clDropActive); | |
} | |
}), | |
/** | |
* Make directory droppable | |
* | |
* @return void | |
*/ | |
makeDroppable = function() { | |
var targets = cwd.find('.directory:not(.'+clDroppable+',.elfinder-na,.elfinder-ro)'); | |
if (fm.isCommandEnabled('paste')) { | |
targets.droppable(droppable); | |
} | |
if (fm.isCommandEnabled('upload')) { | |
targets.addClass('native-droppable'); | |
} | |
}, | |
/** | |
* Preload required thumbnails and on load add css to files. | |
* Return false if required file is not visible yet (in buffer) - | |
* required for old api to stop loading thumbnails. | |
* | |
* @param Object file hash -> thumbnail map | |
* @return Boolean | |
*/ | |
attachThumbnails = function(images) { | |
var url = fm.option('tmbUrl'), | |
ret = true, | |
ndx; | |
$.each(images, function(hash, tmb) { | |
var node = $('#'+fm.cwdHash2Id(hash)); | |
if (node.length) { | |
(function(node, tmb) { | |
$('<img/>') | |
.load(function() { node.find('.elfinder-cwd-icon').css('background', "url('"+tmb+"') center center no-repeat"); }) | |
.attr('src', tmb); | |
})(node, fm.searchStatus.state? fm.tmb(hash) : (url + tmb)); | |
} else { | |
ret = false; | |
if ((ndx = index(hash)) != -1) { | |
buffer[ndx].tmb = tmb; | |
} | |
} | |
}); | |
return ret; | |
}, | |
/** | |
* Load thumbnails from backend. | |
* | |
* @param Array|Boolean files hashes list for new api | true for old api | |
* @return void | |
*/ | |
loadThumbnails = function(files) { | |
var tmbs = []; | |
if (fm.oldAPI) { | |
fm.request({ | |
data : {cmd : 'tmb', current : fm.cwd().hash}, | |
preventFail : true | |
}) | |
.done(function(data) { | |
if (attachThumbnails(data.images||[]) && data.tmb) { | |
loadThumbnails(); | |
} | |
}); | |
return; | |
} | |
tmbs = tmbs = files.splice(0, tmbNum); | |
if (tmbs.length) { | |
fm.request({ | |
data : {cmd : 'tmb', targets : tmbs}, | |
preventFail : true | |
}) | |
.done(function(data) { | |
if (attachThumbnails(data.images||[])) { | |
loadThumbnails(files); | |
} | |
}); | |
} | |
}, | |
/** | |
* Add new files to cwd/buffer | |
* | |
* @param Array new files | |
* @return void | |
*/ | |
add = function(files) { | |
var place = list ? cwd.find('tbody') : cwd, | |
l = files.length, | |
ltmb = [], | |
atmb = {}, | |
dirs = false, | |
findNode = function(file) { | |
var pointer = cwd.find('[id]:first'), file2; | |
while (pointer.length) { | |
file2 = fm.file(fm.cwdId2Hash(pointer.attr('id'))); | |
if (!pointer.hasClass('elfinder-cwd-parent') && file2 && fm.compare(file, file2) < 0) { | |
return pointer; | |
} | |
pointer = pointer.next('[id]'); | |
} | |
}, | |
findIndex = function(file) { | |
var l = buffer.length, i; | |
for (i =0; i < l; i++) { | |
if (fm.compare(file, buffer[i]) < 0) { | |
return i; | |
} | |
} | |
return l || -1; | |
}, | |
file, hash, node, ndx; | |
l && wrapper.removeClass('elfinder-cwd-wrapper-empty'); | |
while (l--) { | |
file = files[l]; | |
hash = file.hash; | |
if ($('#'+fm.cwdHash2Id(hash)).length) { | |
continue; | |
} | |
if ((node = findNode(file)) && node.length) { | |
node.before(itemhtml(file)); | |
} else if ((ndx = findIndex(file)) >= 0) { | |
buffer.splice(ndx, 0, file); | |
} else { | |
place.append(itemhtml(file)); | |
} | |
if ($('#'+fm.cwdHash2Id(hash)).length) { | |
if (file.mime == 'directory') { | |
dirs = true; | |
} else if (file.tmb) { | |
file.tmb === 1 ? ltmb.push(hash) : (atmb[hash] = file.tmb); | |
} | |
} | |
} | |
bottomMarkerShow(place); | |
attachThumbnails(atmb); | |
ltmb.length && loadThumbnails(ltmb); | |
dirs && !mobile && makeDroppable(); | |
}, | |
/** | |
* Remove files from cwd/buffer | |
* | |
* @param Array files hashes | |
* @return void | |
*/ | |
remove = function(files) { | |
var l = files.length, hash, n, ndx; | |
// removed cwd | |
if (!fm.cwd().hash && fm.currentReqCmd !== 'open') { | |
$.each(cwdParents.reverse(), function(i, h) { | |
if (fm.files()[h]) { | |
fm.one(fm.currentReqCmd, function(e, fm) { | |
!fm.cwd().hash && fm.exec('open', h); | |
}); | |
return false; | |
} | |
}); | |
return; | |
} | |
while (l--) { | |
hash = files[l]; | |
if ((n = $('#'+fm.cwdHash2Id(hash))).length) { | |
try { | |
n.remove(); | |
} catch(e) { | |
fm.debug('error', e); | |
} | |
} else if ((ndx = index(hash)) != -1) { | |
buffer.splice(ndx, 1); | |
} | |
} | |
// refresh cwd if empty for a bug of browser (ex. Android Chrome 43.0.2357.93) | |
if (cwd.children().length < 1) { | |
wrapper.addClass('elfinder-cwd-wrapper-empty'); | |
cwd.hide(); | |
setTimeout(function(){ cwd.show(); }, 0); | |
} | |
}, | |
msg = { | |
name : fm.i18n('name'), | |
perm : fm.i18n('perms'), | |
date : fm.i18n('modify'), | |
size : fm.i18n('size'), | |
kind : fm.i18n('kind'), | |
modestr : fm.i18n('mode'), | |
modeoct : fm.i18n('mode'), | |
modeboth : fm.i18n('mode'), | |
}, | |
customColsNameBuild = function() { | |
var name = '', | |
customColsName = '', | |
columns = fm.options.uiOptions.cwd.listView.columns, | |
names = $.extend({}, msg, fm.options.uiOptions.cwd.listView.columnsCustomName); | |
for (var i = 0; i < columns.length; i++) { | |
if (typeof names[columns[i]] !== 'undefined') { | |
name = names[columns[i]]; | |
} else { | |
name = fm.i18n(columns[i]); | |
} | |
customColsName +='<td class="elfinder-cwd-view-th-'+columns[i]+'">'+name+'</td>'; | |
} | |
return customColsName; | |
}, | |
bottomMarkerShow = function(place, cnt) { | |
var ph; | |
place = place || (list ? cwd.find('tbody') : cwd); | |
if (buffer.length > 0) { | |
place.css({height: 'auto'}); | |
ph = place.height(); | |
cnt && (buffer._hpi = ph / cnt); | |
bottomMarker.css({top: (buffer._hpi * buffer.length + ph) + 'px'}).show(); | |
} | |
}, | |
/** | |
* Update directory content | |
* | |
* @param Array files | |
* @return void | |
*/ | |
content = function(files, any) { | |
var phash = fm.cwd().hash; | |
cwdParents = fm.parents(phash); | |
unselectAll(); | |
try { | |
// to avoid problem with draggable | |
cwd.empty(); | |
} catch (e) { | |
cwd.html(''); | |
} | |
cwd.removeClass('elfinder-cwd-view-icons elfinder-cwd-view-list') | |
.addClass('elfinder-cwd-view-'+(list ? 'list' :'icons')); | |
cwd.css('height', 'auto'); | |
bottomMarker.hide(); | |
wrapper[list ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-list') | |
._padding = parseInt(wrapper.css('padding-top')) + parseInt(wrapper.css('padding-bottom')); | |
if (fm.UA.iOS) { | |
wrapper.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch'); | |
} | |
list && cwd.html('<table><thead><tr class="ui-state-default'+(fm.UA.Touch? ' elfinder-touch' : '')+'"><td class="elfinder-cwd-view-th-name">'+msg.name+'</td>'+customColsNameBuild()+'</tr></thead><tbody/></table>'); | |
buffer = $.map(files, function(f) { return any || f.phash == phash ? f : null; }); | |
buffer = fm.sortFiles(buffer); | |
wrapper[(!any && buffer.length < 1) ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-empty').on(scrollEvent, render).trigger(scrollEvent); | |
phash = fm.cwd().phash; | |
if (options.oldSchool && phash && !query) { | |
var parent = $.extend(true, {}, fm.file(phash), {name : '..', mime : 'directory'}); | |
parent = $(itemhtml(parent)) | |
.addClass('elfinder-cwd-parent') | |
.bind('mousedown click mouseup touchstart touchmove touchend dblclick mouseenter', function(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
}) | |
.dblclick(function() { | |
fm.exec('open', fm.cwdId2Hash(this.id)); | |
}); | |
(list ? cwd.find('tbody') : cwd).prepend(parent); | |
} | |
// set droppable | |
if (any || !fm.cwd().write) { | |
wrapper.removeClass('native-droppable') | |
.droppable('disable'); | |
} else { | |
wrapper[fm.isCommandEnabled('upload')? 'addClass' : 'removeClass']('native-droppable'); | |
wrapper.droppable('enable'); | |
} | |
}, | |
/** | |
* CWD node itself | |
* | |
* @type JQuery | |
**/ | |
cwd = $(this) | |
.addClass('ui-helper-clearfix elfinder-cwd') | |
.attr('unselectable', 'on') | |
// fix ui.selectable bugs and add shift+click support | |
.on('click.'+fm.namespace, fileSelector, function(e) { | |
var p = this.id ? $(this) : $(this).parents('[id]:first'), | |
prev, | |
next, | |
pl, | |
nl, | |
sib; | |
if (cwd.data('longtap')) { | |
e.stopPropagation(); | |
return; | |
} | |
e.stopImmediatePropagation(); | |
if (e.shiftKey) { | |
prev = p.prevAll('.'+clSelected+':first'); | |
next = p.nextAll('.'+clSelected+':first'); | |
pl = prev.length; | |
nl = next.length; | |
} | |
if (e.shiftKey && (pl || nl)) { | |
sib = pl ? p.prevUntil('#'+prev.attr('id')) : p.nextUntil('#'+next.attr('id')); | |
sib.add(p).trigger(evtSelect); | |
} else if (e.ctrlKey || e.metaKey) { | |
p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect); | |
} else { | |
if (p.data('touching') && p.hasClass(clSelected)) { | |
p.data('touching', null); | |
fm.dblclick({file : fm.cwdId2Hash(this.id)}); | |
return; | |
} else { | |
unselectAll(); | |
p.trigger(evtSelect); | |
} | |
} | |
trigger(); | |
}) | |
// call fm.open() | |
.on('dblclick.'+fm.namespace, fileSelector, function(e) { | |
fm.dblclick({file : fm.cwdId2Hash(this.id)}); | |
}) | |
// for touch device | |
.on('touchstart.'+fm.namespace, fileSelector, function(e) { | |
e.stopPropagation(); | |
if (e.target.nodeName == 'INPUT' || e.target.nodeName == 'TEXTAREA') { | |
return; | |
} | |
var p = this.id ? $(this) : $(this).parents('[id]:first'), | |
sel = p.prevAll('.'+clSelected+':first').length + | |
p.nextAll('.'+clSelected+':first').length; | |
cwd.data('longtap', null); | |
p.addClass(clHover) | |
.data('touching', true) | |
.data('tmlongtap', setTimeout(function(){ | |
// long tap | |
cwd.data('longtap', true); | |
if (p.hasClass(clSelected) && sel > 0) { | |
p.trigger(evtUnselect); | |
trigger(); | |
} else { | |
if (e.target.nodeName != 'TD' || fm.selected().length > 0) { | |
p.trigger(evtSelect); | |
trigger(); | |
fm.trigger('contextmenu', { | |
'type' : 'files', | |
'targets' : fm.selected(), | |
'x' : e.originalEvent.touches[0].pageX, | |
'y' : e.originalEvent.touches[0].pageY | |
}); | |
} | |
} | |
}, 500)); | |
}) | |
.on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, fileSelector, function(e) { | |
e.stopPropagation(); | |
if (e.target.nodeName == 'INPUT' || e.target.nodeName == 'TEXTAREA') { | |
return; | |
} | |
var p = this.id ? $(this) : $(this).parents('[id]:first'); | |
clearTimeout(p.data('tmlongtap')); | |
if (e.type == 'touchmove') { | |
p.data('touching', null); | |
p.removeClass(clHover); | |
} else if (p.data('touching') && !cwd.data('longtap') && p.hasClass(clSelected)) { | |
e.preventDefault(); | |
p.data('touching', null); | |
fm.dblclick({file : fm.cwdId2Hash(this.id)}); | |
} | |
}) | |
// attach draggable | |
.on('mouseenter.'+fm.namespace, fileSelector, function(e) { | |
var $this = $(this), helper = null, | |
target = list ? $this : $this.children('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename'); | |
if (!mobile && !$this.hasClass(clTmp) && !target.hasClass(clDraggable+' '+clDisabled)) { | |
target.on('mousedown', function(e) { | |
// shiftKey + drag start for HTML5 native drag function | |
if (e.shiftKey && !fm.UA.IE && cwd.data('selectable')) { | |
// destroy jQuery-ui selectable while trigger native drag | |
cwd.selectable('destroy').data('selectable', false); | |
setTimeout(function(){ | |
cwd.selectable(selectableOption).data('selectable', true); | |
}, 10); | |
} | |
target.draggable('option', 'disabled', e.shiftKey); | |
if (e.shiftKey) { | |
target.attr('draggable', 'true'); | |
} else { | |
target.attr('draggable', 'false') | |
.draggable('option', 'cursorAt', {left: 50 - parseInt($(e.currentTarget).css('margin-left')), top: 47}); | |
} | |
}) | |
.on('dragstart', function(e) { | |
var dt = e.dataTransfer || e.originalEvent.dataTransfer || null; | |
helper = null; | |
if (dt && !fm.UA.IE) { | |
var p = this.id ? $(this) : $(this).parents('[id]:first'), | |
elm = $('<span>'), | |
url = '', | |
durl = null, | |
murl = null, | |
files = [], | |
icon = function(f) { | |
var mime = f.mime, i; | |
i = '<div class="elfinder-cwd-icon '+fm.mime2class(mime)+' ui-corner-all"/>'; | |
if (f.tmb && f.tmb !== 1) { | |
i = $(i).css('background', "url('"+fm.option('tmbUrl')+f.tmb+"') center center no-repeat").get(0).outerHTML; | |
} | |
return i; | |
}, l, geturl = []; | |
p.trigger(evtSelect); | |
trigger(); | |
$.each(selectedFiles, function(i, v){ | |
var file = fm.file(v), | |
furl = file.url; | |
if (file && file.mime !== 'directory') { | |
if (!furl) { | |
furl = fm.url(file.hash); | |
} else if (furl == '1') { | |
geturl.push(v); | |
return true; | |
} | |
if (furl) { | |
furl = fm.convAbsUrl(furl); | |
files.push(v); | |
$('<a>').attr('href', furl).text(furl).appendTo(elm); | |
url += furl + "\n"; | |
if (!durl) { | |
durl = file.mime + ':' + file.name + ':' + furl; | |
} | |
if (!murl) { | |
murl = furl + "\n" + file.name; | |
} | |
} | |
} | |
}); | |
if (geturl.length) { | |
$.each(geturl, function(i, v){ | |
var rfile = fm.file(v); | |
rfile.url = ''; | |
fm.request({ | |
data : {cmd : 'url', target : v}, | |
notify : {type : 'url', cnt : 1}, | |
preventDefault : true | |
}) | |
.always(function(data) { | |
rfile.url = data.url? data.url : '1'; | |
}); | |
}); | |
return false; | |
} else if (url) { | |
if (dt.setDragImage) { | |
helper = $('<div class="elfinder-drag-helper html5-native"></div>').append(icon(fm.file(files[0]))).appendTo($(document.body)); | |
if ((l = files.length) > 1) { | |
helper.append(icon(fm.file(files[l-1])) + '<span class="elfinder-drag-num">'+l+'</span>'); | |
} | |
dt.setDragImage(helper.get(0), 50, 47); | |
} | |
dt.effectAllowed = 'copyLink'; | |
dt.setData('DownloadURL', durl); | |
dt.setData('text/x-moz-url', murl); | |
dt.setData('text/uri-list', url); | |
dt.setData('text/plain', url); | |
dt.setData('text/html', elm.html()); | |
dt.setData('elfinderfrom', window.location.href + fm.cwd().hash); | |
dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), ''); | |
} else { | |
return false; | |
} | |
} | |
}) | |
.on('dragend', function(e){ | |
unselectAll(); | |
helper && helper.remove(); | |
}) | |
.draggable(fm.draggable); | |
} | |
}) | |
// add hover class to selected file | |
.on(evtSelect, fileSelector, function(e) { | |
var $this = $(this), | |
id = fm.cwdId2Hash($this.attr('id')); | |
if (!selectLock && !$this.hasClass(clDisabled)) { | |
$this.addClass(clSelected).children().addClass(clHover); | |
if ($.inArray(id, selectedFiles) === -1) { | |
selectedFiles.push(id); | |
} | |
} | |
}) | |
// remove hover class from unselected file | |
.on(evtUnselect, fileSelector, function(e) { | |
var $this = $(this), | |
id = fm.cwdId2Hash($this.attr('id')), | |
ndx; | |
if (!selectLock) { | |
$(this).removeClass(clSelected).children().removeClass(clHover); | |
ndx = $.inArray(id, selectedFiles); | |
if (ndx !== -1) { | |
selectedFiles.splice(ndx, 1); | |
} | |
} | |
}) | |
// disable files wich removing or moving | |
.on(evtDisable, fileSelector, function() { | |
var $this = $(this).removeClass(clHover+' '+clSelected).addClass(clDisabled), | |
child = $this.children(), | |
target = (list ? $this : child); | |
child.removeClass(clHover+' '+clSelected); | |
$this.hasClass(clDroppable) && $this.droppable('disable'); | |
target.hasClass(clDraggable) && target.draggable('disable'); | |
}) | |
// if any files was not removed/moved - unlock its | |
.on(evtEnable, fileSelector, function() { | |
var $this = $(this).removeClass(clDisabled), | |
target = list ? $this : $this.children(); | |
$this.hasClass(clDroppable) && $this.droppable('enable'); | |
target.hasClass(clDraggable) && target.draggable('enable'); | |
}) | |
.on('scrolltoview', fileSelector, function() { | |
scrollToView($(this)); | |
}) | |
.on('mouseenter.'+fm.namespace+' mouseleave.'+fm.namespace, fileSelector, function(e) { | |
fm.trigger('hover', {hash : fm.cwdId2Hash($(this).attr('id')), type : e.type}); | |
$(this).toggleClass(clHover, (e.type == 'mouseenter')); | |
}) | |
.on('contextmenu.'+fm.namespace, function(e) { | |
var file = $(e.target).closest('.'+clFile); | |
if (file.length && (e.target.nodeName != 'TD' || $.inArray(fm.cwdId2Hash(file.get(0).id), fm.selected()) > -1)) { | |
e.stopPropagation(); | |
e.preventDefault(); | |
if (!file.hasClass(clDisabled) && !file.data('touching')) { | |
if (!file.hasClass(clSelected)) { | |
unselectAll(); | |
file.trigger(evtSelect); | |
trigger(); | |
} | |
fm.trigger('contextmenu', { | |
'type' : 'files', | |
'targets' : fm.selected(), | |
'x' : e.pageX, | |
'y' : e.pageY | |
}); | |
} | |
} | |
}) | |
// unselect all on cwd click | |
.on('click.'+fm.namespace, function(e) { | |
if (cwd.data('longtap')) { | |
e.stopPropagation(); | |
return; | |
} | |
!e.shiftKey && !e.ctrlKey && !e.metaKey && unselectAll(); | |
}) | |
// prepend fake file/dir | |
.on('create.'+fm.namespace, function(e, file) { | |
var parent = list ? cwd.find('tbody') : cwd, | |
p = parent.find('.elfinder-cwd-parent'), | |
file = $(itemhtml(file)).addClass(clTmp), | |
selected = fm.selected(); | |
if (selected.length) { | |
fm.trigger('lockfiles', {files: selected}); | |
} else { | |
unselectAll(); | |
} | |
if (p.length) { | |
p.after(file); | |
} else { | |
parent.prepend(file); | |
} | |
cwd.parent().scrollTop(0); | |
}) | |
// unselect all selected files | |
.on('unselectall', unselectAll) | |
.on('selectfile', function(e, id) { | |
$('#'+fm.cwdHash2Id(id)).trigger(evtSelect); | |
trigger(); | |
}), | |
wrapper = $('<div class="elfinder-cwd-wrapper"/>') | |
// make cwd itself droppable for folders from nav panel | |
.droppable($.extend({}, droppable, {autoDisable: false})) | |
.on('contextmenu', function(e) { | |
e.preventDefault(); | |
fm.trigger('contextmenu', { | |
'type' : 'cwd', | |
'targets' : [fm.cwd().hash], | |
'x' : e.pageX, | |
'y' : e.pageY | |
}); | |
}) | |
// for touch device | |
.on('touchstart.'+fm.namespace, function(e) { | |
var p = $(this); | |
cwd.data('longtap', null); | |
p.data('touching', true); | |
p.data('tmlongtap', setTimeout(function(){ | |
// long tap | |
cwd.data('longtap', true); | |
fm.trigger('contextmenu', { | |
'type' : 'cwd', | |
'targets' : [fm.cwd().hash], | |
'x' : e.originalEvent.touches[0].pageX, | |
'y' : e.originalEvent.touches[0].pageY | |
}); | |
}, 500)); | |
}) | |
.on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, function(e) { | |
clearTimeout($(this).data('tmlongtap')); | |
}) | |
.on('mousedown', function(){wrapper._mousedown = true;}) | |
.on('mouseup', function(){wrapper._mousedown = false;}), | |
bottomMarker = $('<div> </div>') | |
.css({position: 'absolute', width: '1px', height: '1px'}) | |
.hide(), | |
restm = null, | |
resize = function(init) { | |
var initHeight = function() { | |
var h = 0; | |
wrapper.siblings('div.elfinder-panel:visible').each(function() { | |
h += $(this).outerHeight(true); | |
}); | |
wrapper.height(wz.height() - h - wrapper._padding); | |
}; | |
init && initHeight(); | |
restm && clearTimeout(restm); | |
restm = setTimeout(function(){ | |
!init && initHeight(); | |
var wph, cwdoh; | |
// fix cwd height if it less then wrapper | |
cwd.css('height', 'auto'); | |
wph = wrapper[0].clientHeight - parseInt(wrapper.css('padding-top')) - parseInt(wrapper.css('padding-bottom')), | |
cwdoh = cwd.outerHeight(true); | |
if (cwdoh < wph) { | |
cwd.height(wph); | |
} | |
}, 20); | |
}, | |
// elfinder node | |
parent = $(this).parent().resize(resize), | |
// workzone node | |
wz = parent.children('.elfinder-workzone').append(wrapper.append(this).append(bottomMarker)) | |
; | |
if (mobile) { | |
// for iOS5 bug | |
$('body').on('touchstart touchmove touchend', function(e){}); | |
} else { | |
// make files selectable | |
cwd | |
.selectable(selectableOption) | |
.data('selectable', true); | |
} | |
fm | |
.one('init', function(){ | |
var style = document.createElement('style'), | |
sheet; | |
document.head.appendChild(style); | |
sheet = style.sheet; | |
sheet.insertRule('.elfinder-cwd-wrapper-empty .elfinder-cwd:after{ content:"'+fm.i18n('emptyFolder')+'" }', 0); | |
sheet.insertRule('.ui-droppable.elfinder-cwd-wrapper-empty .elfinder-cwd:after{ content:"'+fm.i18n('emptyFolder'+(mobile? 'LTap' : 'Drop'))+'" }', 1); | |
sheet.insertRule('.ui-droppable.elfinder-cwd-wrapper-empty.ui-droppable-disabled .elfinder-cwd:after{ content:"'+fm.i18n('emptyFolder')+'" }', 2); | |
}) | |
.bind('open', function(e) { | |
content(e.data.files); | |
resize(); | |
}) | |
.bind('search', function(e) { | |
lastSearch = e.data.files; | |
content(lastSearch, true); | |
wrapper.addClass('elfinder-search-result'); | |
fm.autoSync('stop'); | |
resize(); | |
}) | |
.bind('searchend', function(e) { | |
lastSearch = []; | |
if (query) { | |
query = ''; | |
if (!e.data || !e.data.noupdate) { | |
content(fm.files()); | |
} | |
} | |
wrapper.removeClass('elfinder-search-result'); | |
fm.autoSync(); | |
resize(); | |
}) | |
.bind('searchstart', function(e) { | |
query = e.data.query; | |
}) | |
.bind('sortchange', function() { | |
content(query ? lastSearch : fm.files(), !!query); | |
resize(); | |
}) | |
.bind('viewchange', function() { | |
var sel = fm.selected(), | |
l = fm.storage('view') == 'list'; | |
if (l != list) { | |
list = l; | |
content(query ? lastSearch : fm.files(), !!query); | |
$.each(sel, function(i, h) { | |
selectFile(h); | |
}); | |
trigger(); | |
} | |
resize(); | |
}) | |
.bind('resize', function() { | |
var place = list ? cwd.find('tbody') : cwd; | |
resize(true); | |
bottomMarkerShow(place, place.find('[id]').length); | |
}) | |
.bind('add', function() { | |
resize(); | |
}) | |
.bind('changeclipboard', function(e) { | |
clipCuts = {}; | |
if (e.data && e.data.clipboard && e.data.clipboard.length) { | |
$.each(e.data.clipboard, function(i, f) { | |
if (f.cut) { | |
clipCuts[f.hash] = true; | |
} | |
}); | |
} | |
}) | |
.add(function(e) { | |
var phash = fm.cwd().hash, | |
files = query | |
? $.map(e.data.added || [], function(f) { return f.name.indexOf(query) === -1 ? null : f ;}) | |
: $.map(e.data.added || [], function(f) { return f.phash == phash ? f : null; }) | |
; | |
add(files); | |
}) | |
.change(function(e) { | |
var phash = fm.cwd().hash, | |
sel = fm.selected(), | |
files; | |
if (query) { | |
$.each(e.data.changed || [], function(i, file) { | |
remove([file.hash]); | |
if (file.name.indexOf(query) !== -1) { | |
add([file]); | |
$.inArray(file.hash, sel) !== -1 && selectFile(file.hash); | |
} | |
}); | |
} else { | |
$.each($.map(e.data.changed || [], function(f) { return f.phash == phash ? f : null; }), function(i, file) { | |
remove([file.hash]); | |
add([file]); | |
$.inArray(file.hash, sel) !== -1 && selectFile(file.hash); | |
}); | |
} | |
trigger(); | |
}) | |
.remove(function(e) { | |
remove(e.data.removed || []); | |
trigger(); | |
}) | |
// select dragged file if no selected, disable selectable | |
.dragstart(function(e) { | |
var target = $(e.data.target), | |
oe = e.data.originalEvent; | |
if (target.hasClass(fileSelector.substr(1))) { | |
if (!target.hasClass(clSelected)) { | |
!(oe.ctrlKey || oe.metaKey || oe.shiftKey) && unselectAll(); | |
target.trigger(evtSelect); | |
trigger(); | |
} | |
} | |
cwd.selectable('disable').removeClass(clDisabled); | |
selectLock = true; | |
}) | |
// enable selectable | |
.dragstop(function() { | |
cwd.selectable('enable'); | |
selectLock = false; | |
}) | |
.bind('lockfiles unlockfiles selectfiles unselectfiles', function(e) { | |
var events = { | |
lockfiles : evtDisable , | |
unlockfiles : evtEnable , | |
selectfiles : evtSelect, | |
unselectfiles : evtUnselect }, | |
event = events[e.type], | |
files = e.data.files || [], | |
l = files.length, | |
helper = e.data.helper || $(), | |
parents, ctr; | |
if (l > 0) { | |
parents = fm.parents(files[0]); | |
} | |
if (!helper.data('locked')) { | |
while (l--) { | |
$('#'+fm.cwdHash2Id(files[l])).trigger(event); | |
} | |
trigger(); | |
} | |
if (wrapper.data('dropover') && parents.indexOf(wrapper.data('dropover')) !== -1) { | |
ctr = e.type !== 'lockfiles'; | |
helper.toggleClass('elfinder-drag-helper-plus', ctr); | |
wrapper.toggleClass(clDropActive, ctr); | |
} | |
}) | |
// select new files after some actions | |
.bind('mkdir mkfile duplicate upload rename archive extract paste multiupload', function(e) { | |
if (e.type == 'upload' && e.data._multiupload) return; | |
var phash = fm.cwd().hash, files; | |
unselectAll(); | |
$.each(e.data.added || [], function(i, file) { | |
file && file.phash == phash && selectFile(file.hash); | |
}); | |
trigger(); | |
}) | |
.shortcut({ | |
pattern :'ctrl+a', | |
description : 'selectall', | |
callback : selectAll | |
}) | |
.shortcut({ | |
pattern : 'left right up down shift+left shift+right shift+up shift+down', | |
description : 'selectfiles', | |
type : 'keydown' , //fm.UA.Firefox || fm.UA.Opera ? 'keypress' : 'keydown', | |
callback : function(e) { select(e.keyCode, e.shiftKey); } | |
}) | |
.shortcut({ | |
pattern : 'home', | |
description : 'selectffile', | |
callback : function(e) { | |
unselectAll(); | |
scrollToView(cwd.find('[id]:first').trigger(evtSelect)); | |
trigger(); | |
} | |
}) | |
.shortcut({ | |
pattern : 'end', | |
description : 'selectlfile', | |
callback : function(e) { | |
unselectAll(); | |
scrollToView(cwd.find('[id]:last').trigger(evtSelect)) ; | |
trigger(); | |
} | |
}); | |
}); | |
// fm.timeEnd('cwdLoad') | |
return this; | |
} | |
/* | |
* File: /js/ui/dialog.js | |
*/ | |
/** | |
* @class elFinder dialog | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfinderdialog = function(opts) { | |
var dialog; | |
if (typeof(opts) == 'string' && (dialog = this.closest('.ui-dialog')).length) { | |
if (opts == 'open') { | |
dialog.css('display') == 'none' && dialog.fadeIn(120, function() { | |
dialog.trigger('open'); | |
}); | |
} else if (opts == 'close') { | |
dialog.css('display') != 'none' && dialog.hide().trigger('close'); | |
} else if (opts == 'destroy') { | |
dialog.hide().remove(); | |
} else if (opts == 'toTop') { | |
dialog.trigger('totop'); | |
} else if (opts == 'posInit') { | |
dialog.trigger('posinit'); | |
} | |
} | |
opts = $.extend({}, $.fn.elfinderdialog.defaults, opts); | |
this.filter(':not(.ui-dialog-content)').each(function() { | |
var self = $(this).addClass('ui-dialog-content ui-widget-content'), | |
parent = self.parent(), | |
clactive = 'elfinder-dialog-active', | |
cldialog = 'elfinder-dialog', | |
clnotify = 'elfinder-dialog-notify', | |
clhover = 'ui-state-hover', | |
id = parseInt(Math.random()*1000000), | |
overlay = parent.children('.elfinder-overlay'), | |
buttonset = $('<div class="ui-dialog-buttonset"/>'), | |
buttonpane = $('<div class=" ui-helper-clearfix ui-dialog-buttonpane ui-widget-content"/>') | |
.append(buttonset), | |
btnWidth = 0, | |
platformWin = (window.navigator.platform.indexOf('Win') != -1), | |
dialog = $('<div class="ui-front ui-dialog ui-widget ui-widget-content ui-corner-all ui-draggable std42-dialog '+cldialog+' '+opts.cssClass+'"/>') | |
.hide() | |
.append(self) | |
.appendTo(parent) | |
.draggable({ | |
handle : '.ui-dialog-titlebar', | |
containment : 'document', | |
stop : function(e, ui){ | |
dialog.css({height : opts.height}); | |
} | |
}) | |
.css({ | |
width : opts.width, | |
height : opts.height//, | |
//maxWidth: opts.maxWidth? opts.maxWidth : $(window).width()-10, | |
//maxHeight: opts.maxHeight? opts.maxHeight : $(window).height()-20 | |
}) | |
.mousedown(function(e) { | |
setTimeout(function() { | |
dialog.is(':visible') && dialog.trigger('totop'); | |
}, 10); | |
}) | |
.on('open', function() { | |
var d = $(this), | |
maxWinWidth = (d.outerWidth() > parent.width()-10)? parent.width()-10 : null; | |
maxWinWidth && d.css({width: maxWinWidth, left: '5px'}); | |
if (!dialog.hasClass(clnotify)) { | |
parent.find('.'+cldialog+':visible').not('.'+clnotify).each(function() { | |
var d = $(this), | |
top = parseInt(d.css('top')), | |
left = parseInt(d.css('left')), | |
_top = parseInt(dialog.css('top')), | |
_left = parseInt(dialog.css('left')) | |
; | |
if (d[0] != dialog[0] && (top == _top || left == _left)) { | |
dialog.css({ | |
top : (top+(maxWinWidth? 15 : 10))+'px', | |
left : (maxWinWidth? 5 : left+10)+'px' | |
}); | |
} | |
}); | |
} | |
dialog.trigger('totop'); | |
typeof(opts.open) == 'function' && $.proxy(opts.open, self[0])(); | |
}) | |
.on('close', function() { | |
var dialogs = parent.find('.elfinder-dialog:visible'); | |
$(this).data('modal') && overlay.elfinderoverlay('hide'); | |
// get focus to next dialog | |
if (dialogs.length) { | |
dialogs.find(':last').trigger('totop'); | |
} else { | |
// return focus to parent | |
setTimeout(function() { | |
parent.mousedown().click(); | |
}, 10); | |
} | |
if (typeof(opts.close) == 'function') { | |
$.proxy(opts.close, self[0])(); | |
} else if (opts.destroyOnClose) { | |
dialog.hide().remove(); | |
} | |
}) | |
.on('totop', function() { | |
var $this = $(this); | |
parent.find('.'+cldialog+':visible').removeClass(clactive+' ui-front'); | |
dialog.addClass(clactive+' ui-front'); | |
if (!$this.find('input,textarea').length) { | |
$this.find('.ui-button:'+(platformWin? 'first':'last')).focus().end().find(':text:first').focus(); | |
} | |
$this.data('modal') && overlay.is(':hidden') && overlay.elfinderoverlay('show'); | |
}) | |
.on('posinit', function() { | |
var css = opts.position; | |
if (!css) { | |
css = { | |
top : Math.max(0, parseInt((parent.height() - dialog.outerHeight())/2 - 42))+'px', | |
left : Math.max(0, parseInt((parent.width() - dialog.outerWidth())/2))+'px' | |
}; | |
} | |
dialog.css(css); | |
}) | |
.data({modal: opts.modal}) | |
; | |
dialog.trigger('posinit'); | |
if (opts.closeOnEscape) { | |
$(document).on('keyup.'+id, function(e) { | |
if (e.keyCode == $.ui.keyCode.ESCAPE && dialog.hasClass(clactive)) { | |
self.elfinderdialog('close'); | |
$(document).off('keyup.'+id); | |
} | |
}) | |
} | |
dialog.prepend( | |
$('<div class="ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix">'+opts.title+'</div>') | |
.prepend($('<a href="#" class="ui-dialog-titlebar-close ui-corner-all"><span class="ui-icon ui-icon-closethick"/></a>') | |
.mousedown(function(e) { | |
e.preventDefault(); | |
self.elfinderdialog('close'); | |
})) | |
); | |
$.each(opts.buttons, function(name, cb) { | |
var button = $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">'+name+'</span></button>') | |
.click($.proxy(cb, self[0])) | |
.hover(function(e) { | |
if (opts.btnHoverFocus) { | |
$(this)[e.type == 'mouseenter' ? 'focus' : 'blur'](); | |
} else { | |
$(this).toggleClass(clhover, e.type == 'mouseenter'); | |
} | |
}) | |
.focus(function() { $(this).addClass(clhover) }) | |
.blur(function() { $(this).removeClass(clhover) }) | |
.keydown(function(e) { | |
var next; | |
e.stopPropagation(); | |
if (e.keyCode == $.ui.keyCode.ENTER) { | |
e.preventDefault(); | |
$(this).click(); | |
} else if (e.keyCode == $.ui.keyCode.TAB || e.keyCode == $.ui.keyCode.RIGHT) { | |
e.preventDefault(); | |
next = $(this).next('.ui-button'); | |
next.length ? next.focus() : $(this).parent().children('.ui-button:first').focus(); | |
} else if (e.keyCode == $.ui.keyCode.LEFT) { | |
e.preventDefault(); | |
next = $(this).prev('.ui-button'); | |
next.length ? next.focus() : $(this).parent().children('.ui-button:last').focus() | |
} | |
}) | |
if (platformWin) { | |
buttonset.append(button); | |
} else { | |
buttonset.prepend(button); | |
} | |
}); | |
if (buttonset.children().length) { | |
dialog.append(buttonpane); | |
dialog.show(); | |
buttonpane.find('button').each(function(i, btn) { | |
btnWidth += $(btn).outerWidth(true); | |
}); | |
dialog.hide(); | |
btnWidth += 20; | |
if (dialog.width() < btnWidth) { | |
dialog.width(btnWidth); | |
} | |
} | |
if (opts.resizable && $.fn.resizable) { | |
dialog.resizable({ | |
minWidth : opts.minWidth, | |
minHeight : opts.minHeight, | |
alsoResize : this | |
}); | |
} | |
typeof(opts.create) == 'function' && $.proxy(opts.create, this)(); | |
opts.autoOpen && self.elfinderdialog('open'); | |
}); | |
return this; | |
} | |
$.fn.elfinderdialog.defaults = { | |
cssClass : '', | |
title : '', | |
modal : false, | |
resizable : true, | |
autoOpen : true, | |
closeOnEscape : true, | |
destroyOnClose : false, | |
buttons : {}, | |
btnHoverFocus : true, | |
position : null, | |
width : 320, | |
height : 'auto', | |
minWidth : 200, | |
minHeight : 110 | |
} | |
/* | |
* File: /js/ui/mkdirbutton.js | |
*/ | |
/** | |
* @class elFinder toolbar button to switch mkdir mode. | |
* | |
* @author Naoki Sawada | |
**/ | |
$.fn.elfindermkdirbutton = function(cmd) { | |
return this.each(function() { | |
var button = $(this).elfinderbutton(cmd); | |
cmd.change(function() { | |
button.attr('title', cmd.value); | |
}); | |
}); | |
} | |
/* | |
* File: /js/ui/navbar.js | |
*/ | |
/** | |
* @class elfindernav - elFinder container for diretories tree and places | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfindernavbar = function(fm, opts) { | |
this.not('.elfinder-navbar').each(function() { | |
var nav = $(this).addClass('ui-state-default elfinder-navbar'), | |
parent = nav.parent() | |
.resize(function() { | |
nav.height(wz.height() - delta); | |
}), | |
wz = parent.children('.elfinder-workzone').append(nav), | |
delta = nav.outerHeight() - nav.height(), | |
ltr = fm.direction == 'ltr', | |
handle; | |
if ($.fn.resizable) { | |
handle = nav.resizable({ | |
handles : ltr ? 'e' : 'w', | |
minWidth : opts.minWidth || 150, | |
maxWidth : opts.maxWidth || 500 | |
}) | |
.on('resize scroll', function() { | |
clearTimeout($(this).data('posinit')); | |
$(this).data('posinit', setTimeout(function() { | |
var offset = (fm.UA.Opera && nav.scrollLeft())? 20 : 2; | |
handle.css({ | |
top : parseInt(nav.scrollTop())+'px', | |
left : ltr ? 'auto' : parseInt(nav.scrollLeft() + offset), | |
right: ltr ? parseInt(nav.scrollLeft() - offset) * -1 : 'auto' | |
}); | |
}, 50)); | |
}) | |
.find('.ui-resizable-handle').addClass('ui-front'); | |
if (fm.UA.Touch) { | |
var toggle = function(){ | |
if (handle.data('closed')) { | |
handle.data('closed', false).css({backgroundColor: 'transparent'}); | |
nav.css({width: handle.data('width')}).trigger('resize'); | |
} else { | |
handle.data('closed', true).css({backgroundColor: 'inherit'}); | |
nav.css({width: 8}); | |
} | |
handle.data({startX: null, endX: null}); | |
}; | |
handle.data({closed: false, width: nav.width()}) | |
.on('touchstart', function(e){ | |
handle.data('startX', e.originalEvent.touches[0].pageX); | |
}) | |
.on('touchmove', function(e){ | |
var x = e.originalEvent.touches[0].pageX; | |
var sx = handle.data('startX'); | |
var open = ltr? (sx && sx < x) : (sx > x); | |
var close = ltr? (sx > x) : (sx && sx < x); | |
(open || close) && toggle(); | |
}) | |
.on('touchend', function(e){ | |
handle.data('startX') && toggle(); | |
}); | |
if (fm.UA.Mobile) { | |
handle.data('defWidth', nav.width()); | |
$(window).on('resize', function(e){ | |
var hw = nav.parent().width() / 2; | |
if (handle.data('defWidth') > hw) { | |
nav.width(hw); | |
} else { | |
nav.width(handle.data('defWidth')); | |
} | |
handle.data('width', nav.width()); | |
}); | |
} | |
} | |
fm.one('open', function() { | |
setTimeout(function() { | |
nav.trigger('resize'); | |
}, 150); | |
}); | |
} | |
}); | |
return this; | |
}; | |
/* | |
* File: /js/ui/overlay.js | |
*/ | |
$.fn.elfinderoverlay = function(opts) { | |
this.filter(':not(.elfinder-overlay)').each(function() { | |
opts = $.extend({}, opts); | |
$(this).addClass('ui-front ui-widget-overlay elfinder-overlay') | |
.hide() | |
.mousedown(function(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
}) | |
.data({ | |
cnt : 0, | |
show : typeof(opts.show) == 'function' ? opts.show : function() { }, | |
hide : typeof(opts.hide) == 'function' ? opts.hide : function() { } | |
}); | |
}); | |
if (opts == 'show') { | |
var o = this.eq(0), | |
cnt = o.data('cnt') + 1, | |
show = o.data('show'); | |
o.data('cnt', cnt); | |
if (o.is(':hidden')) { | |
o.show(); | |
show(); | |
} | |
} | |
if (opts == 'hide') { | |
var o = this.eq(0), | |
cnt = o.data('cnt') - 1, | |
hide = o.data('hide'); | |
o.data('cnt', cnt); | |
if (cnt == 0 && o.is(':visible')) { | |
o.hide(); | |
hide(); | |
} | |
} | |
return this; | |
} | |
/* | |
* File: /js/ui/panel.js | |
*/ | |
$.fn.elfinderpanel = function(fm) { | |
return this.each(function() { | |
var panel = $(this).addClass('elfinder-panel ui-state-default ui-corner-all'), | |
margin = 'margin-'+(fm.direction == 'ltr' ? 'left' : 'right'); | |
fm.one('load', function(e) { | |
var navbar = fm.getUI('navbar'); | |
panel.css(margin, parseInt(navbar.outerWidth(true))); | |
navbar.on('resize', function() { | |
panel.is(':visible') && panel.css(margin, parseInt(navbar.outerWidth(true))) | |
}) | |
}) | |
}) | |
} | |
/* | |
* File: /js/ui/path.js | |
*/ | |
/** | |
* @class elFinder ui | |
* Display current folder path in statusbar. | |
* Click on folder name in path - open folder | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfinderpath = function(fm) { | |
return this.each(function() { | |
var query = '', | |
target = '', | |
mimes = [], | |
path = $(this).addClass('elfinder-path').html(' ') | |
.on('click', 'a', function(e) { | |
var hash = $(this).attr('href').substr(5); | |
e.preventDefault(); | |
if (hash != fm.cwd().hash) { | |
if (query) { | |
fm.exec('search', query, { target: hash, mime: mimes.join(' ') }); | |
} else { | |
fm.exec('open', hash); | |
} | |
} | |
}) | |
.prependTo(fm.getUI('statusbar').show()) | |
fm.bind('open searchend parents', function() { | |
var dirs = []; | |
query = ''; | |
target = ''; | |
mimes = []; | |
$.each(fm.parents(fm.cwd().hash), function(i, hash) { | |
dirs.push('<a href="#elf_'+hash+'">'+fm.escape(fm.file(hash).name)+'</a>'); | |
}); | |
path.html(dirs.join(fm.option('separator'))); | |
}) | |
.bind('searchstart', function(e) { | |
if (e.data) { | |
query = e.data.query || ''; | |
target = e.data.target || ''; | |
mimes = e.data.mimes || [] | |
} | |
}) | |
.bind('search', function(e) { | |
var dirs = [], | |
html = ''; | |
if (target) { | |
$.each(fm.parents(target), function(i, hash) { | |
dirs.push('<a href="#elf_'+hash+'">'+fm.escape(fm.file(hash).name)+'</a>'); | |
}); | |
html = dirs.join(fm.option('separator')); | |
} else { | |
html = fm.i18n('btnAll'); | |
} | |
path.html(fm.i18n('searcresult') + ': ' + html); | |
}); | |
}); | |
} | |
/* | |
* File: /js/ui/places.js | |
*/ | |
/** | |
* @class elFinder places/favorites ui | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfinderplaces = function(fm, opts) { | |
return this.each(function() { | |
var dirs = {}, | |
c = 'class', | |
navdir = fm.res(c, 'navdir'), | |
collapsed = fm.res(c, 'navcollapse'), | |
expanded = fm.res(c, 'navexpand'), | |
hover = fm.res(c, 'hover'), | |
clroot = fm.res(c, 'treeroot'), | |
dropover = fm.res(c, 'adroppable'), | |
tpl = fm.res('tpl', 'placedir'), | |
ptpl = fm.res('tpl', 'perms'), | |
spinner = $(fm.res('tpl', 'navspinner')), | |
key = 'places'+(opts.suffix? opts.suffix : ''), | |
menuTimer = null, | |
/** | |
* Convert places dir node into dir hash | |
* | |
* @param String directory id | |
* @return String | |
**/ | |
id2hash = function(id) { return id.substr(6); }, | |
/** | |
* Convert places dir node into dir hash | |
* | |
* @param String directory id | |
* @return String | |
**/ | |
hash2id = function(hash) { return 'place-'+hash; }, | |
/** | |
* Save current places state | |
* | |
* @return void | |
**/ | |
save = function() { | |
var hashes = [], data = []; | |
hashes = $.map(subtree.children().find('[id]'), function(n) { | |
return id2hash(n.id); | |
}); | |
$.each(hashes.reverse(), function(i, h) { | |
data.push(h + '#' + dirs[h].name); | |
}); | |
fm.storage(key, data.join(',')); | |
}, | |
/** | |
* Return node for given dir object | |
* | |
* @param Object directory object | |
* @return jQuery | |
**/ | |
create = function(dir, hash) { | |
return $(tpl.replace(/\{id\}/, hash2id(dir? dir.hash : hash)) | |
.replace(/\{name\}/, fm.escape(dir? dir.name : hash)) | |
.replace(/\{cssclass\}/, dir? (fm.UA.Touch ? 'elfinder-touch ' : '')+fm.perms2class(dir) : '') | |
.replace(/\{permissions\}/, (dir && (!dir.read || !dir.write))? ptpl : '') | |
.replace(/\{title\}/, (dir && dir.path)? fm.escape(dir.path) : '') | |
.replace(/\{symlink\}/, '') | |
.replace(/\{style\}/, '')); | |
}, | |
/** | |
* Add new node into places | |
* | |
* @param Object directory object | |
* @return void | |
**/ | |
add = function(dir) { | |
var node, hash; | |
if (dir.mime !== 'directory') { | |
return false; | |
} | |
hash = dir.hash; | |
if (!fm.files().hasOwnProperty(hash)) { | |
// update cache | |
fm.trigger('tree', {tree: [dir]}); | |
} | |
node = create(dir, hash); | |
if (dir.notfound) { | |
node.addClass('ui-state-disabled'); | |
} | |
dirs[hash] = dir; | |
subtree.prepend(node); | |
root.addClass(collapsed); | |
return true; | |
}, | |
/** | |
* Remove dir from places | |
* | |
* @param String directory hash | |
* @return String removed name | |
**/ | |
remove = function(hash) { | |
var name = null, tgt; | |
if (dirs[hash]) { | |
delete dirs[hash]; | |
tgt = $('#'+hash2id(hash)); | |
if (tgt.length) { | |
name = tgt.text(); | |
tgt.parent().remove(); | |
if (!subtree.children().length) { | |
root.removeClass(collapsed); | |
places.removeClass(expanded); | |
subtree.slideToggle(false); | |
} | |
} | |
} | |
return name; | |
}, | |
/** | |
* Move up dir on places | |
* | |
* @param String directory hash | |
* @return void | |
**/ | |
moveup = function(hash) { | |
var self = $('#'+hash2id(hash)), | |
tgt = self.parent(), | |
prev = tgt.prev('div'), | |
cls = 'ui-state-hover', | |
ctm = fm.getUI('contextmenu'); | |
menuTimer && clearTimeout(menuTimer); | |
if (prev.length) { | |
ctm.find(':first').data('placesHash', hash); | |
self.addClass(cls); | |
tgt.insertBefore(prev); | |
prev = tgt.prev('div'); | |
menuTimer = setTimeout(function() { | |
self.removeClass(cls); | |
if (ctm.find(':first').data('placesHash') === hash) { | |
ctm.hide().empty(); | |
} | |
}, 1500); | |
} | |
if(!prev.length) { | |
self.removeClass(cls); | |
ctm.hide().empty(); | |
} | |
}, | |
/** | |
* Update dir at places | |
* | |
* @param Object directory | |
* @param String previous hash | |
* @return Boolean | |
**/ | |
update = function(dir, preHash) { | |
var hash = dir.hash, | |
tgt = $('#'+hash2id(preHash || hash)), | |
node = create(dir, hash); | |
if (tgt.length > 0) { | |
if (dir.notfound) { | |
node.addClass('ui-state-disabled'); | |
} | |
tgt.parent().replaceWith(node); | |
dirs[hash] = dir; | |
return true | |
} else { | |
return false; | |
} | |
}, | |
/** | |
* Remove all dir from places | |
* | |
* @return void | |
**/ | |
clear = function() { | |
subtree.empty(); | |
root.removeClass(collapsed); | |
places.removeClass(expanded); | |
subtree.slideToggle(false); | |
}, | |
/** | |
* Sort places dirs A-Z | |
* | |
* @return void | |
**/ | |
sort = function() { | |
$.each(dirs, function(h, f) { | |
var dir = fm.file(h) || f, | |
node = create(dir, h), | |
ret = null; | |
if (!dir) { | |
node.hide(); | |
} | |
if (subtree.children().length) { | |
$.each(subtree.children(), function() { | |
var current = $(this); | |
if (dir.name.localeCompare(current.children('.'+navdir).text()) < 0) { | |
ret = !node.insertBefore(current); | |
return ret; | |
} | |
}); | |
if (ret !== null) { | |
return true; | |
} | |
} | |
!$('#'+hash2id(h)).length && subtree.append(node); | |
}); | |
save(); | |
}, | |
/** | |
* Node - wrapper for places root | |
* | |
* @type jQuery | |
**/ | |
wrapper = create({ | |
hash : 'root-'+fm.namespace, | |
name : fm.i18n(opts.name, 'places'), | |
read : true, | |
write : true | |
}), | |
/** | |
* Places root node | |
* | |
* @type jQuery | |
**/ | |
root = wrapper.children('.'+navdir) | |
.addClass(clroot) | |
.click(function(e) { | |
e.stopPropagation(); | |
if (root.hasClass(collapsed)) { | |
places.toggleClass(expanded); | |
subtree.slideToggle(); | |
fm.storage('placesState', places.hasClass(expanded)? 1 : 0); | |
} | |
}) | |
.append( | |
// sort button | |
$('<span class="elfinder-button-icon elfinder-button-icon-sort elfinder-places-root-icon" title="'+fm.i18n('cmdsort')+'"/>') | |
.on('click', function(e) { | |
e.stopPropagation(); | |
subtree.empty(); | |
sort(); | |
} | |
) | |
), | |
/** | |
* Container for dirs | |
* | |
* @type jQuery | |
**/ | |
subtree = wrapper.children('.'+fm.res(c, 'navsubtree')) | |
.sortable({ | |
appendTo : 'body', | |
revert : false, | |
helper : function(e) { | |
var dir = $(e.target).parent(); | |
dir.children().removeClass('ui-state-hover'); | |
return $('<div class="ui-widget elfinder-place-drag elfinder-'+fm.direction+'"/>') | |
.append($('<div class="elfinder-navbar"/>').show().append(dir.clone())); | |
}, | |
stop : function(e, ui) { | |
var target = $(ui.item[0]), | |
top = places.offset().top, | |
left = places.offset().left, | |
width = places.width(), | |
height = places.height(), | |
x = e.pageX, | |
y = e.pageY; | |
if (!(x > left && x < left+width && y > top && y < y+height)) { | |
remove(id2hash(target.children(':first').attr('id'))); | |
save(); | |
} | |
}, | |
update : function(e, ui) { | |
save(); | |
} | |
}), | |
/** | |
* Main places container | |
* | |
* @type jQuery | |
**/ | |
places = $(this).addClass(fm.res(c, 'tree')+' elfinder-places ui-corner-all') | |
.hide() | |
.append(wrapper) | |
.appendTo(fm.getUI('navbar')) | |
.on('mouseenter mouseleave', '.'+navdir, function(e) { | |
$(this).toggleClass('ui-state-hover', (e.type == 'mouseenter')); | |
}) | |
.on('click', '.'+navdir, function(e) { | |
var p = $(this); | |
if (p.data('longtap')) { | |
e.stopPropagation(); | |
return; | |
} | |
fm.exec('open', p.attr('id').substr(6)); | |
}) | |
.on('contextmenu', '.'+navdir+':not(.'+clroot+')', function(e) { | |
var self = $(this), | |
hash = self.attr('id').substr(6); | |
e.preventDefault(); | |
fm.trigger('contextmenu', { | |
raw : [{ | |
label : fm.i18n('moveUp'), | |
icon : 'up', | |
remain : true, | |
callback : function() { moveup(hash); save(); } | |
},'|',{ | |
label : fm.i18n('rmFromPlaces'), | |
icon : 'rm', | |
callback : function() { remove(hash); save(); } | |
}], | |
'x' : e.pageX, | |
'y' : e.pageY | |
}); | |
self.addClass('ui-state-hover'); | |
fm.getUI('contextmenu').children().on('mouseenter', function() { | |
self.addClass('ui-state-hover'); | |
}); | |
fm.bind('closecontextmenu', function() { | |
self.removeClass('ui-state-hover'); | |
}); | |
}) | |
.droppable({ | |
tolerance : 'pointer', | |
accept : '.elfinder-cwd-file-wrapper,.elfinder-tree-dir,.elfinder-cwd-file', | |
hoverClass : fm.res('class', 'adroppable'), | |
classes : { // Deprecated hoverClass jQueryUI>=1.12.0 | |
'ui-droppable-hover': fm.res('class', 'adroppable') | |
}, | |
over : function(e, ui) { | |
var helper = ui.helper, | |
dir = $.map(helper.data('files'), function(h) { return (fm.file(h).mime === 'directory' && !dirs[h])? h : null}); | |
e.stopPropagation(); | |
helper.data('dropover', helper.data('dropover') + 1); | |
if (dir.length > 0) { | |
helper.addClass('elfinder-drag-helper-plus'); | |
} else { | |
$(this).removeClass(dropover); | |
} | |
fm.trigger('unlockfiles', {files : helper.data('files'), helper: helper}); | |
}, | |
out : function(e, ui) { | |
var helper = ui.helper; | |
e.stopPropagation(); | |
helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0)); | |
$(this).removeData('dropover') | |
.removeClass(dropover); | |
fm.trigger('unlockfiles', {files : helper.data('files'), helper: helper}); | |
}, | |
drop : function(e, ui) { | |
var helper = ui.helper, | |
resolve = true; | |
$.each(helper.data('files'), function(i, hash) { | |
var dir = fm.file(hash); | |
if (dir && dir.mime == 'directory' && !dirs[dir.hash]) { | |
add(dir); | |
} else { | |
resolve = false; | |
} | |
}) | |
save(); | |
resolve && helper.hide(); | |
} | |
}) | |
// for touch device | |
.on('touchstart', '.'+navdir+':not(.'+clroot+')', function(e) { | |
var hash = $(this).attr('id').substr(6), | |
p = $(this) | |
.addClass(hover) | |
.data('longtap', null) | |
.data('tmlongtap', setTimeout(function(){ | |
// long tap | |
p.data('longtap', true); | |
fm.trigger('contextmenu', { | |
raw : [{ | |
label : fm.i18n('rmFromPlaces'), | |
icon : 'rm', | |
callback : function() { remove(hash); save(); } | |
}], | |
'x' : e.originalEvent.touches[0].pageX, | |
'y' : e.originalEvent.touches[0].pageY | |
}); | |
}, 500)); | |
}) | |
.on('touchmove touchend', '.'+navdir+':not(.'+clroot+')', function(e) { | |
clearTimeout($(this).data('tmlongtap')); | |
if (e.type == 'touchmove') { | |
$(this).removeClass(hover); | |
} | |
}); | |
// "on regist" for command exec | |
$(this).on('regist', function(e, files){ | |
var added = false; | |
$.each(files, function(i, dir) { | |
if (dir && dir.mime == 'directory' && !dirs[dir.hash]) { | |
if (add(dir)) { | |
added = true; | |
} | |
} | |
}); | |
added && save(); | |
}); | |
// on fm load - show places and load files from backend | |
fm.one('load', function() { | |
var dat, hashes; | |
if (fm.oldAPI) { | |
return; | |
} | |
places.show().parent().show(); | |
dirs = {}; | |
dat = $.map((fm.storage(key) || '').split(','), function(hash) { return hash || null;}); | |
$.each(dat, function(i, d) { | |
var dir = d.split('#') | |
dirs[dir[0]] = dir[1]? dir[1] : dir[0]; | |
}); | |
// allow modify `dirs` | |
/** | |
* example for preset places | |
* | |
* elfinderInstance.bind('placesload', function(e, fm) { | |
* //if (fm.storage(e.data.storageKey) === null) { // for first time only | |
* if (!fm.storage(e.data.storageKey)) { // for empty places | |
* e.data.dirs[targetHash] = fallbackName; // preset folder | |
* } | |
* } | |
**/ | |
fm.trigger('placesload', {dirs: dirs, storageKey: key}, true); | |
hashes = Object.keys(dirs); | |
if (hashes.length) { | |
root.prepend(spinner); | |
fm.request({ | |
data : {cmd : 'info', targets : hashes}, | |
preventDefault : true | |
}) | |
.done(function(data) { | |
var exists = {}; | |
$.each(data.files, function(i, f) { | |
var hash = f.hash; | |
exists[hash] = f; | |
}); | |
$.each(dirs, function(h, f) { | |
add(exists[h] || { hash: h, name: f, mime: 'directory', notfound: true }); | |
}); | |
if (fm.storage('placesState') > 0) { | |
root.click(); | |
} | |
}) | |
.always(function() { | |
spinner.remove(); | |
}) | |
} | |
fm.change(function(e) { | |
var changed = false; | |
$.each(e.data.changed, function(i, file) { | |
if (dirs[file.hash]) { | |
if (file.mime !== 'directory') { | |
if (remove(file.hash)) { | |
changed = true; | |
} | |
} else { | |
if (update(file)) { | |
changed = true; | |
} | |
} | |
} | |
}); | |
changed && save(); | |
}) | |
.bind('rename', function(e) { | |
var changed = false; | |
if (e.data.removed) { | |
$.each(e.data.removed, function(i, hash) { | |
if (e.data.added[i]) { | |
if (update(e.data.added[i], hash)) { | |
changed = true; | |
} | |
} | |
}); | |
} | |
changed && save(); | |
}) | |
.bind('rm paste', function(e) { | |
var names = [], | |
changed = false; | |
if (e.data.removed) { | |
$.each(e.data.removed, function(i, hash) { | |
var name = remove(hash); | |
name && names.push(name); | |
}); | |
} | |
if (names.length) { | |
changed = true; | |
} | |
if (e.data.added && names.length) { | |
$.each(e.data.added, function(i, file) { | |
if ($.inArray(file.name, names) !== 1) { | |
file.mime == 'directory' && add(file); | |
} | |
}); | |
} | |
changed && save(); | |
}) | |
.bind('sync', function() { | |
var hashes = Object.keys(dirs); | |
if (hashes.length) { | |
root.prepend(spinner); | |
fm.request({ | |
data : {cmd : 'info', targets : hashes}, | |
preventDefault : true | |
}) | |
.done(function(data) { | |
var exists = {}, | |
updated = false, | |
cwd = fm.cwd().hash; | |
$.each(data.files || [], function(i, file) { | |
var hash = file.hash; | |
exists[hash] = file; | |
if (!fm.files().hasOwnProperty(file.hash)) { | |
// update cache | |
fm.trigger('tree', {tree: [file]}); | |
} | |
}); | |
$.each(dirs, function(h, f) { | |
if (!f.notfound != !!exists[h]) { | |
if (f.phash === cwd || (exists[h] && exists[h].mime !== 'directory')) { | |
if (remove(h)) { | |
updated = true; | |
} | |
} else { | |
if (update(exists[h] || { hash: h, name: f.name, mime: 'directory', notfound: true })) { | |
updated = true; | |
} | |
} | |
} else if (exists[h] && exists[h].phash != cwd) { | |
// update permission of except cwd | |
update(exists[h]); | |
} | |
}); | |
updated && save(); | |
}) | |
.always(function() { | |
spinner.remove(); | |
}); | |
} | |
}) | |
}) | |
}); | |
} | |
/* | |
* File: /js/ui/searchbutton.js | |
*/ | |
/** | |
* @class elFinder toolbar search button widget. | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfindersearchbutton = function(cmd) { | |
return this.each(function() { | |
var result = false, | |
fm = cmd.fm, | |
id = function(name){return fm.namespace + name}, | |
toolbar= fm.getUI('toolbar'), | |
btnCls = fm.res('class', 'searchbtn'), | |
button = $(this).hide().addClass('ui-widget-content elfinder-button '+btnCls), | |
search = function() { | |
opts && opts.slideUp(); | |
var val = $.trim(input.val()), | |
from = !$('#' + id('SearchFromAll')).prop('checked'), | |
mime = $('#' + id('SearchMime')).prop('checked'); | |
if (from) { | |
if ($('#' + id('SearchFromVol')).prop('checked')) { | |
from = fm.root(fm.cwd().hash); | |
} else { | |
from = fm.cwd().hash; | |
} | |
} | |
if (mime) { | |
mime = val; | |
val = '.'; | |
} | |
if (val) { | |
cmd.exec(val, from, mime).done(function() { | |
result = true; | |
input.focus(); | |
}).fail(function() { | |
abort(); | |
}); | |
} else { | |
fm.trigger('searchend'); | |
} | |
}, | |
abort = function() { | |
opts && opts.slideUp(); | |
input.val(''); | |
if (result) { | |
result = false; | |
fm.trigger('searchend'); | |
} | |
}, | |
input = $('<input type="text" size="42"/>') | |
.focus(function(){ | |
opts && opts.slideDown(); | |
}) | |
.blur(function(){ | |
if (opts) { | |
if (!opts.data('infocus')) { | |
opts.slideUp(); | |
} else { | |
opts.data('infocus', false); | |
} | |
} | |
}) | |
.appendTo(button) | |
// to avoid fm shortcuts on arrows | |
.keypress(function(e) { | |
e.stopPropagation(); | |
}) | |
.keydown(function(e) { | |
e.stopPropagation(); | |
e.keyCode == 13 && search(); | |
if (e.keyCode== 27) { | |
e.preventDefault(); | |
abort(); | |
} | |
}), | |
opts = (fm.api < 2.1)? null : $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>') | |
.append( | |
$('<div class="buttonset"/>') | |
.append( | |
$('<input id="'+id('SearchFromCwd')+'" name="serchfrom" type="radio" checked="checked"/><label for="'+id('SearchFromCwd')+'">'+fm.i18n('btnCwd')+'</label>'), | |
$('<input id="'+id('SearchFromVol')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromVol')+'">'+fm.i18n('btnVolume')+'</label>'), | |
$('<input id="'+id('SearchFromAll')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromAll')+'">'+fm.i18n('btnAll')+'</label>') | |
), | |
$('<div class="buttonset"/>') | |
.append( | |
$('<input id="'+id('SearchName')+'" name="serchcol" type="radio" checked="checked"/><label for="'+id('SearchName')+'">'+fm.i18n('btnFileName')+'</label>'), | |
$('<input id="'+id('SearchMime')+'" name="serchcol" type="radio"/><label for="'+id('SearchMime')+'">'+fm.i18n('btnMime')+'</label>') | |
) | |
) | |
.hide() | |
.css('overflow', 'hidden') | |
.appendTo(button); | |
$('<span class="ui-icon ui-icon-search" title="'+cmd.title+'"/>') | |
.appendTo(button) | |
.click(search); | |
$('<span class="ui-icon ui-icon-close"/>') | |
.appendTo(button) | |
.click(abort); | |
$(function(){ | |
if (!opts) { | |
return; | |
} | |
opts.find('div.buttonset').buttonset(); | |
$('#'+id('SearchFromAll')).next('label').attr('title', fm.i18n('searchTarget', fm.i18n('btnAll'))); | |
$('#'+id('SearchMime')).next('label').attr('title', fm.i18n('searchMime')); | |
opts.find('input') | |
.on('mousedown', function(){ | |
opts.data('infocus', true); | |
}) | |
.on('click', function(){ | |
$.trim(input.val()) && search(); | |
}); | |
}); | |
// wait when button will be added to DOM | |
toolbar.on('load', function(){ | |
var parent = button.parent(); | |
if (parent.length) { | |
toolbar.children('.'+btnCls).remove(); | |
toolbar.prepend(button.show()); | |
parent.remove(); | |
// position icons for ie7 | |
if (fm.UA.ltIE7) { | |
var icon = button.children(fm.direction == 'ltr' ? '.ui-icon-close' : '.ui-icon-search'); | |
icon.css({ | |
right : '', | |
left : parseInt(button.width())-icon.outerWidth(true) | |
}); | |
} | |
fm.resize(); | |
} | |
}); | |
fm | |
.select(function() { | |
input.blur(); | |
}) | |
.bind('searchend', function() { | |
input.val(''); | |
}) | |
.bind('open parents', function() { | |
var dirs = [], | |
volroot = fm.file(fm.root(fm.cwd().hash)); | |
if (volroot) { | |
$.each(fm.parents(fm.cwd().hash), function(i, hash) { | |
dirs.push(fm.file(hash).name); | |
}); | |
$('#'+id('SearchFromCwd')).next('label').attr('title', fm.i18n('searchTarget', dirs.join(fm.option('separator')))); | |
$('#'+id('SearchFromVol')).next('label').attr('title', fm.i18n('searchTarget', volroot.name)); | |
} | |
}) | |
.shortcut({ | |
pattern : 'ctrl+f f3', | |
description : cmd.title, | |
callback : function() { input.select().focus(); } | |
}); | |
}); | |
}; | |
/* | |
* File: /js/ui/sortbutton.js | |
*/ | |
/** | |
* @class elFinder toolbar button menu with sort variants. | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfindersortbutton = function(cmd) { | |
return this.each(function() { | |
var fm = cmd.fm, | |
name = cmd.name, | |
c = 'class', | |
disabled = fm.res(c, 'disabled'), | |
hover = fm.res(c, 'hover'), | |
item = 'elfinder-button-menu-item', | |
selected = item+'-selected', | |
asc = selected+'-asc', | |
desc = selected+'-desc', | |
button = $(this).addClass('ui-state-default elfinder-button elfinder-menubutton elfiner-button-'+name) | |
.attr('title', cmd.title) | |
.append('<span class="elfinder-button-icon elfinder-button-icon-'+name+'"/>') | |
.hover(function(e) { !button.hasClass(disabled) && button.toggleClass(hover); }) | |
.click(function(e) { | |
if (!button.hasClass(disabled)) { | |
e.stopPropagation(); | |
menu.is(':hidden') && cmd.fm.getUI().click(); | |
menu.slideToggle(100); | |
} | |
}), | |
menu = $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>') | |
.hide() | |
.appendTo(button) | |
.on('mouseenter mouseleave', '.'+item, function() { $(this).toggleClass(hover) }) | |
.on('click', '.'+item, function(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
hide(); | |
}), | |
update = function() { | |
menu.children(':not(:last)').removeClass(selected+' '+asc+' '+desc) | |
.filter('[rel="'+fm.sortType+'"]') | |
.addClass(selected+' '+(fm.sortOrder == 'asc' ? asc : desc)); | |
menu.children(':last').toggleClass(selected, fm.sortStickFolders); | |
}, | |
hide = function() { menu.hide(); }; | |
$.each(fm.sortRules, function(name, value) { | |
menu.append($('<div class="'+item+'" rel="'+name+'"><span class="ui-icon ui-icon-arrowthick-1-n"/><span class="ui-icon ui-icon-arrowthick-1-s"/>'+fm.i18n('sort'+name)+'</div>').data('type', name)); | |
}); | |
menu.children().click(function(e) { | |
var type = $(this).attr('rel'); | |
cmd.exec([], { | |
type : type, | |
order : type == fm.sortType ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder, | |
stick : fm.sortStickFolders | |
}); | |
}) | |
$('<div class="'+item+' '+item+'-separated"><span class="ui-icon ui-icon-check"/>'+fm.i18n('sortFoldersFirst')+'</div>') | |
.appendTo(menu) | |
.click(function() { | |
cmd.exec([], {type : fm.sortType, order : fm.sortOrder, stick : !fm.sortStickFolders}); | |
}); | |
fm.bind('disable select', hide).getUI().click(hide); | |
fm.bind('sortchange', update) | |
if (menu.children().length > 1) { | |
cmd.change(function() { | |
button.toggleClass(disabled, cmd.disabled()); | |
update(); | |
}) | |
.change(); | |
} else { | |
button.addClass(disabled); | |
} | |
}); | |
} | |
/* | |
* File: /js/ui/stat.js | |
*/ | |
/** | |
* @class elFinder ui | |
* Display number of files/selected files and its size in statusbar | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfinderstat = function(fm) { | |
return this.each(function() { | |
var size = $(this).addClass('elfinder-stat-size'), | |
sel = $('<div class="elfinder-stat-selected"/>') | |
.on('click', 'a', function(e) { | |
var hash = $(this).data('hash'); | |
e.preventDefault(); | |
fm.exec('opendir', [ hash ]); | |
}), | |
titlesize = fm.i18n('size'), | |
titleitems = fm.i18n('items'), | |
titlesel = fm.i18n('selected'), | |
setstat = function(files, cwd) { | |
var c = 0, | |
s = 0; | |
$.each(files, function(i, file) { | |
if (!cwd || file.phash == cwd) { | |
c++; | |
s += parseInt(file.size)||0; | |
} | |
}) | |
size.html(titleitems+': '+c+', '+titlesize+': '+fm.formatSize(s)); | |
}, | |
search = false; | |
fm.getUI('statusbar').prepend(size).append(sel).show(); | |
fm | |
.bind('open reload add remove change searchend', function() { | |
setstat(fm.files(), fm.cwd().hash); | |
}) | |
.bind('searchend', function() { | |
search = false; | |
}) | |
.search(function(e) { | |
search = true; | |
setstat(e.data.files); | |
}) | |
.select(function() { | |
var s = 0, | |
c = 0, | |
files = fm.selectedFiles(), | |
dirs = [], | |
file; | |
if (files.length == 1) { | |
file = files[0]; | |
s = file.size; | |
if (search) { | |
dirs.push('<a href="#elf_'+file.phash+'" data-hash="'+file.hash+'">'+(file.path? file.path.replace(/\/[^\/]*$/, '') : '..')+'</a>'); | |
} | |
dirs.push(fm.escape(file.name)); | |
sel.html(dirs.join('/') + (s > 0 ? ', '+fm.formatSize(s) : '')); | |
return; | |
} | |
$.each(files, function(i, file) { | |
c++; | |
s += parseInt(file.size)||0; | |
}); | |
sel.html(c ? titlesel+': '+c+', '+titlesize+': '+fm.formatSize(s) : ' '); | |
}) | |
; | |
}) | |
} | |
/* | |
* File: /js/ui/toolbar.js | |
*/ | |
/** | |
* @class elFinder toolbar | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfindertoolbar = function(fm, opts) { | |
this.not('.elfinder-toolbar').each(function() { | |
var commands = fm._commands, | |
self = $(this).addClass('ui-helper-clearfix ui-widget-header ui-corner-top elfinder-toolbar'), | |
panels = opts || [], | |
dispre = null, | |
uiCmdMapPrev = '', | |
l, i, cmd, panel, button; | |
self.prev().length && self.parent().prepend(this); | |
var render = function(disabled){ | |
var name; | |
self.empty(); | |
l = panels.length; | |
while (l--) { | |
if (panels[l]) { | |
panel = $('<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>'); | |
i = panels[l].length; | |
while (i--) { | |
name = panels[l][i]; | |
if ((!disabled || $.inArray(name, disabled) === -1) && (cmd = commands[name])) { | |
button = 'elfinder'+cmd.options.ui; | |
$.fn[button] && panel.prepend($('<div/>')[button](cmd)); | |
} | |
} | |
panel.children().length && self.prepend(panel); | |
panel.children(':gt(0)').before('<span class="ui-widget-content elfinder-toolbar-button-separator"/>'); | |
} | |
} | |
self.children().length? self.show() : self.hide(); | |
self.trigger('load'); | |
}; | |
render(); | |
fm.bind('open sync', function(){ | |
var repCmds = [], | |
disabled = fm.option('disabled'); | |
if (!dispre || dispre.toString() !== disabled.sort().toString()) { | |
render(disabled && disabled.length? disabled : null); | |
} | |
dispre = disabled.concat().sort(); | |
if (uiCmdMapPrev !== JSON.stringify(fm.commandMap)) { | |
uiCmdMapPrev = JSON.stringify(fm.commandMap); | |
if (Object.keys(fm.commandMap).length) { | |
$.each(fm.commandMap, function(from, to){ | |
var cmd = fm._commands[to], | |
button = cmd? 'elfinder'+cmd.options.ui : null; | |
if (button && $.fn[button]) { | |
repCmds.push(from); | |
var btn = $('div.elfinder-buttonset div.elfinder-button').has('span.elfinder-button-icon-'+from); | |
if (btn.length && !btn.next().has('span.elfinder-button-icon-'+to).length) { | |
btn.after($('<div/>')[button](fm._commands[to]).data('origin', from)); | |
btn.hide(); | |
} | |
} | |
}); | |
} | |
// reset toolbar | |
$.each($('div.elfinder-button'), function(){ | |
var origin = $(this).data('origin'); | |
if (origin && $.inArray(origin, repCmds) == -1) { | |
$('span.elfinder-button-icon-'+$(this).data('origin')).parent().show(); | |
$(this).remove(); | |
} | |
}); | |
} | |
}); | |
}); | |
return this; | |
} | |
/* | |
* File: /js/ui/tree.js | |
*/ | |
/** | |
* @class elFinder folders tree | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfindertree = function(fm, opts) { | |
var treeclass = fm.res('class', 'tree'); | |
this.not('.'+treeclass).each(function() { | |
var c = 'class', mobile = fm.UA.Mobile, | |
/** | |
* Root directory class name | |
* | |
* @type String | |
*/ | |
root = fm.res(c, 'treeroot'), | |
/** | |
* Open root dir if not opened yet | |
* | |
* @type Boolean | |
*/ | |
openRoot = opts.openRootOnLoad, | |
/** | |
* Open current work dir if not opened yet | |
* | |
* @type Boolean | |
*/ | |
openCwd = opts.openCwdOnOpen, | |
/** | |
* Subtree class name | |
* | |
* @type String | |
*/ | |
subtree = fm.res(c, 'navsubtree'), | |
/** | |
* Directory class name | |
* | |
* @type String | |
*/ | |
navdir = fm.res(c, 'treedir'), | |
/** | |
* Directory CSS selector | |
* | |
* @type String | |
*/ | |
selNavdir = 'span.' + navdir, | |
/** | |
* Collapsed arrow class name | |
* | |
* @type String | |
*/ | |
collapsed = fm.res(c, 'navcollapse'), | |
/** | |
* Expanded arrow class name | |
* | |
* @type String | |
*/ | |
expanded = fm.res(c, 'navexpand'), | |
/** | |
* Class name to mark arrow for directory with already loaded children | |
* | |
* @type String | |
*/ | |
loaded = 'elfinder-subtree-loaded', | |
/** | |
* Arraw class name | |
* | |
* @type String | |
*/ | |
arrow = fm.res(c, 'navarrow'), | |
/** | |
* Current directory class name | |
* | |
* @type String | |
*/ | |
active = fm.res(c, 'active'), | |
/** | |
* Droppable dirs dropover class | |
* | |
* @type String | |
*/ | |
dropover = fm.res(c, 'adroppable'), | |
/** | |
* Hover class name | |
* | |
* @type String | |
*/ | |
hover = fm.res(c, 'hover'), | |
/** | |
* Disabled dir class name | |
* | |
* @type String | |
*/ | |
disabled = fm.res(c, 'disabled'), | |
/** | |
* Draggable dir class name | |
* | |
* @type String | |
*/ | |
draggable = fm.res(c, 'draggable'), | |
/** | |
* Droppable dir class name | |
* | |
* @type String | |
*/ | |
droppable = fm.res(c, 'droppable'), | |
/** | |
* Un-disabled cmd `paste` volume's root wrapper class | |
* | |
* @type String | |
*/ | |
pastable = 'elfinder-navbar-wrapper-pastable', | |
/** | |
* Un-disabled cmd `upload` volume's root wrapper class | |
* | |
* @type String | |
*/ | |
uploadable = 'elfinder-navbar-wrapper-uploadable', | |
insideNavbar = function(x) { | |
var left = navbar.offset().left; | |
return left <= x && x <= left + navbar.width(); | |
}, | |
drop = fm.droppable.drop, | |
/** | |
* Droppable options | |
* | |
* @type Object | |
*/ | |
droppableopts = $.extend(true, {}, fm.droppable, { | |
// show subfolders on dropover | |
over : function(e, ui) { | |
var dst = $(this), | |
helper = ui.helper, | |
cl = hover+' '+dropover, | |
hash, status; | |
e.stopPropagation(); | |
helper.data('dropover', helper.data('dropover') + 1); | |
dst.data('dropover', true); | |
if (ui.helper.data('namespace') !== fm.namespace) { | |
dst.removeClass(cl); | |
return false; | |
} | |
dst.addClass(hover) | |
if (dst.is('.'+collapsed+':not(.'+expanded+')')) { | |
dst.data('expandTimer', setTimeout(function() { | |
dst.children('.'+arrow).click(); | |
}, 500)); | |
} | |
hash = fm.navId2Hash(dst.attr('id')); | |
dst.data('dropover', hash); | |
$.each(ui.helper.data('files'), function(i, h) { | |
if (h === hash || (fm.file(h).phash === hash && !ui.helper.hasClass('elfinder-drag-helper-plus'))) { | |
dst.removeClass(cl); | |
return false; // break $.each | |
} | |
}); | |
if (helper.data('locked')) { | |
status = 'elfinder-drag-helper-plus'; | |
} else { | |
status = 'elfinder-drag-helper-move'; | |
if (e.shiftKey || e.ctrlKey || e.metaKey) { | |
status += ' elfinder-drag-helper-plus'; | |
} | |
} | |
dst.hasClass(dropover) && helper.addClass(status); | |
setTimeout(function(){ dst.hasClass(dropover) && helper.addClass(status); }, 20); | |
}, | |
out : function(e, ui) { | |
var dst = $(this), | |
helper = ui.helper; | |
e.stopPropagation(); | |
helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0)); | |
dst.data('expandTimer') && clearTimeout(dst.data('expandTimer')); | |
dst.removeData('dropover') | |
.removeClass(hover+' '+dropover); | |
}, | |
deactivate : function() { | |
$(this).removeData('dropover') | |
.removeClass(hover+' '+dropover); | |
}, | |
drop : function(e, ui) { insideNavbar(e.clientX) && drop.call(this, e, ui); } | |
}), | |
spinner = $(fm.res('tpl', 'navspinner')), | |
/** | |
* Directory html template | |
* | |
* @type String | |
*/ | |
tpl = fm.res('tpl', 'navdir'), | |
/** | |
* Permissions marker html template | |
* | |
* @type String | |
*/ | |
ptpl = fm.res('tpl', 'perms'), | |
/** | |
* Lock marker html template | |
* | |
* @type String | |
*/ | |
ltpl = fm.res('tpl', 'lock'), | |
/** | |
* Symlink marker html template | |
* | |
* @type String | |
*/ | |
stpl = fm.res('tpl', 'symlink'), | |
/** | |
* Html template replacement methods | |
* | |
* @type Object | |
*/ | |
replace = { | |
id : function(dir) { return fm.navHash2Id(dir.hash) }, | |
cssclass : function(dir) { | |
var cname = (fm.UA.Touch ? 'elfinder-touch ' : '')+(dir.phash ? '' : root)+' '+navdir+' '+fm.perms2class(dir); | |
dir.dirs && !dir.link && (cname += ' ' + collapsed); | |
opts.getClass && (cname += ' ' + opts.getClass(dir)); | |
dir.csscls && (cname += ' ' + fm.escape(dir.csscls)); | |
return cname; | |
}, | |
permissions : function(dir) { return !dir.read || !dir.write ? ptpl : ''; }, | |
symlink : function(dir) { return dir.alias ? stpl : ''; }, | |
style : function(dir) { return dir.icon ? 'style="background-image:url(\''+fm.escape(dir.icon)+'\')"' : ''; } | |
}, | |
/** | |
* Return html for given dir | |
* | |
* @param Object directory | |
* @return String | |
*/ | |
itemhtml = function(dir) { | |
dir.name = fm.escape(dir.i18 || dir.name); | |
return tpl.replace(/(?:\{([a-z]+)\})/ig, function(m, key) { | |
return dir[key] || (replace[key] ? replace[key](dir) : ''); | |
}); | |
}, | |
/** | |
* Return only dirs from files list | |
* | |
* @param Array files list | |
* @return Array | |
*/ | |
filter = function(files) { | |
return $.map(files||[], function(f) { return f.mime == 'directory' ? f : null }); | |
}, | |
/** | |
* Find parent subtree for required directory | |
* | |
* @param String dir hash | |
* @return jQuery | |
*/ | |
findSubtree = function(hash) { | |
return hash ? $('#'+fm.navHash2Id(hash)).next('.'+subtree) : tree; | |
}, | |
/** | |
* Find directory (wrapper) in required node | |
* before which we can insert new directory | |
* | |
* @param jQuery parent directory | |
* @param Object new directory | |
* @return jQuery | |
*/ | |
findSibling = function(subtree, dir) { | |
var node = subtree.children(':first'), | |
info, compare; | |
compare = fm.naturalCompare; | |
while (node.length) { | |
info = fm.file(fm.navId2Hash(node.children('[id]').attr('id'))); | |
if ((info = fm.file(fm.navId2Hash(node.children('[id]').attr('id')))) | |
&& compare(dir.name, info.name) < 0) { | |
return node; | |
} | |
node = node.next(); | |
} | |
return $(''); | |
}, | |
/** | |
* Add new dirs in tree | |
* | |
* @param Array dirs list | |
* @return void | |
*/ | |
updateTree = function(dirs) { | |
var length = dirs.length, | |
orphans = [], | |
i = dirs.length, | |
dir, html, parent, sibling, init, atonce = {}, base; | |
var firstVol = true; // check for netmount volume | |
while (i--) { | |
dir = dirs[i]; | |
if ($('#'+fm.navHash2Id(dir.hash)).length) { | |
continue; | |
} | |
if ((parent = findSubtree(dir.phash)).length) { | |
if (dir.phash && ((init = !parent.children().length) || (sibling = findSibling(parent, dir)).length)) { | |
if (init) { | |
if (!atonce[dir.phash]) { | |
atonce[dir.phash] = []; | |
} | |
atonce[dir.phash].push(dir); | |
} else { | |
sibling.before(itemhtml(dir)); | |
} | |
} else { | |
parent[firstVol || dir.phash ? 'append' : 'prepend'](itemhtml(dir)); | |
firstVol = false; | |
if (!dir.phash) { | |
base = $('#'+fm.navHash2Id(dir.hash)).parent(); | |
if (!dir.disabled || dir.disabled.length < 1) { | |
base.addClass(pastable+' '+uploadable); | |
} else { | |
if ($.inArray('paste', dir.disabled) === -1) { | |
base.addClass(pastable); | |
} | |
if ($.inArray('upload', dir.disabled) === -1) { | |
base.addClass(uploadable); | |
} | |
} | |
} | |
} | |
} else { | |
orphans.push(dir); | |
} | |
} | |
// When init, html append at once | |
if (Object.keys(atonce).length){ | |
$.each(atonce, function(p, dirs){ | |
var parent = findSubtree(p), | |
html = []; | |
dirs.sort(compare); | |
$.each(dirs, function(i, d){ | |
html.push(itemhtml(d)); | |
}); | |
parent.append(html.join('')); | |
}); | |
} | |
if (orphans.length && orphans.length < length) { | |
return updateTree(orphans); | |
} | |
if (length && !mobile) { | |
updateDroppable(); | |
} | |
}, | |
/** | |
* sort function by dir.name | |
* | |
*/ | |
compare = function(dir1, dir2) { | |
return fm.naturalCompare(dir1.name, dir2.name); | |
}, | |
/** | |
* Auto scroll to cwd | |
* | |
* @return void | |
*/ | |
autoScroll = function() { | |
var current = $('#'+fm.navHash2Id(fm.cwd().hash)); | |
if (current.length) { | |
var parent = tree.parent().stop(false, true), | |
top = parent.offset().top, | |
treeH = parent.height(), | |
bottom = top + treeH - current.outerHeight(), | |
tgtTop = current.offset().top; | |
if (tgtTop < top || tgtTop > bottom) { | |
parent.animate({ scrollTop : parent.scrollTop() + tgtTop - top - treeH / 3 }, { duration : 'fast' }); | |
} | |
} | |
}, | |
/** | |
* Mark current directory as active | |
* If current directory is not in tree - load it and its parents | |
* | |
* @param {Boolean} do not expand cwd | |
* @return void | |
*/ | |
sync = function(noCwd, dirs) { | |
var cwd = fm.cwd(), | |
cwdhash = cwd.hash, | |
current = $('#'+fm.navHash2Id(cwdhash)), | |
noCwd = noCwd || false, | |
dirs = dirs || [], | |
rootNode, dir, link, subs, subsLen, cnt; | |
if (openRoot) { | |
rootNode = $('#'+fm.navHash2Id(fm.root())); | |
rootNode.hasClass(loaded) && rootNode.addClass(expanded).next('.'+subtree).show(); | |
openRoot = false; | |
} | |
if (!current.hasClass(active)) { | |
tree.find(selNavdir+'.'+active).removeClass(active); | |
current.addClass(active); | |
} | |
if (opts.syncTree || !current.length) { | |
if (current.length) { | |
if (!noCwd) { | |
current.addClass(loaded); | |
if (openCwd && current.hasClass(collapsed)) { | |
current.addClass(expanded).next('.'+subtree).slideDown(); | |
} | |
} | |
subs = current.parentsUntil('.'+root).filter('.'+subtree); | |
subsLen = subs.length; | |
cnt = 1; | |
subs.show().prev(selNavdir).addClass(expanded, function(){ | |
!noCwd && subsLen == cnt++ && autoScroll(); | |
}); | |
!subsLen && !noCwd && autoScroll(); | |
return; | |
} | |
if (fm.newAPI) { | |
dir = fm.file(cwdhash); | |
if (dir && dir.phash) { | |
link = $('#'+fm.navHash2Id(dir.phash)); | |
if (link.length && link.hasClass(loaded)) { | |
updateTree([dir]); | |
sync(noCwd); | |
return; | |
} | |
} | |
link = cwd.root? $('#'+fm.navHash2Id(cwd.root)) : null; | |
if (link) { | |
spinner.insertBefore(link.children('.'+arrow)); | |
link.removeClass(collapsed); | |
} | |
fm.request({ | |
data : {cmd : 'parents', target : cwdhash}, | |
preventFail : true | |
}) | |
.done(function(data) { | |
if (fm.api < 2.1) { | |
data.tree = data.tree.concat([cwd]); | |
} | |
dirs = $.merge(dirs, filter(data.tree)); | |
updateTree(dirs); | |
updateArrows(dirs, loaded); | |
cwdhash == cwd.hash && sync(noCwd); | |
}) | |
.always(function(data) { | |
if (link) { | |
spinner.remove(); | |
link.addClass(collapsed+' '+loaded); | |
} | |
}); | |
} | |
} | |
}, | |
/** | |
* Make writable and not root dirs droppable | |
* | |
* @return void | |
*/ | |
updateDroppable = function(target) { | |
var limit = 100, | |
next; | |
if (!target) { | |
tree.find('div.'+uploadable).find(selNavdir+':not(.elfinder-ro,.elfinder-na)').addClass('native-droppable'); | |
target = tree.find('div.'+pastable).find(selNavdir+':not(.'+droppable+',.elfinder-ro,.elfinder-na)'); | |
} | |
if (target.length > limit) { | |
next = target.slice(limit); | |
target = target.slice(0, limit); | |
} | |
target.droppable(droppableopts); | |
if (next) { | |
setTimeout(function(){ | |
updateDroppable(next); | |
}, 20); | |
} | |
}, | |
/** | |
* Check required folders for subfolders and update arrow classes | |
* | |
* @param Array folders to check | |
* @param String css class | |
* @return void | |
*/ | |
updateArrows = function(dirs, cls) { | |
var sel = cls == loaded | |
? '.'+collapsed+':not(.'+loaded+')' | |
: ':not(.'+collapsed+')'; | |
//tree.find('.'+subtree+':has(*)').prev(':not(.'+collapsed+')').addClass(collapsed) | |
$.each(dirs, function(i, dir) { | |
$('#'+fm.navHash2Id(dir.phash)+sel) | |
.filter(function() { return $(this).next('.'+subtree).children().length > 0 }) | |
.addClass(cls); | |
}) | |
}, | |
/** | |
* Navigation tree | |
* | |
* @type JQuery | |
*/ | |
tree = $(this).addClass(treeclass) | |
// make dirs draggable and toggle hover class | |
.on('mouseenter mouseleave', selNavdir, function(e) { | |
var link = $(this), | |
enter = e.type == 'mouseenter'; | |
if (!link.hasClass(dropover+' '+disabled)) { | |
!mobile && enter && !link.hasClass(root+' '+draggable+' elfinder-na elfinder-wo') && link.draggable(fm.draggable); | |
link.toggleClass(hover, enter); | |
} | |
}) | |
// add/remove dropover css class | |
.on('dropover dropout drop', selNavdir, function(e) { | |
$(this)[e.type == 'dropover' ? 'addClass' : 'removeClass'](dropover+' '+hover); | |
}) | |
// open dir or open subfolders in tree | |
.on('click', selNavdir, function(e) { | |
var link = $(this), | |
hash = fm.navId2Hash(link.attr('id')), | |
file = fm.file(hash); | |
if (link.data('longtap')) { | |
e.stopPropagation(); | |
return; | |
} | |
fm.trigger('searchend', { noupdate: true }); | |
if (hash != fm.cwd().hash && !link.hasClass(disabled)) { | |
fm.exec('open', hash); | |
} else if (link.hasClass(collapsed)) { | |
link.children('.'+arrow).click(); | |
} | |
}) | |
// for touch device | |
.on('touchstart', selNavdir, function(e) { | |
e.stopPropagation(); | |
var evt = e.originalEvent, | |
p = $(this) | |
.addClass(hover) | |
.data('longtap', null) | |
.data('tmlongtap', setTimeout(function(e){ | |
// long tap | |
p.data('longtap', true); | |
fm.trigger('contextmenu', { | |
'type' : 'navbar', | |
'targets' : [fm.navId2Hash(p.attr('id'))], | |
'x' : evt.touches[0].pageX, | |
'y' : evt.touches[0].pageY | |
}); | |
}, 500)); | |
}) | |
.on('touchmove touchend', selNavdir, function(e) { | |
e.stopPropagation(); | |
clearTimeout($(this).data('tmlongtap')); | |
if (e.type == 'touchmove') { | |
$(this).removeClass(hover); | |
} | |
}) | |
// toggle subfolders in tree | |
.on('click', selNavdir+'.'+collapsed+' .'+arrow, function(e) { | |
var arrow = $(this), | |
link = arrow.parent(selNavdir), | |
stree = link.next('.'+subtree), | |
slideTH = 30, cnt; | |
e.stopPropagation(); | |
if (link.hasClass(loaded)) { | |
link.toggleClass(expanded); | |
cnt = link.hasClass(expanded)? stree.children().length + stree.find('div.elfinder-navbar-subtree[style*=block]').children().length : stree.find('div:visible').length; | |
if (cnt > slideTH) { | |
stree.toggle(); | |
fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1); | |
} else { | |
stree.stop(true, true).slideToggle('normal', function(){ | |
fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1); | |
}); | |
} | |
} else { | |
spinner.insertBefore(arrow); | |
link.removeClass(collapsed); | |
fm.request({cmd : 'tree', target : fm.navId2Hash(link.attr('id'))}) | |
.done(function(data) { | |
updateTree(filter(data.tree)); | |
if (stree.children().length) { | |
link.addClass(collapsed+' '+expanded); | |
if (stree.children().length > slideTH) { | |
stree.show(); | |
fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1); | |
} else { | |
stree.stop(true, true).slideDown('normal', function(){ | |
fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1); | |
}); | |
} | |
} | |
sync(true); | |
}) | |
.always(function(data) { | |
spinner.remove(); | |
link.addClass(loaded); | |
}); | |
} | |
}) | |
.on('contextmenu', selNavdir, function(e) { | |
var self = $(this); | |
e.preventDefault(); | |
fm.trigger('contextmenu', { | |
'type' : 'navbar', | |
'targets' : [fm.navId2Hash($(this).attr('id'))], | |
'x' : e.pageX, | |
'y' : e.pageY | |
}); | |
self.addClass('ui-state-hover'); | |
fm.getUI('contextmenu').children().on('mouseenter', function() { | |
self.addClass('ui-state-hover'); | |
}); | |
fm.bind('closecontextmenu', function() { | |
self.removeClass('ui-state-hover'); | |
}); | |
}), | |
// move tree into navbar | |
navbar = fm.getUI('navbar').append(tree).show() | |
; | |
fm.open(function(e) { | |
var data = e.data, | |
dirs = filter(data.files), | |
contextmenu = fm.getUI('contextmenu'); | |
data.init && tree.empty(); | |
if (fm.UA.iOS) { | |
navbar.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch'); | |
} | |
if (dirs.length) { | |
if (!contextmenu.data('cmdMaps')) { | |
contextmenu.data('cmdMaps', {}); | |
} | |
updateTree(dirs); | |
updateArrows(dirs, loaded); | |
// support volume driver option `uiCmdMap` | |
$.each(dirs, function(k, v){ | |
if (v.volumeid) { | |
if (v.uiCmdMap && Object.keys(v.uiCmdMap).length && !contextmenu.data('cmdMaps')[v.volumeid]) { | |
contextmenu.data('cmdMaps')[v.volumeid] = v.uiCmdMap; | |
} | |
} | |
}); | |
} | |
sync(false, dirs); | |
}) | |
// add new dirs | |
.add(function(e) { | |
var dirs = filter(e.data.added); | |
if (dirs.length) { | |
updateTree(dirs); | |
updateArrows(dirs, collapsed); | |
} | |
}) | |
// update changed dirs | |
.change(function(e) { | |
var dirs = filter(e.data.changed), | |
length = dirs.length, | |
l = length, | |
dir, node, tmp, realParent, reqParent, realSibling, reqSibling, isExpanded, isLoaded; | |
while (l--) { | |
dir = dirs[l]; | |
if ((node = $('#'+fm.navHash2Id(dir.hash))).length) { | |
if (dir.phash) { | |
realParent = node.closest('.'+subtree); | |
reqParent = findSubtree(dir.phash); | |
realSibling = node.parent().next(); | |
reqSibling = findSibling(reqParent, dir); | |
if (!reqParent.length) { | |
continue; | |
} | |
if (reqParent[0] !== realParent[0] || realSibling.get(0) !== reqSibling.get(0)) { | |
reqSibling.length ? reqSibling.before(node) : reqParent.append(node); | |
} | |
} | |
isExpanded = node.hasClass(expanded); | |
isLoaded = node.hasClass(loaded); | |
tmp = $(itemhtml(dir)); | |
node.replaceWith(tmp.children(selNavdir)); | |
if (dir.dirs | |
&& (isExpanded || isLoaded) | |
&& (node = $('#'+fm.navHash2Id(dir.hash))) | |
&& node.next('.'+subtree).children().length) { | |
isExpanded && node.addClass(expanded); | |
isLoaded && node.addClass(loaded); | |
} | |
} | |
} | |
sync(); | |
length && !mobile && updateDroppable(); | |
}) | |
// remove dirs | |
.remove(function(e) { | |
var dirs = e.data.removed, | |
l = dirs.length, | |
node, stree; | |
while (l--) { | |
if ((node = $('#'+fm.navHash2Id(dirs[l]))).length) { | |
stree = node.closest('.'+subtree); | |
node.parent().detach(); | |
if (!stree.children().length) { | |
stree.hide().prev(selNavdir).removeClass(collapsed+' '+expanded+' '+loaded); | |
} | |
} | |
} | |
}) | |
// lock/unlock dirs while moving | |
.bind('lockfiles unlockfiles', function(e) { | |
var lock = e.type == 'lockfiles', | |
helperLocked = e.data.helper? e.data.helper.data('locked') : false, | |
act = (lock && !helperLocked) ? 'disable' : 'enable', | |
dirs = $.map(e.data.files||[], function(h) { | |
var dir = fm.file(h); | |
return dir && dir.mime == 'directory' ? h : null; | |
}); | |
$.each(dirs, function(i, hash) { | |
var dir = $('#'+fm.navHash2Id(hash)); | |
if (dir.length && !helperLocked) { | |
dir.hasClass(draggable) && dir.draggable(act); | |
dir.hasClass(droppable) && dir.droppable(act); | |
dir[lock ? 'addClass' : 'removeClass'](disabled); | |
} | |
}); | |
}); | |
}); | |
return this; | |
} | |
/* | |
* File: /js/ui/uploadButton.js | |
*/ | |
/** | |
* @class elFinder toolbar's button tor upload file | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfinderuploadbutton = function(cmd) { | |
return this.each(function() { | |
var button = $(this).elfinderbutton(cmd) | |
.off('click'), | |
form = $('<form/>').appendTo(button), | |
input = $('<input type="file" multiple="true" title="'+cmd.fm.i18n('selectForUpload')+'"/>') | |
.change(function() { | |
var _input = $(this); | |
if (_input.val()) { | |
cmd.exec({input : _input.remove()[0]}); | |
input.clone(true).appendTo(form); | |
} | |
}) | |
.on('dragover', function(e) { | |
e.originalEvent.dataTransfer.dropEffect = 'copy'; | |
}); | |
form.append(input.clone(true)); | |
cmd.change(function() { | |
form[cmd.disabled() ? 'hide' : 'show'](); | |
}) | |
.change(); | |
}); | |
} | |
/* | |
* File: /js/ui/viewbutton.js | |
*/ | |
/** | |
* @class elFinder toolbar button to switch current directory view. | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfinderviewbutton = function(cmd) { | |
return this.each(function() { | |
var button = $(this).elfinderbutton(cmd), | |
icon = button.children('.elfinder-button-icon'); | |
cmd.change(function() { | |
var icons = cmd.value == 'icons'; | |
icon.toggleClass('elfinder-button-icon-view-list', icons); | |
button.attr('title', cmd.fm.i18n(icons ? 'viewlist' : 'viewicons')); | |
}); | |
}); | |
} | |
/* | |
* File: /js/ui/workzone.js | |
*/ | |
/** | |
* @class elfinderworkzone - elFinder container for nav and current directory | |
* @author Dmitry (dio) Levashov | |
**/ | |
$.fn.elfinderworkzone = function(fm) { | |
var cl = 'elfinder-workzone'; | |
this.not('.'+cl).each(function() { | |
var wz = $(this).addClass(cl), | |
wdelta = wz.outerHeight(true) - wz.height(), | |
parent = wz.parent(); | |
parent.add(window).on('resize', function() { | |
var height = parent.height(); | |
parent.children(':visible:not(.'+cl+')').each(function() { | |
var ch = $(this); | |
if (ch.css('position') != 'absolute' && ch.css('position') != 'fixed') { | |
height -= ch.outerHeight(true); | |
} | |
}); | |
wz.height(height - wdelta); | |
}); | |
}); | |
return this; | |
} | |
/* | |
* File: /js/commands/archive.js | |
*/ | |
/** | |
* @class elFinder command "archive" | |
* Archive selected files | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.archive = function() { | |
var self = this, | |
fm = self.fm, | |
mimes = [], | |
dfrd; | |
this.variants = []; | |
this.disableOnSearch = false; | |
/** | |
* Update mimes on open/reload | |
* | |
* @return void | |
**/ | |
fm.bind('open reload', function() { | |
self.variants = []; | |
$.each((mimes = fm.option('archivers')['create'] || []), function(i, mime) { | |
self.variants.push([mime, fm.mime2kind(mime)]) | |
}); | |
self.change(); | |
}); | |
this.getstate = function(sel) { | |
var sel = this.files(sel), | |
cnt = sel.length, | |
chk = fm.cwd().write, | |
cwdId; | |
if (chk && fm.searchStatus.state > 1) { | |
cwdId = fm.cwd().volumeid; | |
chk = (cnt === $.map(sel, function(f) { return f.read && f.hash.indexOf(cwdId) === 0 ? f : null; }).length); | |
} | |
return chk && !this._disabled && mimes.length && (cnt || (dfrd && dfrd.state() == 'pending')) ? 0 : -1; | |
} | |
this.exec = function(hashes, type) { | |
var files = this.files(hashes), | |
cnt = files.length, | |
mime = type || mimes[0], | |
cwd = fm.cwd(), | |
error = ['errArchive', 'errPerm', 'errCreatingTempDir', 'errFtpDownloadFile', 'errFtpUploadFile', 'errFtpMkdir', 'errArchiveExec', 'errExtractExec', 'errRm'], | |
i, makeDfrd; | |
dfrd = $.Deferred().fail(function(error) { | |
error && fm.error(error); | |
}); | |
if (!(this.enabled() && cnt && mimes.length && $.inArray(mime, mimes) !== -1)) { | |
return dfrd.reject(); | |
} | |
if (!cwd.write) { | |
return dfrd.reject(error); | |
} | |
for (i = 0; i < cnt; i++) { | |
if (!files[i].read) { | |
return dfrd.reject(error); | |
} | |
} | |
self.mime = mime; | |
self.prefix = ((cnt > 1)? 'Archive' : files[0].name) + '.' + fm.option('archivers')['createext'][mime]; | |
self.data = {targets : self.hashes(hashes), type : mime}; | |
makeDfrd = $.proxy(fm.res('mixin', 'make'), self)(); | |
dfrd.reject(); | |
return makeDfrd; | |
} | |
} | |
/* | |
* File: /js/commands/back.js | |
*/ | |
/** | |
* @class elFinder command "back" | |
* Open last visited folder | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.back = function() { | |
this.alwaysEnabled = true; | |
this.updateOnSelect = false; | |
this.shortcuts = [{ | |
pattern : 'ctrl+left backspace' | |
}]; | |
this.getstate = function() { | |
return this.fm.history.canBack() ? 0 : -1; | |
} | |
this.exec = function() { | |
return this.fm.history.back(); | |
} | |
} | |
/* | |
* File: /js/commands/chmod.js | |
*/ | |
/** | |
* @class elFinder command "chmod". | |
* Chmod files. | |
* | |
* @type elFinder.command | |
* @author Naoki Sawada | |
*/ | |
elFinder.prototype.commands.chmod = function() { | |
this.updateOnSelect = false; | |
var self = this; | |
var fm = this.fm, | |
level = { | |
0 : 'owner', | |
1 : 'group', | |
2 : 'other' | |
}, | |
msg = { | |
read : fm.i18n('read'), | |
write : fm.i18n('write'), | |
execute : fm.i18n('execute'), | |
perm : fm.i18n('perm'), | |
kind : fm.i18n('kind'), | |
files : fm.i18n('files') | |
}, | |
isPerm = function(perm){ | |
return (!isNaN(parseInt(perm, 8) && parseInt(perm, 8) <= 511) || perm.match(/^([r-][w-][x-]){3}$/i)); | |
}; | |
this.tpl = { | |
main : '<div class="ui-helper-clearfix elfinder-info-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}</div>' | |
+'{dataTable}', | |
itemTitle : '<strong>{name}</strong><span id="elfinder-info-kind">{kind}</span>', | |
groupTitle : '<strong>{items}: {num}</strong>', | |
dataTable : '<table id="{id}-table-perm"><tr><td>{0}</td><td>{1}</td><td>{2}</td></tr></table>' | |
+'<div class="">'+msg.perm+': <input id="{id}-perm" type="text" size="4" maxlength="3" value="{value}"></div>', | |
fieldset : '<fieldset id="{id}-fieldset-{level}"><legend>{f_title}{name}</legend>' | |
+'<input type="checkbox" value="4" id="{id}-read-{level}-perm"{checked-r}> <label for="{id}-read-{level}-perm">'+msg.read+'</label><br>' | |
+'<input type="checkbox" value="6" id="{id}-write-{level}-perm"{checked-w}> <label for="{id}-write-{level}-perm">'+msg.write+'</label><br>' | |
+'<input type="checkbox" value="5" id="{id}-execute-{level}-perm"{checked-x}> <label for="{id}-execute-{level}-perm">'+msg.execute+'</label><br>' | |
}; | |
this.shortcuts = [{ | |
//pattern : 'ctrl+p' | |
}]; | |
this.getstate = function(sel) { | |
var fm = this.fm; | |
sel = sel || fm.selected(); | |
if (sel.length == 0) { | |
sel = [ fm.cwd().hash ]; | |
} | |
return !this._disabled && self.checkstate(this.files(sel)) ? 0 : -1; | |
}; | |
this.checkstate = function(sel) { | |
var cnt = sel.length; | |
if (!cnt) return false; | |
var chk = $.map(sel, function(f) { | |
return (f.isowner && f.perm && isPerm(f.perm) && (cnt == 1 || f.mime != 'directory')) ? f : null; | |
}).length; | |
return (cnt == chk)? true : false; | |
}; | |
this.exec = function(hashes) { | |
var files = this.files(hashes); | |
if (! files.length) { | |
hashes = [ this.fm.cwd().hash ]; | |
files = this.files(hashes); | |
} | |
var fm = this.fm, | |
dfrd = $.Deferred().always(function() { | |
fm.enable(); | |
}), | |
tpl = this.tpl, | |
hashes = this.hashes(hashes), | |
cnt = files.length, | |
file = files[0], | |
id = fm.namespace + '-perm-' + file.hash, | |
view = tpl.main, | |
checked = ' checked="checked"', | |
buttons = function() { | |
var buttons = {}; | |
buttons[fm.i18n('btnApply')] = save; | |
buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); }; | |
return buttons; | |
}, | |
save = function() { | |
var perm = $.trim($('#'+id+'-perm').val()); | |
if (!isPerm(perm)) return false; | |
dialog.elfinderdialog('close'); | |
fm.request({ | |
data : { | |
cmd : 'chmod', | |
targets : hashes, | |
mode : perm | |
}, | |
notify : {type : 'chmod', cnt : cnt} | |
}) | |
.fail(function(error) { | |
dfrd.reject(error); | |
}) | |
.done(function(data) { | |
dfrd.resolve(data); | |
}); | |
}, | |
setperm = function() { | |
var perm = ''; | |
var _perm; | |
for (var i = 0; i < 3; i++){ | |
_perm = 0; | |
if ($("#"+id+"-read-"+level[i]+'-perm').is(':checked')) { | |
_perm = (_perm | 4); | |
} | |
if ($("#"+id+"-write-"+level[i]+'-perm').is(':checked')) { | |
_perm = (_perm | 2); | |
} | |
if ($("#"+id+"-execute-"+level[i]+'-perm').is(':checked')) { | |
_perm = (_perm | 1); | |
} | |
perm += _perm.toString(8); | |
} | |
$('#'+id+'-perm').val(perm); | |
}, | |
setcheck = function(perm) { | |
var _perm; | |
for (var i = 0; i < 3; i++){ | |
_perm = parseInt(perm.slice(i, i+1), 8); | |
$("#"+id+"-read-"+level[i]+'-perm').prop("checked", false); | |
$("#"+id+"-write-"+level[i]+'-perm').prop("checked", false); | |
$("#"+id+"-execute-"+level[i]+'-perm').prop("checked", false); | |
if ((_perm & 4) == 4) { | |
$("#"+id+"-read-"+level[i]+'-perm').prop("checked", true); | |
} | |
if ((_perm & 2) == 2) { | |
$("#"+id+"-write-"+level[i]+'-perm').prop("checked", true); | |
} | |
if ((_perm & 1) == 1) { | |
$("#"+id+"-execute-"+level[i]+'-perm').prop("checked", true); | |
} | |
} | |
setperm(); | |
}, | |
makeperm = function(files) { | |
var perm = '777', ret = '', chk, _chk, _perm; | |
var len = files.length; | |
for (var i2 = 0; i2 < len; i2++) { | |
chk = getPerm(files[i2].perm);; | |
ret = ''; | |
for (var i = 0; i < 3; i++){ | |
_chk = parseInt(chk.slice(i, i+1), 8); | |
_perm = parseInt(perm.slice(i, i+1), 8); | |
if ((_chk & 4) != 4 && (_perm & 4) == 4) { | |
_perm -= 4; | |
} | |
if ((_chk & 2) != 2 && (_perm & 2) == 2) { | |
_perm -= 2; | |
} | |
if ((_chk & 1) != 1 && (_perm & 1) == 1) { | |
_perm -= 1; | |
} | |
ret += _perm.toString(8); | |
} | |
perm = ret; | |
} | |
return perm; | |
}, | |
makeName = function(name) { | |
return name? ':'+name : ''; | |
}, | |
makeDataTable = function(perm, f) { | |
var _perm, fieldset; | |
var value = ''; | |
var dataTable = tpl.dataTable; | |
for (var i = 0; i < 3; i++){ | |
_perm = parseInt(perm.slice(i, i+1), 8); | |
value += _perm.toString(8); | |
fieldset = tpl.fieldset.replace('{f_title}', fm.i18n(level[i])).replace('{name}', makeName(f[level[i]])).replace(/\{level\}/g, level[i]); | |
dataTable = dataTable.replace('{'+i+'}', fieldset) | |
.replace('{checked-r}', ((_perm & 4) == 4)? checked : '') | |
.replace('{checked-w}', ((_perm & 2) == 2)? checked : '') | |
.replace('{checked-x}', ((_perm & 1) == 1)? checked : ''); | |
} | |
dataTable = dataTable.replace('{value}', value).replace('{valueCaption}', msg['perm']); | |
return dataTable; | |
}, | |
getPerm = function(perm){ | |
if (isNaN(parseInt(perm, 8))) { | |
var mode_array = perm.split(''); | |
var a = []; | |
for (var i = 0, l = mode_array.length; i < l; i++) { | |
if (i === 0 || i === 3 || i === 6) { | |
if (mode_array[i].match(/[r]/i)) { | |
a.push(1); | |
} else if (mode_array[i].match(/[-]/)) { | |
a.push(0); | |
} | |
} else if ( i === 1 || i === 4 || i === 7) { | |
if (mode_array[i].match(/[w]/i)) { | |
a.push(1); | |
} else if (mode_array[i].match(/[-]/)) { | |
a.push(0); | |
} | |
} else { | |
if (mode_array[i].match(/[x]/i)) { | |
a.push(1); | |
} else if (mode_array[i].match(/[-]/)) { | |
a.push(0); | |
} | |
} | |
} | |
a.splice(3, 0, ","); | |
a.splice(7, 0, ","); | |
var b = a.join(""); | |
var b_array = b.split(","); | |
var c = []; | |
for (var j = 0, m = b_array.length; j < m; j++) { | |
var p = parseInt(b_array[j], 2).toString(8); | |
c.push(p) | |
} | |
perm = c.join(''); | |
} else { | |
perm = parseInt(perm, 8).toString(8); | |
} | |
return perm; | |
}, | |
opts = { | |
title : this.title, | |
width : 'auto', | |
buttons : buttons(), | |
close : function() { $(this).elfinderdialog('destroy'); } | |
}, | |
dialog = fm.getUI().find('#'+id), | |
tmb = '', title, dataTable; | |
if (dialog.length) { | |
dialog.elfinderdialog('toTop'); | |
return $.Deferred().resolve(); | |
} | |
view = view.replace('{class}', cnt > 1 ? 'elfinder-cwd-icon-group' : fm.mime2class(file.mime)); | |
if (cnt > 1) { | |
title = tpl.groupTitle.replace('{items}', fm.i18n('items')).replace('{num}', cnt); | |
} else { | |
title = tpl.itemTitle.replace('{name}', file.name).replace('{kind}', fm.mime2kind(file)); | |
if (file.tmb) { | |
tmb = fm.option('tmbUrl')+file.tmb; | |
} | |
} | |
dataTable = makeDataTable(makeperm(files), files.length == 1? files[0] : {}); | |
view = view.replace('{title}', title).replace('{dataTable}', dataTable).replace(/{id}/g, id); | |
dialog = fm.dialog(view, opts); | |
dialog.attr('id', id); | |
// load thumbnail | |
if (tmb) { | |
$('<img/>') | |
.on('load', function() { dialog.find('.elfinder-cwd-icon').css('background', 'url("'+tmb+'") center center no-repeat'); }) | |
.attr('src', tmb); | |
} | |
$('#' + id + '-table-perm :checkbox').on('click', function(){setperm('perm');}); | |
$('#' + id + '-perm').on('keydown', function(e) { | |
var c = e.keyCode; | |
e.stopPropagation(); | |
if (c == 13) { | |
save(); | |
return; | |
} | |
}).on('focus', function(e){ | |
$(this).select(); | |
}).on('keyup', function(e) { | |
if ($(this).val().length == 3) { | |
$(this).select(); | |
setcheck($(this).val()); | |
} | |
}); | |
return dfrd; | |
}; | |
}; | |
/* | |
* File: /js/commands/copy.js | |
*/ | |
/** | |
* @class elFinder command "copy". | |
* Put files in filemanager clipboard. | |
* | |
* @type elFinder.command | |
* @author Dmitry (dio) Levashov | |
*/ | |
elFinder.prototype.commands.copy = function() { | |
this.shortcuts = [{ | |
pattern : 'ctrl+c ctrl+insert' | |
}]; | |
this.getstate = function(sel) { | |
var sel = this.files(sel), | |
cnt = sel.length; | |
return !this._disabled && cnt && $.map(sel, function(f) { return f.phash && f.read ? f : null }).length == cnt ? 0 : -1; | |
} | |
this.exec = function(hashes) { | |
var fm = this.fm, | |
dfrd = $.Deferred() | |
.fail(function(error) { | |
fm.error(error); | |
}); | |
$.each(this.files(hashes), function(i, file) { | |
if (!(file.read && file.phash)) { | |
return !dfrd.reject(['errCopy', file.name, 'errPerm']); | |
} | |
}); | |
return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes))); | |
} | |
} | |
/* | |
* File: /js/commands/cut.js | |
*/ | |
/** | |
* @class elFinder command "copy". | |
* Put files in filemanager clipboard. | |
* | |
* @type elFinder.command | |
* @author Dmitry (dio) Levashov | |
*/ | |
elFinder.prototype.commands.cut = function() { | |
this.shortcuts = [{ | |
pattern : 'ctrl+x shift+insert' | |
}]; | |
this.getstate = function(sel) { | |
var sel = this.files(sel), | |
cnt = sel.length; | |
return !this._disabled && cnt && $.map(sel, function(f) { return f.phash && f.read && !f.locked ? f : null }).length == cnt ? 0 : -1; | |
} | |
this.exec = function(hashes) { | |
var fm = this.fm, | |
dfrd = $.Deferred() | |
.fail(function(error) { | |
fm.error(error); | |
}); | |
$.each(this.files(hashes), function(i, file) { | |
if (!(file.read && file.phash) ) { | |
return !dfrd.reject(['errCopy', file.name, 'errPerm']); | |
} | |
if (file.locked) { | |
return !dfrd.reject(['errLocked', file.name]); | |
} | |
}); | |
return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes), true)); | |
} | |
} | |
/* | |
* File: /js/commands/download.js | |
*/ | |
/** | |
* @class elFinder command "download". | |
* Download selected files. | |
* Only for new api | |
* | |
* @author Dmitry (dio) Levashov, dio@std42.ru | |
**/ | |
elFinder.prototype.commands.zipdl = function() {}; | |
elFinder.prototype.commands.download = function() { | |
var self = this, | |
fm = this.fm, | |
zipOn = false, | |
filter = function(hashes) { | |
var mixed = false, | |
croot = ''; | |
if (fm.searchStatus.state > 1 && fm.searchStatus.target === '') { | |
hashes = $.map(hashes, function(h) { | |
return fm.isCommandEnabled('download', h)? h : null; | |
}); | |
croot = fm.root(hashes[0]); | |
$.each(hashes, function(i, h) { | |
if (mixed = (croot !== fm.root(h))) { | |
return false; | |
} | |
}); | |
zipOn = (!mixed && fm.command('zipdl') && fm.isCommandEnabled('zipdl', croot)); | |
} else { | |
if (!fm.isCommandEnabled('download', hashes[0])) { | |
return []; | |
} | |
zipOn = (fm.command('zipdl') && fm.isCommandEnabled('zipdl', hashes[0])); | |
} | |
return (!zipOn)? | |
$.map(self.files(hashes), function(f) { return f.mime == 'directory' ? null : f; }) | |
: self.files(hashes); | |
}; | |
this.linkedCmds = ['zipdl']; | |
this.shortcuts = [{ | |
pattern : 'shift+enter' | |
}]; | |
this.getstate = function(sel) { | |
var sel = this.hashes(sel), | |
cnt = sel.length, | |
maxReq = this.options.maxRequests || 10, | |
czipdl = fm.command('zipdl'), | |
mixed = false, | |
croot = ''; | |
if (cnt > 0 && fm.searchStatus.state > 1 && fm.searchStatus.target === '') { | |
croot = fm.root(sel[0]); | |
$.each(sel, function(i, h) { | |
if (mixed = (croot !== fm.root(h))) { | |
return false; | |
} | |
}); | |
} | |
return (mixed || !czipdl || czipdl._disabled)? | |
(!this._disabled && cnt && cnt <= maxReq && ((!fm.UA.IE && !fm.UA.Mobile) || cnt == 1) && cnt == filter(sel).length ? 0 : -1) | |
: (!this._disabled && cnt ? 0 : -1); | |
}; | |
fm.bind('contextmenu', function(e){ | |
var fm = self.fm, | |
helper = null, | |
targets, file, link, | |
getExtra = function(file) { | |
var link = file.url || fm.url(file.hash); | |
return { | |
icon: 'link', | |
node: $('<a/>') | |
.attr({href: link, target: '_blank', title: fm.i18n('link')}) | |
.text(file.name) | |
.on('mousedown click touchstart touchmove touchend contextmenu', function(e){ | |
var cm = fm.getUI('contextmenu'); | |
e.stopPropagation(); | |
// 'mouseEvInternal' for Firefox's bug (maybe) | |
cm.data('mouseEvInternal', true); | |
setTimeout(function(){ | |
cm.data('mouseEvInternal', false); | |
}, 500); | |
}) | |
.on('dragstart', function(e) { | |
var dt = e.dataTransfer || e.originalEvent.dataTransfer || null; | |
helper = null; | |
if (dt) { | |
var icon = function(f) { | |
var mime = f.mime, i; | |
i = '<div class="elfinder-cwd-icon '+fm.mime2class(mime)+' ui-corner-all"/>'; | |
if (f.tmb && f.tmb !== 1) { | |
i = $(i).css('background', "url('"+fm.option('tmbUrl')+f.tmb+"') center center no-repeat").get(0).outerHTML; | |
} | |
return i; | |
}; | |
dt.effectAllowed = 'copyLink'; | |
if (dt.setDragImage) { | |
helper = $('<div class="elfinder-drag-helper html5-native">').append(icon(file)).appendTo($(document.body)); | |
dt.setDragImage(helper.get(0), 50, 47); | |
} | |
if (!fm.UA.IE) { | |
dt.setData('elfinderfrom', window.location.href + file.phash); | |
dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), ''); | |
} | |
} | |
}) | |
.on('dragend', function(e) { | |
helper && helper.remove(); | |
}) | |
}; | |
}; | |
self.extra = null; | |
if (e.data) { | |
targets = e.data.targets || []; | |
if (targets.length === 1 && (file = fm.file(targets[0])) && file.mime !== 'directory') { | |
if (file.url != '1') { | |
self.extra = getExtra(file); | |
} else { | |
// Get URL ondemand | |
var node; | |
self.extra = { | |
icon: 'link', | |
node: $('<a/>') | |
.attr({href: '#', title: fm.i18n('getLink'), draggable: 'false'}) | |
.text(file.name) | |
.on('click', function(e){ | |
var parent = node.parent(); | |
e.stopPropagation(); | |
e.preventDefault(); | |
parent.removeClass('ui-state-disabled').addClass('elfinder-button-icon-spinner'); | |
fm.request({ | |
data : {cmd : 'url', target : file.hash}, | |
preventDefault : true | |
}) | |
.always(function(data) { | |
parent.removeClass('elfinder-button-icon-spinner'); | |
if (data.url) { | |
var rfile = fm.file(file.hash); | |
rfile.url = data.url; | |
node.replaceWith(getExtra(file).node); | |
} else { | |
parent.addClass('ui-state-disabled'); | |
} | |
}); | |
}) | |
}; | |
node = self.extra.node; | |
node.ready(function(){ | |
setTimeout(function(){ | |
node.parent().addClass('ui-state-disabled').css('pointer-events', 'auto'); | |
}, 10); | |
}); | |
} | |
} | |
} | |
}); | |
this.exec = function(hashes) { | |
var hashes = this.hashes(hashes), | |
fm = this.fm, | |
base = fm.options.url, | |
files = filter(hashes), | |
dfrd = $.Deferred(), | |
iframes = '', | |
cdata = '', | |
i, url; | |
if (!files.length) { | |
return dfrd.reject(); | |
} | |
var link = $('<a>').hide().appendTo($('body')), | |
html5dl = (typeof link.get(0).download === 'string'); | |
if (zipOn && (files.length > 1 || files[0].mime === 'directory')) { | |
dfrd = fm.request({ | |
data : {cmd : 'zipdl', targets : hashes}, | |
notify : {type : 'zipdl', cnt : 1, hideCnt : true, multi : true}, | |
cancel : true, | |
preventDefault : true | |
}).done(function(e) { | |
var zipdl, dialog, btn = {}, dllink, form, | |
uniq = 'dlw' + (+new Date()); | |
if (e.error) { | |
fm.error(e.error); | |
dfrd.reject(); | |
} else if (e.zipdl) { | |
zipdl = e.zipdl; | |
if (!html5dl && fm.UA.Mobile) { | |
url = fm.options.url + (fm.options.url.indexOf('?') === -1 ? '?' : '&') | |
+ 'cmd=zipdl&download=1'; | |
$.each([hashes[0], zipdl.file, zipdl.name, zipdl.mime], function(key, val) { | |
url += '&targets%5B%5D='+encodeURIComponent(val); | |
}); | |
$.each(fm.options.customData, function(key, val) { | |
url += '&'+encodeURIComponent(key)+'='+encodeURIComponent(val); | |
}); | |
url += '&'+encodeURIComponent(zipdl.name); | |
dllink = $('<a/>') | |
.attr('href', url) | |
.attr('download', encodeURIComponent(zipdl.name)) | |
.attr('target', '_blank') | |
.on('click', function() { | |
fm.trigger('download', {files : files}); | |
dfrd.resolve(hashes); | |
dialog.elfinderdialog('close'); | |
}) | |
.append('<span class="elfinder-button-icon elfinder-button-icon-download"></span>'+fm.escape(zipdl.name)); | |
btn[fm.i18n('btnCancel')] = function() { | |
dialog.elfinderdialog('close'); | |
}; | |
dialog = fm.dialog(dllink, { | |
title: fm.i18n('link'), | |
buttons: btn, | |
width: '200px' | |
}); | |
} else { | |
form = $('<form action="'+fm.options.url+'" method="post" target="'+uniq+'" style="display:none"/>') | |
.append('<input type="hidden" name="cmd" value="zipdl"/>') | |
.append('<input type="hidden" name="download" value="1"/>'); | |
$.each([hashes[0], zipdl.file, zipdl.name, zipdl.mime], function(key, val) { | |
form.append('<input type="hidden" name="targets[]" value="'+fm.escape(val)+'"/>'); | |
}); | |
$.each(fm.options.customData, function(key, val) { | |
form.append('<input type="hidden" name="'+key+'" value="'+fm.escape(val)+'"/>'); | |
}); | |
form.attr('target', uniq).appendTo('body'); | |
iframes = $('<iframe style="display:none" name="'+uniq+'">') | |
.appendTo('body') | |
.ready(function() { | |
form.submit().remove(); | |
fm.trigger('download', {files : files}); | |
dfrd.resolve(hashes); | |
setTimeout(function() { | |
iframes.remove(); | |
}, fm.UA.Firefox? 20000 : 1000); // give mozilla 20 sec file to be saved | |
}); | |
} | |
} | |
}).fail(function(error) { | |
error && fm.error(error); | |
dfrd.reject(); | |
}).always(function() { | |
link.remove(); | |
}); | |
fm.trigger('download', {files : files}); | |
return dfrd; | |
} else { | |
for (i = 0; i < files.length; i++) { | |
url = fm.openUrl(files[i].hash, true); | |
if (html5dl) { | |
link.attr('href', url) | |
.attr('download', encodeURIComponent(files[i].name)) | |
.attr('target', '_blank') | |
.get(0).click(); | |
} else { | |
if (fm.UA.Mobile) { | |
setTimeout(function(){ | |
if (! window.open(url)) { | |
fm.error('errPopup'); | |
} | |
}, 100); | |
} else { | |
iframes += '<iframe class="downloader" id="downloader-' + files[i].hash+'" style="display:none" src="'+url+'"/>'; | |
} | |
} | |
} | |
link.remove(); | |
$(iframes) | |
.appendTo('body') | |
.ready(function() { | |
setTimeout(function() { | |
$(iframes).each(function() { | |
$('#' + $(this).attr('id')).remove(); | |
}); | |
}, fm.UA.Firefox? (20000 + (10000 * i)) : 1000); // give mozilla 20 sec + 10 sec for each file to be saved | |
}); | |
fm.trigger('download', {files : files}); | |
return dfrd.resolve(hashes); | |
} | |
}; | |
}; | |
/* | |
* File: /js/commands/duplicate.js | |
*/ | |
/** | |
* @class elFinder command "duplicate" | |
* Create file/folder copy with suffix "copy Number" | |
* | |
* @type elFinder.command | |
* @author Dmitry (dio) Levashov | |
*/ | |
elFinder.prototype.commands.duplicate = function() { | |
var fm = this.fm; | |
this.getstate = function(sel) { | |
var sel = this.files(sel), | |
cnt = sel.length; | |
return !this._disabled && cnt && fm.cwd().write && $.map(sel, function(f) { return f.phash && f.read && f.phash === fm.cwd().hash? f : null }).length == cnt ? 0 : -1; | |
} | |
this.exec = function(hashes) { | |
var fm = this.fm, | |
files = this.files(hashes), | |
cnt = files.length, | |
dfrd = $.Deferred() | |
.fail(function(error) { | |
error && fm.error(error); | |
}), | |
args = []; | |
if (!cnt || this.getstate(hashes) === -1) { | |
return dfrd.reject(); | |
} | |
$.each(files, function(i, file) { | |
if (!file.read || !fm.file(file.phash).write) { | |
return !dfrd.reject(['errCopy', file.name, 'errPerm']); | |
} | |
}); | |
if (dfrd.state() == 'rejected') { | |
return dfrd; | |
} | |
return fm.request({ | |
data : {cmd : 'duplicate', targets : this.hashes(hashes)}, | |
notify : {type : 'copy', cnt : cnt} | |
}); | |
} | |
} | |
/* | |
* File: /js/commands/edit.js | |
*/ | |
/** | |
* @class elFinder command "edit". | |
* Edit text file in dialog window | |
* | |
* @author Dmitry (dio) Levashov, dio@std42.ru | |
**/ | |
elFinder.prototype.commands.edit = function() { | |
var self = this, | |
fm = this.fm, | |
mimes = fm.res('mimes', 'text') || [], | |
rtrim = function(str){ | |
return str.replace(/\s+$/, ''); | |
}, | |
/** | |
* Return files acceptable to edit | |
* | |
* @param Array files hashes | |
* @return Array | |
**/ | |
filter = function(files) { | |
return $.map(files, function(file) { | |
return (file.mime.indexOf('text/') === 0 || $.inArray(file.mime, mimes) !== -1) | |
&& file.mime.indexOf('text/rtf') | |
&& (!self.onlyMimes.length || $.inArray(file.mime, self.onlyMimes) !== -1) | |
&& file.read && file.write ? file : null; | |
}); | |
}, | |
/** | |
* Open dialog with textarea to edit file | |
* | |
* @param String id dialog id | |
* @param Object file file object | |
* @param String content file content | |
* @return $.Deferred | |
**/ | |
dialog = function(id, file, content) { | |
var dfrd = $.Deferred(), | |
ta = $('<textarea class="elfinder-file-edit" rows="20" id="'+id+'-ta">'+fm.escape(content)+'</textarea>'), | |
old = ta.val(), | |
save = function() { | |
ta.editor && ta.editor.save(ta[0], ta.editor.instance); | |
old = ta.val(); | |
dfrd.notifyWith(ta); | |
}, | |
cancel = function() { | |
var close = function(){ | |
dfrd.reject(); | |
ta.elfinderdialog('close'); | |
}; | |
ta.editor && ta.editor.save(ta[0], ta.editor.instance); | |
if (rtrim(old) !== rtrim(ta.val())) { | |
old = ta.val(); | |
fm.confirm({ | |
title : self.title, | |
text : 'confirmNotSave', | |
accept : { | |
label : 'btnSaveClose', | |
callback : function() { | |
save(); | |
close(); | |
} | |
}, | |
cancel : { | |
label : 'btnClose', | |
callback : close | |
} | |
}); | |
} else { | |
close(); | |
} | |
}, | |
savecl = function() { | |
save(); | |
cancel(); | |
}, | |
opts = { | |
title : fm.escape(file.name), | |
width : self.options.dialogWidth || 450, | |
buttons : {}, | |
btnHoverFocus : false, | |
closeOnEscape : false, | |
close : function() { | |
var $this = $(this), | |
close = function(){ | |
ta.editor && ta.editor.close(ta[0], ta.editor.instance); | |
$this.elfinderdialog('destroy'); | |
}; | |
ta.editor && ta.editor.save(ta[0], ta.editor.instance); | |
if (rtrim(old) !== rtrim(ta.val())) { | |
fm.confirm({ | |
title : self.title, | |
text : 'confirmNotSave', | |
accept : { | |
label : 'btnSaveClose', | |
callback : function() { | |
save(); | |
close(); | |
} | |
}, | |
cancel : { | |
label : 'btnClose', | |
callback : close | |
} | |
}); | |
} else { | |
close(); | |
} | |
}, | |
open : function() { | |
fm.disable(); | |
ta.focus(); | |
ta[0].setSelectionRange && ta[0].setSelectionRange(0, 0); | |
if (ta.editor) { | |
ta.editor.instance = ta.editor.load(ta[0]) || null; | |
ta.editor.focus(ta[0], ta.editor.instance); | |
} | |
} | |
}, | |
mimeMatch = function(fileMime, editorMimes){ | |
editorMimes = editorMimes || mimes.concat('text/'); | |
if ($.inArray(fileMime, editorMimes) !== -1 ) { | |
return true; | |
} | |
var i, l; | |
l = editorMimes.length; | |
for (i = 0; i < l; i++) { | |
if (fileMime.indexOf(editorMimes[i]) === 0) { | |
return true; | |
} | |
} | |
return false; | |
}, | |
extMatch = function(fileName, editorExts){ | |
if (!editorExts || !editorExts.length) { | |
return true; | |
} | |
var ext = fileName.replace(/^.+\.([^.]+)|(.+)$/, '$1$2').toLowerCase(), | |
i, l; | |
l = editorExts.length; | |
for (i = 0; i < l; i++) { | |
if (ext === editorExts[i].toLowerCase()) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
ta.getContent = function() { | |
return ta.val(); | |
}; | |
$.each(self.options.editors || [], function(i, editor) { | |
if (mimeMatch(file.mime, editor.mimes || null) | |
&& extMatch(file.name, editor.exts || null) | |
&& typeof editor.load == 'function' | |
&& typeof editor.save == 'function') { | |
ta.editor = { | |
load : editor.load, | |
save : editor.save, | |
close : typeof editor.close == 'function' ? editor.close : function() {}, | |
focus : typeof editor.focus == 'function' ? editor.focus : function() {}, | |
instance : null, | |
doSave : save, | |
doCancel : cancel, | |
doClose : savecl, | |
file : file | |
}; | |
return false; | |
} | |
}); | |
if (!ta.editor) { | |
ta.keydown(function(e) { | |
var code = e.keyCode, | |
value, start; | |
e.stopPropagation(); | |
if (code == 9) { | |
e.preventDefault(); | |
// insert tab on tab press | |
if (this.setSelectionRange) { | |
value = this.value; | |
start = this.selectionStart; | |
this.value = value.substr(0, start) + "\t" + value.substr(this.selectionEnd); | |
start += 1; | |
this.setSelectionRange(start, start); | |
} | |
} | |
if (e.ctrlKey || e.metaKey) { | |
// close on ctrl+w/q | |
if (code == 81 || code == 87) { | |
e.preventDefault(); | |
cancel(); | |
} | |
if (code == 83) { | |
e.preventDefault(); | |
save(); | |
} | |
} | |
}).on('mouseenter', function(){this.focus();}); | |
} | |
opts.buttons[fm.i18n('btnSave')] = save; | |
opts.buttons[fm.i18n('btnSaveClose')] = savecl; | |
opts.buttons[fm.i18n('btnCancel')] = cancel; | |
fm.dialog(ta, opts) | |
.attr('id', id) | |
.on('keydown keyup keypress', function(e) { | |
e.stopPropagation(); | |
}); | |
return dfrd.promise(); | |
}, | |
/** | |
* Get file content and | |
* open dialog with textarea to edit file content | |
* | |
* @param String file hash | |
* @return jQuery.Deferred | |
**/ | |
edit = function(file, doconv) { | |
var hash = file.hash, | |
opts = fm.options, | |
dfrd = $.Deferred(), | |
data = {cmd : 'file', target : hash}, | |
id = 'edit-'+fm.namespace+'-'+file.hash, | |
d = fm.getUI().find('#'+id), | |
conv = !doconv? 0 : 1, | |
error; | |
if (d.length) { | |
d.elfinderdialog('toTop'); | |
return dfrd.resolve(); | |
} | |
if (!file.read || !file.write) { | |
error = ['errOpen', file.name, 'errPerm']; | |
fm.error(error); | |
return dfrd.reject(error); | |
} | |
fm.request({ | |
data : {cmd : 'get', target : hash, conv : conv}, | |
notify : {type : 'file', cnt : 1}, | |
syncOnFail : true | |
}) | |
.done(function(data) { | |
if (data.doconv) { | |
fm.confirm({ | |
title : self.title, | |
text : 'confirmConvUTF8', | |
accept : { | |
label : 'btnConv', | |
callback : function() { | |
dfrd = edit(file, 1); | |
} | |
}, | |
cancel : { | |
label : 'btnCancel', | |
callback : function() { dfrd.reject(); } | |
} | |
}); | |
} else { | |
dialog(id, file, data.content) | |
.progress(function() { | |
var ta = this; | |
fm.request({ | |
options : {type : 'post'}, | |
data : { | |
cmd : 'put', | |
target : hash, | |
content : ta.getContent() | |
}, | |
notify : {type : 'save', cnt : 1}, | |
syncOnFail : true | |
}) | |
.fail(function(error) { | |
dfrd.reject(error); | |
}) | |
.done(function(data) { | |
data.changed && data.changed.length && fm.change(data); | |
dfrd.resolve(data); | |
setTimeout(function(){ | |
ta.focus(); | |
ta.editor && ta.editor.focus(ta[0], ta.editor.instance); | |
}, 50); | |
}); | |
}); | |
} | |
}) | |
.fail(function(error) { | |
dfrd.reject(error); | |
}); | |
return dfrd.promise(); | |
}; | |
this.shortcuts = [{ | |
pattern : 'ctrl+e' | |
}]; | |
this.init = function() { | |
this.onlyMimes = this.options.mimes || []; | |
}; | |
this.getstate = function(sel) { | |
var sel = this.files(sel), | |
cnt = sel.length; | |
return !this._disabled && cnt && filter(sel).length == cnt ? 0 : -1; | |
}; | |
this.exec = function(hashes) { | |
var files = filter(this.files(hashes)), | |
list = [], | |
file; | |
if (this.disabled()) { | |
return $.Deferred().reject(); | |
} | |
while ((file = files.shift())) { | |
list.push(edit(file)); | |
} | |
return list.length | |
? $.when.apply(null, list) | |
: $.Deferred().reject(); | |
}; | |
}; | |
/* | |
* File: /js/commands/extract.js | |
*/ | |
/** | |
* @class elFinder command "extract" | |
* Extract files from archive | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.extract = function() { | |
var self = this, | |
fm = self.fm, | |
mimes = [], | |
filter = function(files) { | |
return $.map(files, function(file) { | |
return file.read && $.inArray(file.mime, mimes) !== -1 ? file : null | |
}) | |
}; | |
this.variants = []; | |
this.disableOnSearch = true; | |
// Update mimes list on open/reload | |
fm.bind('open reload', function() { | |
mimes = fm.option('archivers')['extract'] || []; | |
self.variants = [['makedir', fm.i18n('cmdmkdir')], ['intohere', fm.i18n('btnCwd')]]; | |
self.change(); | |
}); | |
this.getstate = function(sel) { | |
var sel = this.files(sel), | |
cnt = sel.length; | |
return !this._disabled && cnt && this.fm.cwd().write && filter(sel).length == cnt ? 0 : -1; | |
} | |
this.exec = function(hashes, extractTo) { | |
var files = this.files(hashes), | |
dfrd = $.Deferred(), | |
cnt = files.length, | |
makedir = (extractTo == 'makedir')? 1 : 0, | |
i, error, | |
decision; | |
var overwriteAll = false; | |
var omitAll = false; | |
var mkdirAll = 0; | |
var names = $.map(fm.files(hashes), function(file) { return file.name; }); | |
var map = {}; | |
$.map(fm.files(hashes), function(file) { map[file.name] = file; }); | |
var decide = function(decision) { | |
switch (decision) { | |
case 'overwrite_all' : | |
overwriteAll = true; | |
break; | |
case 'omit_all': | |
omitAll = true; | |
break; | |
} | |
}; | |
var unpack = function(file) { | |
if (!(file.read && fm.file(file.phash).write)) { | |
error = ['errExtract', file.name, 'errPerm']; | |
fm.error(error); | |
dfrd.reject(error); | |
} else if ($.inArray(file.mime, mimes) === -1) { | |
error = ['errExtract', file.name, 'errNoArchive']; | |
fm.error(error); | |
dfrd.reject(error); | |
} else { | |
fm.request({ | |
data:{cmd:'extract', target:file.hash, makedir:makedir}, | |
notify:{type:'extract', cnt:1}, | |
syncOnFail:true | |
}) | |
.fail(function (error) { | |
if (dfrd.state() != 'rejected') { | |
dfrd.reject(error); | |
} | |
}) | |
.done(function () { | |
}); | |
} | |
}; | |
var confirm = function(files, index) { | |
var file = files[index], | |
name = file.name.replace(/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/ig, ''), | |
existed = ($.inArray(name, names) >= 0), | |
next = function(){ | |
if((index+1) < cnt) { | |
confirm(files, index+1); | |
} else { | |
dfrd.resolve(); | |
} | |
}; | |
if (!makedir && existed && map[name].mime != 'directory') { | |
fm.confirm( | |
{ | |
title : fm.i18n('ntfextract'), | |
text : ['errExists', name, 'confirmRepl'], | |
accept:{ | |
label : 'btnYes', | |
callback:function (all) { | |
decision = all ? 'overwrite_all' : 'overwrite'; | |
decide(decision); | |
if(!overwriteAll && !omitAll) { | |
if('overwrite' == decision) { | |
unpack(file); | |
} | |
if((index+1) < cnt) { | |
confirm(files, index+1); | |
} else { | |
dfrd.resolve(); | |
} | |
} else if(overwriteAll) { | |
for (i = index; i < cnt; i++) { | |
unpack(files[i]); | |
} | |
dfrd.resolve(); | |
} | |
} | |
}, | |
reject : { | |
label : 'btnNo', | |
callback:function (all) { | |
decision = all ? 'omit_all' : 'omit'; | |
decide(decision); | |
if(!overwriteAll && !omitAll && (index+1) < cnt) { | |
confirm(files, index+1); | |
} else if (omitAll) { | |
dfrd.resolve(); | |
} | |
} | |
}, | |
cancel : { | |
label : 'btnCancel', | |
callback:function () { | |
dfrd.resolve(); | |
} | |
}, | |
all : ((index+1) < cnt) | |
} | |
); | |
} else if (!makedir) { | |
if (mkdirAll == 0) { | |
fm.confirm({ | |
title : fm.i18n('cmdextract'), | |
text : [fm.i18n('cmdextract')+' "'+file.name+'"', 'confirmRepl'], | |
accept:{ | |
label : 'btnYes', | |
callback:function (all) { | |
all && (mkdirAll = 1); | |
unpack(file); | |
next(); | |
} | |
}, | |
reject : { | |
label : 'btnNo', | |
callback:function (all) { | |
all && (mkdirAll = -1); | |
next(); | |
} | |
}, | |
cancel : { | |
label : 'btnCancel', | |
callback:function () { | |
dfrd.resolve(); | |
} | |
}, | |
all : ((index+1) < cnt) | |
}); | |
} else { | |
(mkdirAll > 0) && unpack(file); | |
next(); | |
} | |
} else { | |
unpack(file); | |
next(); | |
} | |
}; | |
if (!(this.enabled() && cnt && mimes.length)) { | |
return dfrd.reject(); | |
} | |
if(cnt > 0) { | |
confirm(files, 0); | |
} | |
return dfrd; | |
} | |
} | |
/* | |
* File: /js/commands/forward.js | |
*/ | |
/** | |
* @class elFinder command "forward" | |
* Open next visited folder | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.forward = function() { | |
this.alwaysEnabled = true; | |
this.updateOnSelect = true; | |
this.shortcuts = [{ | |
pattern : 'ctrl+right' | |
}]; | |
this.getstate = function() { | |
return this.fm.history.canForward() ? 0 : -1; | |
} | |
this.exec = function() { | |
return this.fm.history.forward(); | |
} | |
} | |
/* | |
* File: /js/commands/getfile.js | |
*/ | |
/** | |
* @class elFinder command "getfile". | |
* Return selected files info into outer callback. | |
* For use elFinder with wysiwyg editors etc. | |
* | |
* @author Dmitry (dio) Levashov, dio@std42.ru | |
**/ | |
elFinder.prototype.commands.getfile = function() { | |
var self = this, | |
fm = this.fm, | |
filter = function(files) { | |
var o = self.options; | |
files = $.map(files, function(file) { | |
return file.mime != 'directory' || o.folders ? file : null; | |
}); | |
return o.multiple || files.length == 1 ? files : []; | |
}; | |
this.alwaysEnabled = true; | |
this.callback = fm.options.getFileCallback; | |
this._disabled = typeof(this.callback) == 'function'; | |
this.getstate = function(sel) { | |
var sel = this.files(sel), | |
cnt = sel.length; | |
return this.callback && cnt && filter(sel).length == cnt ? 0 : -1; | |
} | |
this.exec = function(hashes) { | |
var fm = this.fm, | |
opts = this.options, | |
files = this.files(hashes), | |
cnt = files.length, | |
url = fm.option('url'), | |
tmb = fm.option('tmbUrl'), | |
dfrd = $.Deferred() | |
.done(function(data) { | |
fm.trigger('getfile', {files : data}); | |
self.callback(data, fm); | |
if (opts.oncomplete == 'close') { | |
fm.hide(); | |
} else if (opts.oncomplete == 'destroy') { | |
fm.destroy(); | |
} | |
}), | |
result = function(file) { | |
return opts.onlyURL | |
? opts.multiple ? $.map(files, function(f) { return f.url; }) : files[0].url | |
: opts.multiple ? files : files[0]; | |
}, | |
req = [], | |
i, file, dim; | |
if (this.getstate(hashes) == -1) { | |
return dfrd.reject(); | |
} | |
for (i = 0; i < cnt; i++) { | |
file = files[i]; | |
if (file.mime == 'directory' && !opts.folders) { | |
return dfrd.reject(); | |
} | |
file.baseUrl = url; | |
if (file.url == '1') { | |
req.push(fm.request({ | |
data : {cmd : 'url', target : file.hash}, | |
notify : {type : 'url', cnt : 1, hideCnt : true}, | |
preventDefault : true | |
}) | |
.done(function(data) { | |
if (data.url) { | |
var rfile = fm.file(this.hash); | |
rfile.url = this.url = data.url; | |
} | |
}.bind(file))); | |
} else { | |
file.url = fm.url(file.hash); | |
} | |
file.path = fm.path(file.hash); | |
if (file.tmb && file.tmb != 1) { | |
file.tmb = tmb + file.tmb; | |
} | |
if (!file.width && !file.height) { | |
if (file.dim) { | |
dim = file.dim.split('x'); | |
file.width = dim[0]; | |
file.height = dim[1]; | |
} else if (file.mime.indexOf('image') !== -1) { | |
req.push(fm.request({ | |
data : {cmd : 'dim', target : file.hash}, | |
notify : {type : 'dim', cnt : 1, hideCnt : true}, | |
preventDefault : true | |
}) | |
.done(function(data) { | |
if (data.dim) { | |
var dim = data.dim.split('x'); | |
var rfile = fm.file(this.hash); | |
rfile.width = this.width = dim[0]; | |
rfile.height = this.height = dim[1]; | |
} | |
}.bind(file))); | |
} | |
} | |
} | |
if (req.length) { | |
$.when.apply(null, req).always(function() { | |
dfrd.resolve(result(files)); | |
}) | |
return dfrd; | |
} | |
return dfrd.resolve(result(files)); | |
} | |
} | |
/* | |
* File: /js/commands/help.js | |
*/ | |
/** | |
* @class elFinder command "help" | |
* "About" dialog | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.help = function() { | |
var fm = this.fm, | |
self = this, | |
linktpl = '<div class="elfinder-help-link"> <a href="{url}">{link}</a></div>', | |
linktpltgt = '<div class="elfinder-help-link"> <a href="{url}" target="_blank">{link}</a></div>', | |
atpl = '<div class="elfinder-help-team"><div>{author}</div>{work}</div>', | |
url = /\{url\}/, | |
link = /\{link\}/, | |
author = /\{author\}/, | |
work = /\{work\}/, | |
r = 'replace', | |
prim = 'ui-priority-primary', | |
sec = 'ui-priority-secondary', | |
lic = 'elfinder-help-license', | |
tab = '<li class="ui-state-default ui-corner-top"><a href="#{id}">{title}</a></li>', | |
html = ['<div class="ui-tabs ui-widget ui-widget-content ui-corner-all elfinder-help">', | |
'<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all">'], | |
stpl = '<div class="elfinder-help-shortcut"><div class="elfinder-help-shortcut-pattern">{pattern}</div> {descrip}</div>', | |
sep = '<div class="elfinder-help-separator"/>', | |
about = function() { | |
html.push('<div id="about" class="ui-tabs-panel ui-widget-content ui-corner-bottom"><div class="elfinder-help-logo"/>'); | |
html.push('<h3>elFinder</h3>'); | |
html.push('<div class="'+prim+'">'+fm.i18n('webfm')+'</div>'); | |
html.push('<div class="'+sec+'">'+fm.i18n('ver')+': '+fm.version+', '+fm.i18n('protocolver')+': <span id="apiver"></span></div>'); | |
html.push('<div class="'+sec+'">jQuery/jQuery UI: '+$().jquery+'/'+$.ui.version+'</div>'); | |
html.push(sep); | |
html.push(linktpltgt[r](url, 'http://elfinder.org/')[r](link, fm.i18n('homepage'))); | |
html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder/wiki')[r](link, fm.i18n('docs'))); | |
html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder')[r](link, fm.i18n('github'))); | |
html.push(linktpltgt[r](url, 'http://twitter.com/elrte_elfinder')[r](link, fm.i18n('twitter'))); | |
html.push(sep); | |
html.push('<div class="'+prim+'">'+fm.i18n('team')+'</div>'); | |
html.push(atpl[r](author, 'Dmitry "dio" Levashov <dio@std42.ru>')[r](work, fm.i18n('chiefdev'))); | |
html.push(atpl[r](author, 'Troex Nevelin <troex@fury.scancode.ru>')[r](work, fm.i18n('maintainer'))); | |
html.push(atpl[r](author, 'Alexey Sukhotin <strogg@yandex.ru>')[r](work, fm.i18n('contributor'))); | |
html.push(atpl[r](author, 'Naoki Sawada <hypweb@gmail.com>')[r](work, fm.i18n('contributor'))); | |
fm.i18[fm.lang].translator && html.push(atpl[r](author, fm.i18[fm.lang].translator)[r](work, fm.i18n('translator')+' ('+fm.i18[fm.lang].language+')')); | |
html.push(sep); | |
html.push('<div class="'+lic+'">'+fm.i18n('icons')+': Pixelmixer, <a href="http://p.yusukekamiyamane.com" target="_blank">Fugue</a></div>'); | |
html.push(sep); | |
html.push('<div class="'+lic+'">Licence: BSD Licence</div>'); | |
html.push('<div class="'+lic+'">Copyright © 2009-2016, Studio 42</div>'); | |
html.push('<div class="'+lic+'">„ …'+fm.i18n('dontforget')+' ”</div>'); | |
html.push('</div>'); | |
}, | |
shortcuts = function() { | |
var sh = fm.shortcuts(); | |
// shortcuts tab | |
html.push('<div id="shortcuts" class="ui-tabs-panel ui-widget-content ui-corner-bottom">'); | |
if (sh.length) { | |
html.push('<div class="ui-widget-content elfinder-help-shortcuts">'); | |
$.each(sh, function(i, s) { | |
html.push(stpl.replace(/\{pattern\}/, s[0]).replace(/\{descrip\}/, s[1])); | |
}); | |
html.push('</div>'); | |
} else { | |
html.push('<div class="elfinder-help-disabled">'+fm.i18n('shortcutsof')+'</div>'); | |
} | |
html.push('</div>'); | |
}, | |
help = function() { | |
// help tab | |
html.push('<div id="help" class="ui-tabs-panel ui-widget-content ui-corner-bottom">'); | |
html.push('<a href="http://elfinder.org/forum/" target="_blank" class="elfinder-dont-panic"><span>DON\'T PANIC</span></a>'); | |
html.push('</div>'); | |
// end help | |
}, | |
content = ''; | |
this.alwaysEnabled = true; | |
this.updateOnSelect = false; | |
this.state = 0; | |
this.shortcuts = [{ | |
pattern : 'f1', | |
description : this.title | |
}]; | |
setTimeout(function() { | |
var parts = self.options.view || ['about', 'shortcuts', 'help']; | |
$.each(parts, function(i, title) { | |
html.push(tab[r](/\{id\}/, title)[r](/\{title\}/, fm.i18n(title))); | |
}); | |
html.push('</ul>'); | |
$.inArray('about', parts) !== -1 && about(); | |
$.inArray('shortcuts', parts) !== -1 && shortcuts(); | |
$.inArray('help', parts) !== -1 && help(); | |
html.push('</div>'); | |
content = $(html.join('')); | |
content.find('.ui-tabs-nav li') | |
.hover(function() { | |
$(this).toggleClass('ui-state-hover'); | |
}) | |
.children() | |
.click(function(e) { | |
var link = $(this); | |
e.preventDefault(); | |
e.stopPropagation(); | |
if (!link.hasClass('ui-tabs-selected')) { | |
link.parent().addClass('ui-tabs-selected ui-state-active').siblings().removeClass('ui-tabs-selected').removeClass('ui-state-active'); | |
content.find('.ui-tabs-panel').hide().filter(link.attr('href')).show(); | |
} | |
}) | |
.filter(':first').click(); | |
}, 200); | |
this.getstate = function() { | |
return 0; | |
}; | |
this.exec = function() { | |
if (!this.dialog) { | |
content.find('#apiver').text(this.fm.api); | |
this.dialog = this.fm.dialog(content, {title : this.title, width : 530, autoOpen : false, destroyOnClose : false}); | |
} | |
this.dialog.elfinderdialog('open').find('.ui-tabs-nav li a:first').click(); | |
}; | |
}; | |
/* | |
* File: /js/commands/home.js | |
*/ | |
elFinder.prototype.commands.home = function() { | |
this.title = 'Home'; | |
this.alwaysEnabled = true; | |
this.updateOnSelect = false; | |
this.shortcuts = [{ | |
pattern : 'ctrl+home ctrl+shift+up', | |
description : 'Home' | |
}]; | |
this.getstate = function() { | |
var root = this.fm.root(), | |
cwd = this.fm.cwd().hash; | |
return root && cwd && root != cwd ? 0: -1; | |
} | |
this.exec = function() { | |
return this.fm.exec('open', this.fm.root()); | |
} | |
} | |
/* | |
* File: /js/commands/info.js | |
*/ | |
/** | |
* @class elFinder command "info". | |
* Display dialog with file properties. | |
* | |
* @author Dmitry (dio) Levashov, dio@std42.ru | |
**/ | |
elFinder.prototype.commands.info = function() { | |
var m = 'msg', | |
fm = this.fm, | |
spclass = 'elfinder-info-spinner', | |
btnclass = 'elfinder-info-button', | |
msg = { | |
calc : fm.i18n('calc'), | |
size : fm.i18n('size'), | |
unknown : fm.i18n('unknown'), | |
path : fm.i18n('path'), | |
aliasfor : fm.i18n('aliasfor'), | |
modify : fm.i18n('modify'), | |
perms : fm.i18n('perms'), | |
locked : fm.i18n('locked'), | |
dim : fm.i18n('dim'), | |
kind : fm.i18n('kind'), | |
files : fm.i18n('files'), | |
folders : fm.i18n('folders'), | |
items : fm.i18n('items'), | |
yes : fm.i18n('yes'), | |
no : fm.i18n('no'), | |
link : fm.i18n('link'), | |
owner : fm.i18n('owner'), | |
group : fm.i18n('group'), | |
perm : fm.i18n('perm'), | |
getlink : fm.i18n('getLink') | |
}; | |
this.tpl = { | |
main : '<div class="ui-helper-clearfix elfinder-info-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}</div><table class="elfinder-info-tb">{content}</table>', | |
itemTitle : '<strong>{name}</strong><span class="elfinder-info-kind">{kind}</span>', | |
groupTitle : '<strong>{items}: {num}</strong>', | |
row : '<tr><td>{label} : </td><td>{value}</td></tr>', | |
spinner : '<span>{text}</span> <span class="'+spclass+' '+spclass+'-{name}"/>' | |
}; | |
this.alwaysEnabled = true; | |
this.updateOnSelect = false; | |
this.shortcuts = [{ | |
pattern : 'ctrl+i' | |
}]; | |
this.init = function() { | |
$.each(msg, function(k, v) { | |
msg[k] = fm.i18n(v); | |
}); | |
}; | |
this.getstate = function() { | |
return 0; | |
}; | |
this.exec = function(hashes) { | |
var files = this.files(hashes); | |
if (! files.length) { | |
files = this.files([ this.fm.cwd().hash ]); | |
} | |
var self = this, | |
fm = this.fm, | |
o = this.options, | |
tpl = this.tpl, | |
row = tpl.row, | |
cnt = files.length, | |
content = [], | |
view = tpl.main, | |
l = '{label}', | |
v = '{value}', | |
reqs = [], | |
opts = { | |
title : this.title, | |
width : 'auto', | |
close : function() { | |
$(this).elfinderdialog('destroy'); | |
$.each(reqs, function(i, req) { | |
var xhr = (req && req.xhr)? req.xhr : null; | |
if (xhr && xhr.state() == 'pending') { | |
xhr.quiet = true; | |
xhr.abort(); | |
} | |
}); | |
} | |
}, | |
count = [], | |
replSpinner = function(msg, name) { dialog.find('.'+spclass+'-'+name).parent().html(msg); }, | |
id = fm.namespace+'-info-'+$.map(files, function(f) { return f.hash; }).join('-'), | |
dialog = fm.getUI().find('#'+id), | |
customActions = [], | |
size, tmb, file, title, dcnt; | |
if (!cnt) { | |
return $.Deferred().reject(); | |
} | |
if (dialog.length) { | |
dialog.elfinderdialog('toTop'); | |
return $.Deferred().resolve(); | |
} | |
if (cnt == 1) { | |
file = files[0]; | |
view = view.replace('{class}', fm.mime2class(file.mime)); | |
title = tpl.itemTitle.replace('{name}', fm.escape(file.i18 || file.name)).replace('{kind}', '<span title="'+fm.escape(file.mime)+'">'+fm.mime2kind(file)+'</span>'); | |
if (file.tmb) { | |
tmb = fm.option('tmbUrl')+file.tmb; | |
} | |
if (!file.read) { | |
size = msg.unknown; | |
} else if (file.mime != 'directory' || file.alias) { | |
size = fm.formatSize(file.size); | |
} else { | |
size = tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size'); | |
count.push(file.hash); | |
} | |
content.push(row.replace(l, msg.size).replace(v, size)); | |
file.alias && content.push(row.replace(l, msg.aliasfor).replace(v, file.alias)); | |
content.push(row.replace(l, msg.path).replace(v, fm.escape(fm.path(file.hash, true)))); | |
if (file.read) { | |
var href, | |
name_esc = fm.escape(file.name); | |
if (file.url == '1') { | |
content.push(row.replace(l, msg.link).replace(v, '<button class="'+btnclass+' '+spclass+'-url">'+msg.getlink+'</button>')); | |
} else { | |
if (o.nullUrlDirLinkSelf && file.mime == 'directory' && file.url === null) { | |
var loc = window.location; | |
href = loc.pathname + loc.search + '#elf_' + file.hash; | |
} else { | |
href = fm.url(file.hash); | |
} | |
content.push(row.replace(l, msg.link).replace(v, '<a href="'+href+'" target="_blank">'+name_esc+'</a>')); | |
} | |
} | |
if (file.dim) { // old api | |
content.push(row.replace(l, msg.dim).replace(v, file.dim)); | |
} else if (file.mime.indexOf('image') !== -1) { | |
if (file.width && file.height) { | |
content.push(row.replace(l, msg.dim).replace(v, file.width+'x'+file.height)); | |
} else { | |
content.push(row.replace(l, msg.dim).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'dim'))); | |
reqs.push(fm.request({ | |
data : {cmd : 'dim', target : file.hash}, | |
preventDefault : true | |
}) | |
.fail(function() { | |
replSpinner(msg.unknown, 'dim'); | |
}) | |
.done(function(data) { | |
replSpinner(data.dim || msg.unknown, 'dim'); | |
if (data.dim) { | |
var dim = data.dim.split('x'); | |
var rfile = fm.file(file.hash); | |
rfile.width = dim[0]; | |
rfile.height = dim[1]; | |
} | |
})); | |
} | |
} | |
content.push(row.replace(l, msg.modify).replace(v, fm.formatDate(file))); | |
content.push(row.replace(l, msg.perms).replace(v, fm.formatPermissions(file))); | |
content.push(row.replace(l, msg.locked).replace(v, file.locked ? msg.yes : msg.no)); | |
file.owner && content.push(row.replace(l, msg.owner).replace(v, file.owner)); | |
file.group && content.push(row.replace(l, msg.group).replace(v, file.group)); | |
file.perm && content.push(row.replace(l, msg.perm).replace(v, fm.formatFileMode(file.perm))); | |
// Add custom info fields | |
if (o.custom) { | |
$.each(o.custom, function(name, details) { | |
if ( | |
(!details.mimes || $.map(details.mimes, function(m){return (file.mime === m || file.mime.indexOf(m+'/') === 0)? true : null;}).length) | |
&& | |
(!details.hashRegex || file.hash.match(details.hashRegex)) | |
) { | |
// Add to the content | |
content.push(row.replace(l, fm.i18n(details.label)).replace(v , details.tpl.replace('{id}', id))); | |
// Register the action | |
if (details.action && (typeof details.action == 'function')) { | |
customActions.push(details.action); | |
} | |
} | |
}); | |
} | |
} else { | |
view = view.replace('{class}', 'elfinder-cwd-icon-group'); | |
title = tpl.groupTitle.replace('{items}', msg.items).replace('{num}', cnt); | |
dcnt = $.map(files, function(f) { return f.mime == 'directory' ? 1 : null ; }).length; | |
if (!dcnt) { | |
size = 0; | |
$.each(files, function(h, f) { | |
var s = parseInt(f.size); | |
if (s >= 0 && size >= 0) { | |
size += s; | |
} else { | |
size = 'unknown'; | |
} | |
}); | |
content.push(row.replace(l, msg.kind).replace(v, msg.files)); | |
content.push(row.replace(l, msg.size).replace(v, fm.formatSize(size))); | |
} else { | |
content.push(row.replace(l, msg.kind).replace(v, dcnt == cnt ? msg.folders : msg.folders+' '+dcnt+', '+msg.files+' '+(cnt-dcnt))); | |
content.push(row.replace(l, msg.size).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size'))); | |
count = $.map(files, function(f) { return f.hash; }); | |
} | |
} | |
view = view.replace('{title}', title).replace('{content}', content.join('')); | |
dialog = fm.dialog(view, opts); | |
dialog.attr('id', id); | |
if (file && file.url == '1') { | |
dialog.on('click', '.'+spclass+'-url', function(){ | |
$(this).parent().html(tpl.spinner.replace('{text}', fm.i18n('ntfurl')).replace('{name}', 'url')); | |
fm.request({ | |
data : {cmd : 'url', target : file.hash}, | |
preventDefault : true | |
}) | |
.fail(function() { | |
replSpinner(name_esc, 'url'); | |
}) | |
.done(function(data) { | |
if (data.url) { | |
replSpinner('<a href="'+data.url+'" target="_blank">'+name_esc+'</a>' || name_esc, 'url'); | |
var rfile = fm.file(file.hash); | |
rfile.url = data.url; | |
} else { | |
replSpinner(name_esc, 'url'); | |
} | |
}); | |
}); | |
} | |
// load thumbnail | |
if (tmb) { | |
$('<img/>') | |
.load(function() { dialog.find('.elfinder-cwd-icon').css('background', 'url("'+tmb+'") center center no-repeat'); }) | |
.attr('src', tmb); | |
} | |
// send request to count total size | |
if (count.length) { | |
reqs.push(fm.request({ | |
data : {cmd : 'size', targets : count}, | |
preventDefault : true | |
}) | |
.fail(function() { | |
replSpinner(msg.unknown, 'size'); | |
}) | |
.done(function(data) { | |
var size = parseInt(data.size); | |
replSpinner(size >= 0 ? fm.formatSize(size) : msg.unknown, 'size'); | |
}) | |
); | |
} | |
// call custom actions | |
if (customActions.length) { | |
$.each(customActions, function(i, action) { | |
try { | |
action(file, fm, dialog); | |
} catch(e) { | |
fm.debug('error', e); | |
} | |
}); | |
} | |
}; | |
}; | |
/* | |
* File: /js/commands/mkdir.js | |
*/ | |
/** | |
* @class elFinder command "mkdir" | |
* Create new folder | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.mkdir = function() { | |
var fm = this.fm, | |
self = this; | |
this.value = ''; | |
this.disableOnSearch = true; | |
this.updateOnSelect = false; | |
this.mime = 'directory'; | |
this.prefix = 'untitled folder'; | |
this.exec = $.proxy(fm.res('mixin', 'make'), this); | |
this.shortcuts = [{ | |
pattern : 'ctrl+shift+n' | |
}]; | |
this.options = { ui : 'mkdirbutton' }; | |
fm.bind('select', function(e) { | |
var sel = (e.data && e.data.selected)? e.data.selected : []; | |
self.title = sel.length? fm.i18n('cmdmkdirin') : fm.i18n('cmdmkdir'); | |
self.update(void(0), self.title); | |
}); | |
this.getstate = function(sel) { | |
var cwd = fm.cwd(), | |
sel = (sel && sel[0] != cwd.hash)? this.files(sel) : [], | |
cnt = sel.length; | |
return !this._disabled && cwd.write && (!cnt || $.map(sel, function(f) { return f.phash && f.read && !f.locked ? f : null }).length == cnt)? 0 : -1; | |
} | |
} | |
/* | |
* File: /js/commands/mkfile.js | |
*/ | |
/** | |
* @class elFinder command "mkfile" | |
* Create new empty file | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.mkfile = function() { | |
this.disableOnSearch = true; | |
this.updateOnSelect = false; | |
this.mime = 'text/plain'; | |
this.prefix = 'untitled file.txt'; | |
this.exec = $.proxy(this.fm.res('mixin', 'make'), this); | |
this.getstate = function() { | |
return !this._disabled && this.fm.cwd().write ? 0 : -1; | |
} | |
} | |
/* | |
* File: /js/commands/netmount.js | |
*/ | |
/** | |
* @class elFinder command "netmount" | |
* Mount network volume with user credentials. | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.netmount = function() { | |
var self = this; | |
this.alwaysEnabled = true; | |
this.updateOnSelect = false; | |
this.drivers = []; | |
this.handlers = { | |
load : function() { | |
this.drivers = this.fm.netDrivers; | |
} | |
} | |
this.getstate = function() { | |
return this.drivers.length ? 0 : -1; | |
} | |
this.exec = function() { | |
var fm = self.fm, | |
dfrd = $.Deferred(), | |
o = self.options, | |
create = function() { | |
var inputs = { | |
protocol : $('<select/>').change(function(){ | |
var protocol = this.value; | |
content.find('.elfinder-netmount-tr').hide(); | |
content.find('.elfinder-netmount-tr-'+protocol).show(); | |
if (typeof o[protocol].select == 'function') { | |
o[protocol].select(fm); | |
} | |
}) | |
}, | |
opts = { | |
title : fm.i18n('netMountDialogTitle'), | |
resizable : false, | |
modal : true, | |
destroyOnClose : true, | |
close : function() { | |
delete self.dialog; | |
dfrd.state() == 'pending' && dfrd.reject(); | |
}, | |
buttons : {} | |
}, | |
content = $('<table class="elfinder-info-tb elfinder-netmount-tb"/>'), | |
hidden = $('<div/>'), | |
dialog; | |
content.append($('<tr/>').append($('<td>'+fm.i18n('protocol')+'</td>')).append($('<td/>').append(inputs.protocol))); | |
$.each(self.drivers, function(i, protocol) { | |
inputs.protocol.append('<option value="'+protocol+'">'+fm.i18n(protocol)+'</option>'); | |
$.each(o[protocol].inputs, function(name, input) { | |
input.attr('name', name); | |
if (input.attr('type') != 'hidden') { | |
input.addClass('ui-corner-all elfinder-netmount-inputs-'+protocol); | |
content.append($('<tr/>').addClass('elfinder-netmount-tr elfinder-netmount-tr-'+protocol).append($('<td>'+fm.i18n(name)+'</td>')).append($('<td/>').append(input))); | |
} else { | |
input.addClass('elfinder-netmount-inputs-'+protocol); | |
hidden.append(input); | |
} | |
}); | |
}); | |
content.append(hidden); | |
content.find('.elfinder-netmount-tr').hide(); | |
opts.buttons[fm.i18n('btnMount')] = function() { | |
var protocol = inputs.protocol.val(); | |
var data = {cmd : 'netmount', protocol: protocol}; | |
$.each(content.find('input.elfinder-netmount-inputs-'+protocol), function(name, input) { | |
var val; | |
if (typeof input.val == 'function') { | |
val = $.trim(input.val()); | |
} else { | |
val = $.trim(input.value); | |
} | |
if (val) { | |
data[input.name] = val; | |
} | |
}); | |
if (!data.host) { | |
return fm.trigger('error', {error : 'errNetMountHostReq'}); | |
} | |
fm.request({data : data, notify : {type : 'netmount', cnt : 1, hideCnt : true}}) | |
.done(function(data) { | |
data.added && data.added.length && fm.exec('open', data.added[0].hash); | |
dfrd.resolve(); | |
}) | |
.fail(function(error) { dfrd.reject(error); }); | |
self.dialog.elfinderdialog('close'); | |
}; | |
opts.buttons[fm.i18n('btnCancel')] = function() { | |
self.dialog.elfinderdialog('close'); | |
}; | |
dialog = fm.dialog(content, opts); | |
dialog.ready(function(){ | |
inputs.protocol.change(); | |
dialog.elfinderdialog('posInit'); | |
}); | |
return dialog; | |
} | |
; | |
fm.bind('netmount', function(e) { | |
var d = e.data || null; | |
if (d && d.protocol) { | |
if (o[d.protocol] && typeof o[d.protocol].done == 'function') { | |
o[d.protocol].done(fm, d); | |
} | |
} | |
}); | |
if (!self.dialog) { | |
self.dialog = create(); | |
} | |
return dfrd.promise(); | |
} | |
} | |
elFinder.prototype.commands.netunmount = function() { | |
var self = this; | |
this.alwaysEnabled = true; | |
this.updateOnSelect = false; | |
this.drivers = []; | |
this.handlers = { | |
load : function() { | |
this.drivers = this.fm.netDrivers; | |
} | |
}; | |
this.getstate = function(sel) { | |
var fm = this.fm; | |
return !!sel && this.drivers.length && !this._disabled && fm.file(sel[0]).netkey ? 0 : -1; | |
}; | |
this.exec = function(hashes) { | |
var self = this, | |
fm = this.fm, | |
dfrd = $.Deferred() | |
.fail(function(error) { | |
error && fm.error(error); | |
}), | |
drive = fm.file(hashes[0]); | |
if (this._disabled) { | |
return dfrd.reject(); | |
} | |
if (dfrd.state() == 'pending') { | |
fm.confirm({ | |
title : self.title, | |
text : fm.i18n('confirmUnmount', drive.name), | |
accept : { | |
label : 'btnUnmount', | |
callback : function() { | |
fm.request({ | |
data : {cmd : 'netmount', protocol : 'netunmount', host: drive.netkey, user : drive.hash, pass : 'dum'}, | |
notify : {type : 'netunmount', cnt : 1, hideCnt : true}, | |
preventFail : true | |
}) | |
.fail(function(error) { | |
dfrd.reject(error); | |
}) | |
.done(function(data) { | |
var chDrive = (fm.root() == drive.hash); | |
data.removed = [ drive.hash ]; | |
fm.remove(data); | |
if (chDrive) { | |
var files = fm.files(); | |
for (var i in files) { | |
if (fm.file(i).mime == 'directory') { | |
fm.exec('open', i); | |
break; | |
} | |
} | |
} | |
dfrd.resolve(); | |
}); | |
} | |
}, | |
cancel : { | |
label : 'btnCancel', | |
callback : function() { dfrd.reject(); } | |
} | |
}); | |
} | |
return dfrd; | |
}; | |
}; | |
/* | |
* File: /js/commands/open.js | |
*/ | |
/** | |
* @class elFinder command "open" | |
* Enter folder or open files in new windows | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.open = function() { | |
this.alwaysEnabled = true; | |
this._handlers = { | |
dblclick : function(e) { e.preventDefault(); this.exec() }, | |
'select enable disable reload' : function(e) { this.update(e.type == 'disable' ? -1 : void(0)); } | |
} | |
this.shortcuts = [{ | |
pattern : 'ctrl+down numpad_enter'+(this.fm.OS != 'mac' && ' enter') | |
}]; | |
this.getstate = function(sel) { | |
var sel = this.files(sel), | |
cnt = sel.length; | |
return cnt == 1 | |
? 0 | |
: (cnt && !this.fm.UA.Mobile) ? ($.map(sel, function(file) { return file.mime == 'directory' ? null : file}).length == cnt ? 0 : -1) : -1 | |
} | |
this.exec = function(hashes, opts) { | |
var fm = this.fm, | |
dfrd = $.Deferred().fail(function(error) { error && fm.error(error); }), | |
files = this.files(hashes), | |
cnt = files.length, | |
thash = (typeof opts == 'object')? opts.thash : false, | |
file, url, s, w, imgW, imgH, winW, winH, reg, link, html5dl, inline; | |
if (!cnt && !thash) { | |
{ | |
return dfrd.reject(); | |
} | |
} | |
// open folder | |
if (thash || (cnt == 1 && (file = files[0]) && file.mime == 'directory')) { | |
return !thash && file && !file.read | |
? dfrd.reject(['errOpen', file.name, 'errPerm']) | |
: fm.request({ | |
data : {cmd : 'open', target : thash || file.hash}, | |
notify : {type : 'open', cnt : 1, hideCnt : true}, | |
syncOnFail : true | |
}); | |
} | |
files = $.map(files, function(file) { return file.mime != 'directory' ? file : null }); | |
// nothing to open or files and folders selected - do nothing | |
if (cnt != files.length) { | |
return dfrd.reject(); | |
} | |
try { | |
reg = new RegExp(fm.option('dispInlineRegex')); | |
} catch(e) { | |
reg = false; | |
} | |
// open files | |
link = $('<a>').hide().appendTo($('body')), | |
html5dl = (typeof link.get(0).download === 'string'); | |
cnt = files.length; | |
while (cnt--) { | |
file = files[cnt]; | |
if (!file.read) { | |
return dfrd.reject(['errOpen', file.name, 'errPerm']); | |
} | |
inline = (reg && file.mime.match(reg)); | |
url = fm.openUrl(file.hash, !inline); | |
if (fm.UA.Mobile || !inline) { | |
if (html5dl) { | |
!inline && link.attr('download', file.name); | |
link.attr('href', url) | |
.attr('target', '_blank') | |
.get(0).click(); | |
} else { | |
var wnd = window.open(url); | |
if (!wnd) { | |
return dfrd.reject('errPopup'); | |
} | |
} | |
} else { | |
// set window size for image if set | |
imgW = winW = Math.round(2 * $(window).width() / 3); | |
imgH = winH = Math.round(2 * $(window).height() / 3); | |
if (parseInt(file.width) && parseInt(file.height)) { | |
imgW = parseInt(file.width); | |
imgH = parseInt(file.height); | |
} else if (file.dim) { | |
s = file.dim.split('x'); | |
imgW = parseInt(s[0]); | |
imgH = parseInt(s[1]); | |
} | |
if (winW >= imgW && winH >= imgH) { | |
winW = imgW; | |
winH = imgH; | |
} else { | |
if ((imgW - winW) > (imgH - winH)) { | |
winH = Math.round(imgH * (winW / imgW)); | |
} else { | |
winW = Math.round(imgW * (winH / imgH)); | |
} | |
} | |
w = 'width='+winW+',height='+winH; | |
if (url.indexOf(fm.options.url) === 0) { | |
url = ''; | |
} | |
var wnd = window.open(url, 'new_window', w + ',top=50,left=50,scrollbars=yes,resizable=yes'); | |
if (!wnd) { | |
return dfrd.reject('errPopup'); | |
} | |
if (url === '') { | |
var form = document.createElement("form"); | |
form.action = fm.options.url; | |
form.method = 'POST'; | |
form.target = 'new_window'; | |
form.style.display = 'none'; | |
var params = $.extend({}, fm.options.customData, { | |
cmd: 'file', | |
target: file.hash | |
}); | |
$.each(params, function(key, val) | |
{ | |
var input = document.createElement("input"); | |
input.name = key; | |
input.value = val; | |
form.appendChild(input); | |
}); | |
document.body.appendChild(form); | |
form.submit(); | |
} | |
wnd.focus(); | |
} | |
} | |
link.remove(); | |
return dfrd.resolve(hashes); | |
} | |
} | |
/* | |
* File: /js/commands/opendir.js | |
*/ | |
/** | |
* @class elFinder command "opendir" | |
* Enter parent folder | |
* | |
* @author Naoki Sawada | |
**/ | |
elFinder.prototype.commands.opendir = function() { | |
this.alwaysEnabled = true; | |
this.getstate = function() { | |
var sel = this.fm.selected(), | |
cnt = sel.length, | |
cwdWrapper; | |
if (cnt !== 1) { | |
return -1; | |
} | |
cwdWrapper = this.fm.getUI('cwd').parent(); | |
return cwdWrapper.hasClass('elfinder-search-result')? 0 : -1; | |
} | |
this.exec = function(hashes) { | |
var fm = this.fm, | |
dfrd = $.Deferred(), | |
files = this.files(hashes), | |
cnt = files.length, | |
hash, pcheck = null; | |
if (!cnt || !files[0].phash) { | |
return dfrd.reject(); | |
} | |
hash = files[0].phash; | |
if (!fm.file(hash)) { | |
// parents check | |
pcheck = fm.request({ | |
data : {cmd : 'parents', target : hash}, | |
syncOnFail : false | |
}); | |
} | |
// open folder | |
$.when(pcheck) | |
.done(function(data){ | |
fm.trigger('searchend', { noupdate: true }); | |
fm.request({ | |
data : {cmd : 'open', target : hash}, | |
notify : {type : 'open', cnt : 1, hideCnt : true}, | |
syncOnFail : false | |
}); | |
}); | |
return dfrd; | |
} | |
} | |
/* | |
* File: /js/commands/paste.js | |
*/ | |
/** | |
* @class elFinder command "paste" | |
* Paste filesfrom clipboard into directory. | |
* If files pasted in its parent directory - files duplicates will created | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.paste = function() { | |
this.updateOnSelect = false; | |
this.handlers = { | |
changeclipboard : function() { this.update(); } | |
} | |
this.shortcuts = [{ | |
pattern : 'ctrl+v shift+insert' | |
}]; | |
this.getstate = function(dst) { | |
if (this._disabled) { | |
return -1; | |
} | |
if (dst) { | |
if ($.isArray(dst)) { | |
if (dst.length != 1) { | |
return -1; | |
} | |
dst = this.fm.file(dst[0]); | |
} | |
} else { | |
dst = this.fm.cwd(); | |
} | |
return this.fm.clipboard().length && dst.mime == 'directory' && dst.write ? 0 : -1; | |
} | |
this.exec = function(dst) { | |
var self = this, | |
fm = self.fm, | |
dst = dst ? this.files(dst)[0] : fm.cwd(), | |
files = fm.clipboard(), | |
cnt = files.length, | |
cut = cnt ? files[0].cut : false, | |
error = cut ? 'errMove' : 'errCopy', | |
fpaste = [], | |
fcopy = [], | |
dfrd = $.Deferred() | |
.fail(function(error) { | |
error && fm.error(error); | |
}) | |
.always(function() { | |
fm.unlockfiles({files : $.map(files, function(f) { return f.hash})}); | |
}), | |
copy = function(files) { | |
return files.length && fm._commands.duplicate | |
? fm.exec('duplicate', files) | |
: $.Deferred().resolve(); | |
}, | |
paste = function(files) { | |
var dfrd = $.Deferred(), | |
existed = [], | |
hashes = {}, | |
intersect = function(files, names) { | |
var ret = [], | |
i = files.length; | |
while (i--) { | |
$.inArray(files[i].name, names) !== -1 && ret.unshift(i); | |
} | |
return ret; | |
}, | |
confirm = function(ndx) { | |
var i = existed[ndx], | |
file = files[i], | |
last = ndx == existed.length-1; | |
if (!file) { | |
return; | |
} | |
fm.confirm({ | |
title : fm.i18n(cut ? 'moveFiles' : 'copyFiles'), | |
text : ['errExists', file.name, 'confirmRepl'], | |
all : !last, | |
accept : { | |
label : 'btnYes', | |
callback : function(all) { | |
!last && !all | |
? confirm(++ndx) | |
: paste(files); | |
} | |
}, | |
reject : { | |
label : 'btnNo', | |
callback : function(all) { | |
var i; | |
if (all) { | |
i = existed.length; | |
while (ndx < i--) { | |
files[existed[i]].remove = true | |
} | |
} else { | |
files[existed[ndx]].remove = true; | |
} | |
!last && !all | |
? confirm(++ndx) | |
: paste(files); | |
} | |
}, | |
cancel : { | |
label : 'btnCancel', | |
callback : function() { | |
dfrd.resolve(); | |
} | |
}, | |
buttons : [ | |
{ | |
label : 'btnBackup', | |
callback : function(all) { | |
var i; | |
if (all) { | |
i = existed.length; | |
while (ndx < i--) { | |
files[existed[i]].rename = true | |
} | |
} else { | |
files[existed[ndx]].rename = true; | |
} | |
!last && !all | |
? confirm(++ndx) | |
: paste(files); | |
} | |
} | |
] | |
}) | |
}, | |
valid = function(names) { | |
var exists = {}; | |
if (names) { | |
if ($.isArray(names)) { | |
if (names.length) { | |
if (typeof names[0] == 'string') { | |
// elFinder <= 2.1.6 command `is` results | |
existed = intersect(files, names); | |
} else { | |
$.each(names, function(i, v) { | |
exists[v.name] = v.hash; | |
}); | |
existed = intersect(files, $.map(exists, function(h, n) { return n; })); | |
$.each(files, function(i, file) { | |
if (exists[file.name]) { | |
hashes[exists[file.name]] = file.name; | |
} | |
}); | |
} | |
} | |
} else { | |
existed = intersect(files, $.map(names, function(n) { return n; })); | |
hashes = names; | |
} | |
} | |
existed.length ? confirm(0) : paste(files); | |
}, | |
paste = function(files) { | |
var renames = [], | |
files = $.map(files, function(file) { | |
if (file.rename) { | |
renames.push(file.name); | |
} | |
return !file.remove ? file : null; | |
}), | |
cnt = files.length, | |
groups = {}, | |
args = [], | |
src; | |
if (!cnt) { | |
return dfrd.resolve(); | |
} | |
src = files[0].phash; | |
files = $.map(files, function(f) { return f.hash; }); | |
fm.request({ | |
data : {cmd : 'paste', dst : dst.hash, targets : files, cut : cut ? 1 : 0, src : src, renames : renames, hashes : hashes, suffix : fm.options.backupSuffix}, | |
notify : {type : cut ? 'move' : 'copy', cnt : cnt} | |
}) | |
.done(function(data) { | |
dfrd.resolve(data); | |
if (data && data.added && data.added[0]) { | |
var newItem = fm.getUI('cwd').find('#'+fm.cwdHash2Id(data.added[0].hash)); | |
if (newItem.length) { | |
newItem.trigger('scrolltoview'); | |
} | |
} | |
}) | |
.always(function() { | |
fm.unlockfiles({files : files}); | |
}); | |
}, | |
internames; | |
if (!fm.isCommandEnabled(self.name, dst.hash) || !files.length) { | |
return dfrd.resolve(); | |
} | |
if (fm.oldAPI) { | |
paste(files); | |
} else { | |
if (!fm.option('copyOverwrite')) { | |
paste(files); | |
} else { | |
internames = $.map(files, function(f) { return f.name}); | |
dst.hash == fm.cwd().hash | |
? valid($.map(fm.files(), function(file) { return file.phash == dst.hash ? {hash: file.hash, name: file.name} : null })) | |
: fm.request({ | |
data : {cmd : 'ls', target : dst.hash, intersect : internames}, | |
notify : {type : 'prepare', cnt : 1, hideCnt : true}, | |
preventFail : true | |
}) | |
.always(function(data) { | |
valid(data.list); | |
}); | |
} | |
} | |
return dfrd; | |
}, | |
parents, fparents; | |
if (!cnt || !dst || dst.mime != 'directory') { | |
return dfrd.reject(); | |
} | |
if (!dst.write) { | |
return dfrd.reject([error, files[0].name, 'errPerm']); | |
} | |
parents = fm.parents(dst.hash); | |
$.each(files, function(i, file) { | |
if (!file.read) { | |
return !dfrd.reject([error, files[0].name, 'errPerm']); | |
} | |
if (cut && file.locked) { | |
return !dfrd.reject(['errLocked', file.name]); | |
} | |
if ($.inArray(file.hash, parents) !== -1) { | |
return !dfrd.reject(['errCopyInItself', file.name]); | |
} | |
fparents = fm.parents(file.hash); | |
fparents.pop(); | |
if ($.inArray(dst.hash, fparents) !== -1) { | |
if ($.map(fparents, function(h) { var d = fm.file(h); return d.phash == dst.hash && d.name == file.name ? d : null }).length) { | |
return !dfrd.reject(['errReplByChild', file.name]); | |
} | |
} | |
if (file.phash == dst.hash) { | |
fcopy.push(file.hash); | |
} else { | |
fpaste.push({ | |
hash : file.hash, | |
phash : file.phash, | |
name : file.name | |
}); | |
} | |
}); | |
if (dfrd.state() == 'rejected') { | |
return dfrd; | |
} | |
return $.when( | |
copy(fcopy), | |
paste(fpaste) | |
).always(function() { | |
cut && fm.clipboard([]); | |
}); | |
} | |
} | |
/* | |
* File: /js/commands/places.js | |
*/ | |
/** | |
* @class elFinder command "places" | |
* Regist to Places | |
* | |
* @author Naoki Sawada | |
**/ | |
elFinder.prototype.commands.places = function() { | |
var self = this, | |
fm = this.fm, | |
filter = function(hashes) { | |
return $.map(self.files(hashes), function(f) { return f.mime == 'directory' ? f : null; }); | |
}, | |
places = null; | |
this.getstate = function(sel) { | |
var sel = this.hashes(sel), | |
cnt = sel.length; | |
return places && cnt && cnt == filter(sel).length ? 0 : -1; | |
}; | |
this.exec = function(hashes) { | |
var files = this.files(hashes); | |
places.trigger('regist', [ files ]); | |
}; | |
fm.one('load', function(){ | |
places = fm.ui.places; | |
}); | |
}; | |
/* | |
* File: /js/commands/quicklook.js | |
*/ | |
/** | |
* @class elFinder command "quicklook" | |
* Fast preview for some files types | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.quicklook = function() { | |
var self = this, | |
fm = self.fm, | |
/** | |
* window closed state | |
* | |
* @type Number | |
**/ | |
closed = 0, | |
/** | |
* window animated state | |
* | |
* @type Number | |
**/ | |
animated = 1, | |
/** | |
* window opened state | |
* | |
* @type Number | |
**/ | |
opened = 2, | |
/** | |
* window state | |
* | |
* @type Number | |
**/ | |
state = closed, | |
/** | |
* next/prev event name (requied to cwd catch it) | |
* | |
* @type Number | |
**/ | |
// keydown = fm.UA.Firefox || fm.UA.Opera ? 'keypress' : 'keydown', | |
/** | |
* navbar icon class | |
* | |
* @type Number | |
**/ | |
navicon = 'elfinder-quicklook-navbar-icon', | |
/** | |
* navbar "fullscreen" icon class | |
* | |
* @type Number | |
**/ | |
fullscreen = 'elfinder-quicklook-fullscreen', | |
/** | |
* Triger keydown/keypress event with left/right arrow key code | |
* | |
* @param Number left/right arrow key code | |
* @return void | |
**/ | |
navtrigger = function(code) { | |
$(document).trigger($.Event('keydown', { keyCode: code, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false })); | |
}, | |
/** | |
* Return css for closed window | |
* | |
* @param jQuery file node in cwd | |
* @return void | |
**/ | |
closedCss = function(node) { | |
return { | |
opacity : 0, | |
width : 20,//node.width(), | |
height : fm.view == 'list' ? 1 : 20, | |
top : node.offset().top+'px', | |
left : node.offset().left+'px' | |
} | |
}, | |
/** | |
* Return css for opened window | |
* | |
* @return void | |
**/ | |
openedCss = function() { | |
var win = $(window); | |
var w = Math.min(width, $(window).width()-10); | |
var h = Math.min(height, $(window).height()-80); | |
return { | |
opacity : 1, | |
width : w, | |
height : h, | |
top : parseInt((win.height() - h - 60)/2 + win.scrollTop()), | |
left : parseInt((win.width() - w)/2 + win.scrollLeft()) | |
} | |
}, | |
support = function(codec) { | |
var media = document.createElement(codec.substr(0, codec.indexOf('/'))), | |
value = false; | |
try { | |
value = media.canPlayType && media.canPlayType(codec); | |
} catch (e) { | |
} | |
return value && value !== '' && value != 'no'; | |
}, | |
/** | |
* Opened window width (from config) | |
* | |
* @type Number | |
**/ | |
width, | |
/** | |
* Opened window height (from config) | |
* | |
* @type Number | |
**/ | |
height, | |
/** | |
* elFinder node | |
* | |
* @type jQuery | |
**/ | |
parent, | |
/** | |
* elFinder current directory node | |
* | |
* @type jQuery | |
**/ | |
cwd, | |
title = $('<div class="elfinder-quicklook-title"/>'), | |
icon = $('<div/>'), | |
info = $('<div class="elfinder-quicklook-info"/>'),//.hide(), | |
fsicon = $('<div class="'+navicon+' '+navicon+'-fullscreen"/>') | |
.mousedown(function(e) { | |
var win = self.window, | |
full = win.hasClass(fullscreen), | |
scroll = 'scroll.'+fm.namespace, | |
$window = $(window); | |
e.stopPropagation(); | |
if (full) { | |
win.css(win.data('position')).unbind('mousemove'); | |
$window.unbind(scroll).trigger(self.resize).unbind(self.resize); | |
navbar.unbind('mouseenter').unbind('mousemove'); | |
} else { | |
win.data('position', { | |
left : win.css('left'), | |
top : win.css('top'), | |
width : win.width(), | |
height : win.height() | |
}) | |
.css({ | |
width : '100%', | |
height : '100%' | |
}); | |
$(window).bind(scroll, function() { | |
win.css({ | |
left : parseInt($(window).scrollLeft())+'px', | |
top : parseInt($(window).scrollTop()) +'px' | |
}) | |
}) | |
.bind(self.resize, function(e) { | |
self.preview.trigger('changesize'); | |
}) | |
.trigger(scroll) | |
.trigger(self.resize); | |
win.bind('mousemove', function(e) { | |
navbar.stop(true, true).show().delay(3000).fadeOut('slow'); | |
}) | |
.mousemove(); | |
navbar.mouseenter(function() { | |
navbar.stop(true, true).show(); | |
}) | |
.mousemove(function(e) { | |
e.stopPropagation(); | |
}); | |
} | |
navbar.attr('style', '').draggable(full ? 'destroy' : {}); | |
win.toggleClass(fullscreen); | |
$(this).toggleClass(navicon+'-fullscreen-off'); | |
var collection = win; | |
if(parent.is('.ui-resizable')) { | |
collection = collection.add(parent); | |
}; | |
$.fn.resizable && !fm.UA.Touch && collection.resizable(full ? 'enable' : 'disable').removeClass('ui-state-disabled'); | |
}), | |
navbar = $('<div class="elfinder-quicklook-navbar"/>') | |
.append($('<div class="'+navicon+' '+navicon+'-prev"/>').mousedown(function() { navtrigger(37); })) | |
.append(fsicon) | |
.append($('<div class="'+navicon+' '+navicon+'-next"/>').mousedown(function() { navtrigger(39); })) | |
.append('<div class="elfinder-quicklook-navbar-separator"/>') | |
.append($('<div class="'+navicon+' '+navicon+'-close"/>').mousedown(function() { self.window.trigger('close'); })) | |
; | |
this.resize = 'resize.'+fm.namespace; | |
this.info = $('<div class="elfinder-quicklook-info-wrapper"/>') | |
.append(icon) | |
.append(info); | |
this.preview = $('<div class="elfinder-quicklook-preview ui-helper-clearfix"/>') | |
// clean info/icon | |
.bind('change', function(e) { | |
self.info.attr('style', '').hide(); | |
icon.removeAttr('class').attr('style', ''); | |
info.html(''); | |
}) | |
// update info/icon | |
.bind('update', function(e) { | |
var fm = self.fm, | |
preview = self.preview, | |
file = e.file, | |
tpl = '<div class="elfinder-quicklook-info-data">{value}</div>', | |
tmb; | |
if (file) { | |
!file.read && e.stopImmediatePropagation(); | |
self.window.data('hash', file.hash); | |
self.preview.unbind('changesize').trigger('change').children().remove(); | |
title.html(fm.escape(file.name)); | |
info.html( | |
tpl.replace(/\{value\}/, fm.escape(file.name)) | |
+ tpl.replace(/\{value\}/, fm.mime2kind(file)) | |
+ (file.mime == 'directory' ? '' : tpl.replace(/\{value\}/, fm.formatSize(file.size))) | |
+ tpl.replace(/\{value\}/, fm.i18n('modify')+': '+ fm.formatDate(file)) | |
) | |
icon.addClass('elfinder-cwd-icon ui-corner-all '+fm.mime2class(file.mime)); | |
if (file.tmb) { | |
$('<img/>') | |
.hide() | |
.appendTo(self.preview) | |
.load(function() { | |
icon.css('background', 'url("'+tmb+'") center center no-repeat'); | |
$(this).remove(); | |
}) | |
.attr('src', (tmb = fm.tmb(file.hash))); | |
} | |
self.info.delay(100).fadeIn(10); | |
} else { | |
e.stopImmediatePropagation(); | |
} | |
}); | |
this.window = $('<div class="ui-front ui-helper-reset ui-widget elfinder-quicklook" style="position:absolute"/>') | |
.click(function(e) { e.stopPropagation(); }) | |
.append( | |
$('<div class="elfinder-quicklook-titlebar"/>') | |
.append(title) | |
.append($('<span class="ui-icon ui-icon-circle-close"/>').mousedown(function(e) { | |
e.stopPropagation(); | |
self.window.trigger('close'); | |
})) | |
) | |
.append(this.preview.add(navbar)) | |
.append(self.info.hide()) | |
.draggable({handle : 'div.elfinder-quicklook-titlebar'}) | |
.bind('open', function(e) { | |
var win = self.window, | |
file = self.value, | |
node; | |
if (self.closed() && file && (node = cwd.find('#'+fm.cwdHash2Id(file.hash))).length) { | |
navbar.attr('style', ''); | |
state = animated; | |
node.trigger('scrolltoview'); | |
win.css(closedCss(node)) | |
.show() | |
.animate(openedCss(), 550, function() { | |
state = opened; | |
self.update(1, self.value); | |
}); | |
} | |
}) | |
.bind('close', function(e) { | |
var win = self.window, | |
preview = self.preview.trigger('change'), | |
file = self.value, | |
node = cwd.find('#'+fm.cwdHash2Id(win.data('hash'))), | |
close = function() { | |
state = closed; | |
win.hide(); | |
preview.children().remove(); | |
self.update(0, self.value); | |
}; | |
if (self.opened()) { | |
state = animated; | |
win.hasClass(fullscreen) && fsicon.mousedown() | |
node.length | |
? win.animate(closedCss(node), 500, close) | |
: close(); | |
} | |
}); | |
/** | |
* This command cannot be disable by backend | |
* | |
* @type Boolean | |
**/ | |
this.alwaysEnabled = true; | |
/** | |
* Selected file | |
* | |
* @type Object | |
**/ | |
this.value = null; | |
this.handlers = { | |
// save selected file | |
select : function() { this.update(void(0), this.fm.selectedFiles()[0]); }, | |
error : function() { self.window.is(':visible') && self.window.data('hash', '').trigger('close'); }, | |
'searchshow searchhide' : function() { this.opened() && this.window.trigger('close'); } | |
} | |
this.shortcuts = [{ | |
pattern : 'space' | |
}]; | |
this.support = { | |
audio : { | |
ogg : support('audio/ogg; codecs="vorbis"'), | |
mp3 : support('audio/mpeg;'), | |
wav : support('audio/wav; codecs="1"'), | |
m4a : support('audio/x-m4a;') || support('audio/aac;') | |
}, | |
video : { | |
ogg : support('video/ogg; codecs="theora"'), | |
webm : support('video/webm; codecs="vp8, vorbis"'), | |
mp4 : support('video/mp4; codecs="avc1.42E01E"') || support('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') | |
} | |
} | |
/** | |
* Return true if quickLoock window is visible and not animated | |
* | |
* @return Boolean | |
**/ | |
this.closed = function() { | |
return state == closed; | |
} | |
/** | |
* Return true if quickLoock window is hidden | |
* | |
* @return Boolean | |
**/ | |
this.opened = function() { | |
return state == opened; | |
} | |
/** | |
* Init command. | |
* Add default plugins and init other plugins | |
* | |
* @return Object | |
**/ | |
this.init = function() { | |
var o = this.options, | |
win = this.window, | |
preview = this.preview, | |
i, p; | |
width = o.width > 0 ? parseInt(o.width) : 450; | |
height = o.height > 0 ? parseInt(o.height) : 300; | |
fm.one('load', function() { | |
parent = fm.getUI(); | |
cwd = fm.getUI('cwd'); | |
win.appendTo('body'); | |
// close window on escape | |
$(document).keydown(function(e) { | |
e.keyCode == 27 && self.opened() && win.trigger('close') | |
}) | |
if ($.fn.resizable && !fm.UA.Touch) { | |
win.resizable({ | |
handles : 'se', | |
minWidth : 350, | |
minHeight : 120, | |
resize : function() { | |
// use another event to avoid recursion in fullscreen mode | |
// may be there is clever solution, but i cant find it :( | |
preview.trigger('changesize'); | |
} | |
}); | |
} | |
self.change(function() { | |
if (self.opened()) { | |
self.value ? preview.trigger($.Event('update', {file : self.value})) : win.trigger('close'); | |
} | |
}); | |
$.each(fm.commands.quicklook.plugins || [], function(i, plugin) { | |
if (typeof(plugin) == 'function') { | |
new plugin(self) | |
} | |
}); | |
preview.bind('update', function() { | |
self.info.show(); | |
}); | |
}); | |
} | |
this.getstate = function() { | |
return this.fm.selected().length == 1 ? state == opened ? 1 : 0 : -1; | |
} | |
this.exec = function() { | |
this.enabled() && this.window.trigger(this.opened() ? 'close' : 'open'); | |
} | |
this.hideinfo = function() { | |
this.info.stop(true).hide(); | |
} | |
} | |
/* | |
* File: /js/commands/quicklook.plugins.js | |
*/ | |
elFinder.prototype.commands.quicklook.plugins = [ | |
/** | |
* Images preview plugin | |
* | |
* @param elFinder.commands.quicklook | |
**/ | |
function(ql) { | |
var mimes = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml'], | |
preview = ql.preview; | |
// what kind of images we can display | |
$.each(navigator.mimeTypes, function(i, o) { | |
var mime = o.type; | |
if (mime.indexOf('image/') === 0 && $.inArray(mime, mimes)) { | |
mimes.push(mime); | |
} | |
}); | |
preview.bind('update', function(e) { | |
var file = e.file, | |
img; | |
if ($.inArray(file.mime, mimes) !== -1) { | |
// this is our file - stop event propagation | |
e.stopImmediatePropagation(); | |
img = $('<img/>') | |
.hide() | |
.appendTo(preview) | |
.load(function() { | |
// timeout - because of strange safari bug - | |
// sometimes cant get image height 0_o | |
setTimeout(function() { | |
var prop = (img.width()/img.height()).toFixed(2); | |
preview.bind('changesize', function() { | |
var pw = parseInt(preview.width()), | |
ph = parseInt(preview.height()), | |
w, h; | |
if (prop < (pw/ph).toFixed(2)) { | |
h = ph; | |
w = Math.floor(h * prop); | |
} else { | |
w = pw; | |
h = Math.floor(w/prop); | |
} | |
img.width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0); | |
}) | |
.trigger('changesize'); | |
// hide info/icon | |
ql.hideinfo(); | |
//show image | |
img.fadeIn(100); | |
}, 1) | |
}) | |
.attr('src', ql.fm.openUrl(file.hash)); | |
} | |
}); | |
}, | |
/** | |
* HTML preview plugin | |
* | |
* @param elFinder.commands.quicklook | |
**/ | |
function(ql) { | |
var mimes = ['text/html', 'application/xhtml+xml'], | |
preview = ql.preview, | |
fm = ql.fm; | |
preview.bind('update', function(e) { | |
var file = e.file, jqxhr; | |
if ($.inArray(file.mime, mimes) !== -1) { | |
e.stopImmediatePropagation(); | |
// stop loading on change file if not loaded yet | |
preview.one('change', function() { | |
jqxhr.state() == 'pending' && jqxhr.reject(); | |
}); | |
jqxhr = fm.request({ | |
data : {cmd : 'get', target : file.hash, current : file.phash, conv : 1}, | |
preventDefault : true | |
}) | |
.done(function(data) { | |
ql.hideinfo(); | |
doc = $('<iframe class="elfinder-quicklook-preview-html"/>').appendTo(preview)[0].contentWindow.document; | |
doc.open(); | |
doc.write(data.content); | |
doc.close(); | |
}); | |
} | |
}) | |
}, | |
/** | |
* Texts preview plugin | |
* | |
* @param elFinder.commands.quicklook | |
**/ | |
function(ql) { | |
var fm = ql.fm, | |
mimes = fm.res('mimes', 'text'), | |
preview = ql.preview; | |
preview.bind('update', function(e) { | |
var file = e.file, | |
mime = file.mime, | |
jqxhr; | |
if (mime.indexOf('text/') === 0 || $.inArray(mime, mimes) !== -1) { | |
e.stopImmediatePropagation(); | |
// stop loading on change file if not loadin yet | |
preview.one('change', function() { | |
jqxhr.state() == 'pending' && jqxhr.reject(); | |
}); | |
jqxhr = fm.request({ | |
data : {cmd : 'get', target : file.hash, conv : 1}, | |
preventDefault : true | |
}) | |
.done(function(data) { | |
ql.hideinfo(); | |
$('<div class="elfinder-quicklook-preview-text-wrapper"><pre class="elfinder-quicklook-preview-text">'+fm.escape(data.content)+'</pre></div>').appendTo(preview); | |
}); | |
} | |
}); | |
}, | |
/** | |
* PDF preview plugin | |
* | |
* @param elFinder.commands.quicklook | |
**/ | |
function(ql) { | |
var fm = ql.fm, | |
mime = 'application/pdf', | |
preview = ql.preview, | |
active = false; | |
if ((fm.UA.Safari && fm.OS == 'mac') || fm.UA.IE) { | |
active = true; | |
} else { | |
$.each(navigator.plugins, function(i, plugins) { | |
$.each(plugins, function(i, plugin) { | |
if (plugin.type == mime) { | |
return !(active = true); | |
} | |
}); | |
}); | |
} | |
active && preview.bind('update', function(e) { | |
var file = e.file, node; | |
if (file.mime == mime) { | |
e.stopImmediatePropagation(); | |
preview.one('change', function() { | |
node.unbind('load').remove(); | |
}); | |
node = $('<iframe class="elfinder-quicklook-preview-pdf"/>') | |
.hide() | |
.appendTo(preview) | |
.load(function() { | |
ql.hideinfo(); | |
node.show(); | |
}) | |
.attr('src', fm.url(file.hash)); | |
} | |
}) | |
}, | |
/** | |
* Flash preview plugin | |
* | |
* @param elFinder.commands.quicklook | |
**/ | |
function(ql) { | |
var fm = ql.fm, | |
mime = 'application/x-shockwave-flash', | |
preview = ql.preview, | |
active = false; | |
$.each(navigator.plugins, function(i, plugins) { | |
$.each(plugins, function(i, plugin) { | |
if (plugin.type == mime) { | |
return !(active = true); | |
} | |
}); | |
}); | |
active && preview.bind('update', function(e) { | |
var file = e.file, | |
node; | |
if (file.mime == mime) { | |
e.stopImmediatePropagation(); | |
ql.hideinfo(); | |
preview.append((node = $('<embed class="elfinder-quicklook-preview-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" src="'+fm.url(file.hash)+'" quality="high" type="application/x-shockwave-flash" />'))); | |
} | |
}); | |
}, | |
/** | |
* HTML5 audio preview plugin | |
* | |
* @param elFinder.commands.quicklook | |
**/ | |
function(ql) { | |
var preview = ql.preview, | |
autoplay = !!ql.options['autoplay'], | |
mimes = { | |
'audio/mpeg' : 'mp3', | |
'audio/mpeg3' : 'mp3', | |
'audio/mp3' : 'mp3', | |
'audio/x-mpeg3' : 'mp3', | |
'audio/x-mp3' : 'mp3', | |
'audio/x-wav' : 'wav', | |
'audio/wav' : 'wav', | |
'audio/x-m4a' : 'm4a', | |
'audio/aac' : 'm4a', | |
'audio/mp4' : 'm4a', | |
'audio/x-mp4' : 'm4a', | |
'audio/ogg' : 'ogg' | |
}, | |
node; | |
preview.bind('update', function(e) { | |
var file = e.file, | |
type = mimes[file.mime]; | |
if (ql.support.audio[type]) { | |
e.stopImmediatePropagation(); | |
node = $('<audio class="elfinder-quicklook-preview-audio" controls preload="auto" autobuffer><source src="'+ql.fm.openUrl(file.hash)+'" /></audio>') | |
.appendTo(preview); | |
autoplay && node[0].play(); | |
} | |
}).bind('change', function() { | |
if (node && node.parent().length) { | |
node[0].pause(); | |
node.remove(); | |
node= null; | |
} | |
}); | |
}, | |
/** | |
* HTML5 video preview plugin | |
* | |
* @param elFinder.commands.quicklook | |
**/ | |
function(ql) { | |
var preview = ql.preview, | |
autoplay = !!ql.options['autoplay'], | |
mimes = { | |
'video/mp4' : 'mp4', | |
'video/x-m4v' : 'mp4', | |
'video/ogg' : 'ogg', | |
'application/ogg' : 'ogg', | |
'video/webm' : 'webm' | |
}, | |
node; | |
preview.bind('update', function(e) { | |
var file = e.file, | |
type = mimes[file.mime]; | |
if (ql.support.video[type]) { | |
e.stopImmediatePropagation(); | |
ql.hideinfo(); | |
node = $('<video class="elfinder-quicklook-preview-video" controls preload="auto" autobuffer><source src="'+ql.fm.openUrl(file.hash)+'" /></video>').appendTo(preview); | |
autoplay && node[0].play(); | |
} | |
}).bind('change', function() { | |
if (node && node.parent().length) { | |
node[0].pause(); | |
node.remove(); | |
node= null; | |
} | |
}); | |
}, | |
/** | |
* Audio/video preview plugin using browser plugins | |
* | |
* @param elFinder.commands.quicklook | |
**/ | |
function(ql) { | |
var preview = ql.preview, | |
mimes = [], | |
node; | |
$.each(navigator.plugins, function(i, plugins) { | |
$.each(plugins, function(i, plugin) { | |
(plugin.type.indexOf('audio/') === 0 || plugin.type.indexOf('video/') === 0) && mimes.push(plugin.type); | |
}); | |
}); | |
preview.bind('update', function(e) { | |
var file = e.file, | |
mime = file.mime, | |
video; | |
if ($.inArray(file.mime, mimes) !== -1) { | |
e.stopImmediatePropagation(); | |
(video = mime.indexOf('video/') === 0) && ql.hideinfo(); | |
node = $('<embed src="'+ql.fm.openUrl(file.hash)+'" type="'+mime+'" class="elfinder-quicklook-preview-'+(video ? 'video' : 'audio')+'"/>') | |
.appendTo(preview); | |
} | |
}).bind('change', function() { | |
if (node && node.parent().length) { | |
node.remove(); | |
node= null; | |
} | |
}); | |
}, | |
/** | |
* Any supported files preview plugin using Google docs online viewer | |
* | |
* @param elFinder.commands.quicklook | |
**/ | |
function(ql) { | |
var fm = ql.fm, | |
mimes = ql.options.googleDocsMimes || [], | |
preview = ql.preview; | |
preview.bind('update', function(e) { | |
var win = ql.window, | |
file = e.file, node, loading; | |
if ($.inArray(file.mime, mimes) !== -1) { | |
if (file.url == '1') { | |
$('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info')) | |
.on('click', function() { | |
$(this).html('<span class="elfinder-info-spinner">'); | |
fm.request({ | |
data : {cmd : 'url', target : file.hash}, | |
preventDefault : true | |
}) | |
.always(function() { | |
$(this).html(''); | |
}) | |
.done(function(data) { | |
var rfile = fm.file(file.hash); | |
ql.value.url = rfile.url = data.url || ''; | |
if (ql.value.url) { | |
preview.trigger($.Event('update', {file : ql.value})); | |
} | |
}); | |
}); | |
} | |
if (file.url !== '' && file.url != '1') { | |
e.stopImmediatePropagation(); | |
preview.one('change', function() { | |
loading.remove(); | |
node.off('load').remove(); | |
}); | |
loading = $('<div class="elfinder-quicklook-info-data"> Now loading...<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info')); | |
node = $('<iframe class="elfinder-quicklook-preview-iframe"/>') | |
.css('background-color', 'transparent') | |
.appendTo(preview) | |
.on('load', function() { | |
ql.hideinfo(); | |
loading.remove(); | |
$(this).css('background-color', '#fff').show(); | |
}) | |
.attr('src', 'http://docs.google.com/gview?embedded=true&url=' + encodeURIComponent(fm.url(file.hash))); | |
var test; | |
} | |
} | |
}); | |
} | |
] | |
/* | |
* File: /js/commands/reload.js | |
*/ | |
/** | |
* @class elFinder command "reload" | |
* Sync files and folders | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.reload = function() { | |
var self = this, | |
search = false; | |
this.alwaysEnabled = true; | |
this.updateOnSelect = true; | |
this.shortcuts = [{ | |
pattern : 'ctrl+shift+r f5' | |
}]; | |
this.getstate = function() { | |
return 0; | |
}; | |
this.init = function() { | |
this.fm.bind('search searchend', function(e) { | |
search = e.type == 'search'; | |
}); | |
}; | |
this.fm.bind('contextmenu', function(e){ | |
var fm = self.fm; | |
if (fm.options.sync >= 1000) { | |
var node; | |
self.extra = { | |
icon: 'accept', | |
node: $('<span/>') | |
.attr({title: fm.i18n('autoSync')}) | |
.on('click', function(e){ | |
e.stopPropagation(); | |
e.preventDefault(); | |
node.parent() | |
.toggleClass('ui-state-disabled', fm.options.syncStart) | |
.parent().removeClass('ui-state-hover'); | |
fm.options.syncStart = !fm.options.syncStart; | |
fm.autoSync(fm.options.syncStart? null : 'stop'); | |
}) | |
}; | |
node = self.extra.node; | |
node.ready(function(){ | |
setTimeout(function(){ | |
node.parent().toggleClass('ui-state-disabled', !fm.options.syncStart).css('pointer-events', 'auto'); | |
}, 10); | |
}); | |
} | |
}); | |
this.exec = function() { | |
var fm = this.fm; | |
if (!search) { | |
var dfrd = fm.sync(), | |
timeout = setTimeout(function() { | |
fm.notify({type : 'reload', cnt : 1, hideCnt : true}); | |
dfrd.always(function() { fm.notify({type : 'reload', cnt : -1}); }); | |
}, fm.notifyDelay); | |
return dfrd.always(function() { | |
clearTimeout(timeout); | |
fm.trigger('reload'); | |
}); | |
} else { | |
$('div.elfinder-toolbar > div.'+fm.res('class', 'searchbtn') + ' > span.ui-icon-search').click(); | |
} | |
}; | |
}; | |
/* | |
* File: /js/commands/rename.js | |
*/ | |
/** | |
* @class elFinder command "rename". | |
* Rename selected file. | |
* | |
* @author Dmitry (dio) Levashov, dio@std42.ru | |
**/ | |
elFinder.prototype.commands.rename = function() { | |
this.shortcuts = [{ | |
pattern : 'f2'+(this.fm.OS == 'mac' ? ' enter' : '') | |
}]; | |
this.getstate = function(sel) { | |
var sel = this.files(sel); | |
return !this._disabled && sel.length == 1 && sel[0].phash && !sel[0].locked ? 0 : -1; | |
}; | |
this.exec = function(hashes) { | |
var fm = this.fm, | |
cwd = fm.getUI('cwd'), | |
sel = hashes || (fm.selected().length? fm.selected() : false) || [fm.cwd().hash], | |
cnt = sel.length, | |
file = fm.file(sel.shift()), | |
filename = '.elfinder-cwd-filename', | |
type = (hashes && hashes._type)? hashes._type : (fm.selected().length? 'files' : 'navbar'), | |
navbar = (type === 'navbar'), | |
target = $('#'+fm[navbar? 'navHash2Id' : 'cwdHash2Id'](file.hash)), | |
incwd = (fm.cwd().hash == file.hash), | |
tarea = (type === 'files' && fm.storage('view') != 'list'), | |
rest = function(){ | |
if (!overlay.is(':hidden')) { | |
overlay.addClass('ui-front') | |
.elfinderoverlay('hide') | |
.off('click', cancel); | |
} | |
pnode.removeClass('ui-front').css('position', ''); | |
if (tarea) { | |
node.css('max-height', ''); | |
} else if (!navbar) { | |
pnode.css('width', '') | |
.parent('td').css('overflow', ''); | |
} | |
}, colwidth, | |
dfrd = $.Deferred() | |
.done(function(data){ | |
incwd && fm.exec('open', data.added[0].hash); | |
}) | |
.fail(function(error) { | |
var parent = input.parent(), | |
name = fm.escape(file.name); | |
if (tarea) { | |
name = name.replace(/([_.])/g, '​$1'); | |
} | |
//rest(); | |
if (navbar) { | |
input.replaceWith(name); | |
} else { | |
if (parent.length) { | |
input.remove(); | |
parent.html(name); | |
} else { | |
//cwd.find('#'+fm.cwdHash2Id(file.hash)).find(filename).html(name); | |
target.find(filename).html(name); | |
setTimeout(function() { | |
cwd.find('#'+fm.cwdHash2Id(file.hash)).click(); | |
}, 50); | |
} | |
} | |
error && fm.error(error); | |
}) | |
.always(function() { | |
rest(); | |
fm.enable(); | |
}), | |
input = $(tarea? '<textarea/>' : '<input type="text"/>') | |
.on('keyup text', function(){ | |
if (tarea) { | |
this.style.height = '1px'; | |
this.style.height = this.scrollHeight + 'px'; | |
} else if (colwidth) { | |
this.style.width = colwidth + 'px'; | |
if (this.scrollWidth > colwidth) { | |
this.style.width = this.scrollWidth + 10 + 'px'; | |
} | |
} | |
}) | |
.keydown(function(e) { | |
e.stopPropagation(); | |
e.stopImmediatePropagation(); | |
if (e.keyCode == $.ui.keyCode.ESCAPE) { | |
dfrd.reject(); | |
} else if (e.keyCode == $.ui.keyCode.ENTER) { | |
input.blur(); | |
} | |
}) | |
.mousedown(function(e) { | |
e.stopPropagation(); | |
}) | |
.click(function(e) { // for touch device | |
e.stopPropagation(); | |
}) | |
.dblclick(function(e) { | |
e.stopPropagation(); | |
e.preventDefault(); | |
}) | |
.blur(function() { | |
var name = $.trim(input.val()), | |
parent = input.parent(), | |
valid = true; | |
if (pnode.length) { | |
if (input[0].setSelectionRange) { | |
input[0].setSelectionRange(0, 0) | |
} | |
if (name == file.name) { | |
return dfrd.reject(); | |
} | |
if (fm.options.validName && fm.options.validName.test) { | |
try { | |
valid = fm.options.validName.test(name); | |
} catch(e) { | |
valid = false; | |
} | |
} | |
if (!name || name === '..' || !valid) { | |
fm.error('errInvName'); | |
return false; | |
} | |
if (fm.fileByName(name, file.phash)) { | |
fm.error(['errExists', name]); | |
return false; | |
} | |
rest(); | |
(navbar? pnode : node).html(fm.escape(name)); | |
fm.lockfiles({files : [file.hash]}); | |
fm.request({ | |
data : {cmd : 'rename', target : file.hash, name : name}, | |
notify : {type : 'rename', cnt : 1} | |
}) | |
.fail(function(error) { | |
dfrd.reject(); | |
fm.sync(); | |
}) | |
.done(function(data) { | |
dfrd.resolve(data); | |
if (!navbar && data && data.added && data.added[0]) { | |
var newItem = cwd.find('#'+fm.cwdHash2Id(data.added[0].hash)); | |
if (newItem.length) { | |
newItem.trigger('scrolltoview'); | |
} | |
} | |
}) | |
.always(function() { | |
fm.unlockfiles({files : [file.hash]}) | |
}); | |
} | |
}), | |
node = navbar? target.contents().filter(function(){ return this.nodeType==3 && $(this).parent().attr('id') === fm.navHash2Id(file.hash); }) | |
: target.find(filename), | |
name = file.name.replace(/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/ig, ''), | |
pnode = node.parent(), | |
overlay = fm.getUI().children('.elfinder-overlay'), | |
cancel = function(e) { | |
e.stopPropagation(); | |
dfrd.reject(); | |
}; | |
pnode.addClass('ui-front').css('position', 'relative'); | |
if (navbar) { | |
node.replaceWith(input.val(file.name)); | |
} else { | |
if (tarea) { | |
node.css('max-height', 'none'); | |
} else if (!navbar) { | |
colwidth = pnode.width(); | |
pnode.width(colwidth - 15) | |
.parent('td').css('overflow', 'visible'); | |
} | |
node.empty().append(input.val(file.name)); | |
} | |
if (fm.UA.Mobile) { | |
overlay.on('click', cancel) | |
.removeClass('ui-front').elfinderoverlay('show'); | |
} | |
if (cnt > 1 || this.getstate([file.hash]) < 0) { | |
return dfrd.reject(); | |
} | |
if (!file || !node.length) { | |
return dfrd.reject('errCmdParams', this.title); | |
} | |
if (file.locked) { | |
return dfrd.reject(['errLocked', file.name]); | |
} | |
fm.one('select', function() { | |
input.parent().length && file && $.inArray(file.hash, fm.selected()) === -1 && input.blur(); | |
}) | |
input.trigger('keyup'); | |
input.select().focus(); | |
input[0].setSelectionRange && input[0].setSelectionRange(0, name.length); | |
return dfrd; | |
}; | |
} | |
/* | |
* File: /js/commands/resize.js | |
*/ | |
/** | |
* @class elFinder command "resize" | |
* Open dialog to resize image | |
* | |
* @author Dmitry (dio) Levashov | |
* @author Alexey Sukhotin | |
* @author Naoki Sawada | |
* @author Sergio Jovani | |
**/ | |
elFinder.prototype.commands.resize = function() { | |
this.updateOnSelect = false; | |
this.getstate = function() { | |
var sel = this.fm.selectedFiles(); | |
return !this._disabled && sel.length == 1 && sel[0].read && sel[0].write && sel[0].mime.indexOf('image/') !== -1 ? 0 : -1; | |
}; | |
this.exec = function(hashes) { | |
var fm = this.fm, | |
files = this.files(hashes), | |
dfrd = $.Deferred(), | |
open = function(file, id) { | |
var isJpeg = (file.mime === 'image/jpeg'), | |
dialog = $('<div class="elfinder-dialog-resize"/>'), | |
input = '<input type="text" size="5"/>', | |
row = '<div class="elfinder-resize-row"/>', | |
label = '<div class="elfinder-resize-label"/>', | |
control = $('<div class="elfinder-resize-control"/>'), | |
preview = $('<div class="ui-front elfinder-resize-preview"/>'), | |
spinner = $('<div class="elfinder-resize-spinner">'+fm.i18n('ntfloadimg')+'</div>'), | |
rhandle = $('<div class="elfinder-resize-handle"/>'), | |
rhandlec = $('<div class="elfinder-resize-handle"/>'), | |
uiresize = $('<div class="elfinder-resize-uiresize"/>'), | |
uicrop = $('<div class="elfinder-resize-uicrop"/>'), | |
uibuttonset = '<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>', | |
uibutton = '<div class="ui-state-default elfinder-button"/>', | |
uiseparator = '<span class="ui-widget-content elfinder-toolbar-button-separator"/>', | |
uirotate = $('<div class="elfinder-resize-rotate"/>'), | |
uideg270 = $(uibutton).attr('title',fm.i18n('rotate-cw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-l"/>') | |
.click(function(){ | |
rdegree = rdegree - 90; | |
rotate.update(rdegree); | |
})), | |
uideg90 = $(uibutton).attr('title',fm.i18n('rotate-ccw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-r"/>') | |
.click(function(){ | |
rdegree = rdegree + 90; | |
rotate.update(rdegree); | |
})), | |
uiprop = $('<span />'), | |
reset = $('<div class="ui-state-default ui-corner-all elfinder-resize-reset"><span class="ui-icon ui-icon-arrowreturnthick-1-w"/></div>'), | |
uitype = $('<div class="elfinder-resize-type"/>') | |
.append('<input class="" type="radio" name="type" id="'+id+'-resize" value="resize" checked="checked" /><label for="'+id+'-resize">'+fm.i18n('resize')+'</label>', | |
'<input type="radio" name="type" id="'+id+'-crop" value="crop" /><label for="'+id+'-crop">'+fm.i18n('crop')+'</label>', | |
'<input type="radio" name="type" id="'+id+'-rotate" value="rotate" /><label for="'+id+'-rotate">'+fm.i18n('rotate')+'</label>'), | |
type = $('input', uitype).attr('disabled', 'disabled') | |
.change(function() { | |
var val = $('input:checked', uitype).val(); | |
resetView(); | |
resizable(true); | |
croppable(true); | |
rotateable(true); | |
if (val == 'resize') { | |
uiresize.show(); | |
uirotate.hide(); | |
uicrop.hide(); | |
resizable(); | |
} | |
else if (val == 'crop') { | |
uirotate.hide(); | |
uiresize.hide(); | |
uicrop.show(); | |
croppable(); | |
} else if (val == 'rotate') { | |
uiresize.hide(); | |
uicrop.hide(); | |
uirotate.show(); | |
rotateable(); | |
} | |
}), | |
constr = $('<input type="checkbox" checked="checked"/>') | |
.change(function() { | |
cratio = !!constr.prop('checked'); | |
resize.fixHeight(); | |
resizable(true); | |
resizable(); | |
}), | |
width = $(input) | |
.change(function() { | |
var w = parseInt(width.val()), | |
h = parseInt(cratio ? Math.round(w/ratio) : height.val()); | |
if (w > 0 && h > 0) { | |
resize.updateView(w, h); | |
height.val(h); | |
} | |
}), | |
height = $(input) | |
.change(function() { | |
var h = parseInt(height.val()), | |
w = parseInt(cratio ? Math.round(h*ratio) : width.val()); | |
if (w > 0 && h > 0) { | |
resize.updateView(w, h); | |
width.val(w); | |
} | |
}), | |
pointX = $(input).change(function(){crop.updateView();}), | |
pointY = $(input).change(function(){crop.updateView();}), | |
offsetX = $(input).change(function(){crop.updateView();}), | |
offsetY = $(input).change(function(){crop.updateView();}), | |
quality = isJpeg? | |
$(input).val(fm.option('jpgQuality')) | |
.addClass('quality') | |
.on('blur', function(){ | |
var q = Math.min(100, Math.max(1, parseInt(this.value))); | |
dialog.find('input.quality').val(q); | |
}) | |
: null, | |
degree = $('<input type="text" size="3" maxlength="3" value="0" />') | |
.change(function() { | |
rotate.update(); | |
}), | |
uidegslider = $('<div class="elfinder-resize-rotate-slider"/>') | |
.slider({ | |
min: 0, | |
max: 360, | |
value: degree.val(), | |
animate: true, | |
change: function(event, ui) { | |
if (ui.value != uidegslider.slider('value')) { | |
rotate.update(ui.value); | |
} | |
}, | |
slide: function(event, ui) { | |
rotate.update(ui.value, false); | |
} | |
}), | |
ratio = 1, | |
prop = 1, | |
owidth = 0, | |
oheight = 0, | |
cratio = true, | |
pwidth = 0, | |
pheight = 0, | |
rwidth = 0, | |
rheight = 0, | |
rdegree = 0, | |
img = $('<img/>') | |
.load(function() { | |
spinner.remove(); | |
owidth = img.width(); | |
oheight = img.height(); | |
ratio = owidth/oheight; | |
resize.updateView(owidth, oheight); | |
rhandle.append(img.show()).show(); | |
width.val(owidth); | |
height.val(oheight); | |
var r_scale = Math.min(pwidth, pheight) / Math.sqrt(Math.pow(owidth, 2) + Math.pow(oheight, 2)); | |
rwidth = owidth * r_scale; | |
rheight = oheight * r_scale; | |
type.button('enable'); | |
control.find('input,select').removeAttr('disabled') | |
.filter(':text').keydown(function(e) { | |
var c = e.keyCode, i; | |
e.stopPropagation(); | |
if ((c >= 37 && c <= 40) | |
|| c == $.ui.keyCode.BACKSPACE | |
|| c == $.ui.keyCode.DELETE | |
|| (c == 65 && (e.ctrlKey||e.metaKey)) | |
|| c == 27) { | |
return; | |
} | |
if (c == 9) { | |
i = $(this).parent()[e.shiftKey ? 'prevAll' : 'nextAll']('div.elfinder-resize-row').children(':text'); | |
if (i.length) { | |
i[0].focus(); | |
} else { | |
$(this).parent().parent().find(':text:' + (e.shiftKey ? 'last' : 'first')).focus(); | |
} | |
} | |
if (c == 13) { | |
fm.confirm({ | |
title : $('input:checked', uitype).val(), | |
text : 'confirmReq', | |
accept : { | |
label : 'btnApply', | |
callback : function() { | |
save(); | |
} | |
}, | |
cancel : { | |
label : 'btnCancel', | |
callback : function(){} | |
} | |
}); | |
return; | |
} | |
if (!((c >= 48 && c <= 57) || (c >= 96 && c <= 105))) { | |
e.preventDefault(); | |
} | |
}) | |
.filter(':first').focus(); | |
resizable(); | |
reset.hover(function() { reset.toggleClass('ui-state-hover'); }).click(resetView); | |
}) | |
.error(function() { | |
spinner.text('Unable to load image').css('background', 'transparent'); | |
}), | |
basec = $('<div/>'), | |
imgc = $('<img/>'), | |
coverc = $('<div/>'), | |
imgr = $('<img/>'), | |
round = function(v) { | |
return isJpeg? Math.round(v/8)*8 : Math.round(v); | |
}, | |
resetView = function() { | |
width.val(owidth); | |
height.val(oheight); | |
resize.updateView(owidth, oheight); | |
}, | |
resize = { | |
update : function() { | |
width.val(round(img.width()/prop)); | |
height.val(round(img.height()/prop)); | |
}, | |
updateView : function(w, h) { | |
if (w > pwidth || h > pheight) { | |
if (w / pwidth > h / pheight) { | |
prop = pwidth / w; | |
img.width(pwidth).height(Math.ceil(h*prop)); | |
} else { | |
prop = pheight / h; | |
img.height(pheight).width(Math.ceil(w*prop)); | |
} | |
} else { | |
img.width(w).height(h); | |
} | |
prop = img.width()/w; | |
uiprop.text('1 : '+(1/prop).toFixed(2)); | |
resize.updateHandle(); | |
}, | |
updateHandle : function() { | |
rhandle.width(img.width()).height(img.height()); | |
}, | |
fixHeight : function() { | |
var w, h; | |
if (cratio) { | |
w = width.val(); | |
h = Math.round(w/ratio); | |
resize.updateView(w, h); | |
height.val(h); | |
} | |
} | |
}, | |
crop = { | |
update : function() { | |
offsetX.val(round((rhandlec.data('w')||rhandlec.width())/prop)); | |
offsetY.val(round((rhandlec.data('h')||rhandlec.height())/prop)); | |
pointX.val(Math.round(((rhandlec.data('x')||rhandlec.offset().left)-imgc.offset().left)/prop)); | |
pointY.val(Math.round(((rhandlec.data('y')||rhandlec.offset().top)-imgc.offset().top)/prop)); | |
}, | |
updateView : function() { | |
var x = parseInt(pointX.val()) * prop + imgc.offset().left; | |
var y = parseInt(pointY.val()) * prop + imgc.offset().top; | |
var w = offsetX.val() * prop; | |
var h = offsetY.val() * prop; | |
rhandlec.data({x: x, y: y, w: w, h: h}) | |
.width(Math.round(w)) | |
.height(Math.round(h)) | |
.offset({left: Math.round(x), top: Math.round(y)}); | |
coverc.width(rhandlec.width()) | |
.height(rhandlec.height()); | |
}, | |
resize_update : function() { | |
rhandlec.data({w: null, h: null}); | |
crop.update(); | |
coverc.width(rhandlec.width()) | |
.height(rhandlec.height()); | |
}, | |
drag_update : function() { | |
rhandlec.data({x: null, y: null}); | |
crop.update(); | |
} | |
}, | |
rotate = { | |
mouseStartAngle : 0, | |
imageStartAngle : 0, | |
imageBeingRotated : false, | |
update : function(value, animate) { | |
if (typeof value == 'undefined') { | |
rdegree = value = parseInt(degree.val()); | |
} | |
if (typeof animate == 'undefined') { | |
animate = true; | |
} | |
if (! animate || fm.UA.Opera || fm.UA.ltIE8) { | |
imgr.rotate(value); | |
} else { | |
imgr.animate({rotate: value + 'deg'}); | |
} | |
value = value % 360; | |
if (value < 0) { | |
value += 360; | |
} | |
degree.val(parseInt(value)); | |
uidegslider.slider('value', degree.val()); | |
}, | |
execute : function ( e ) { | |
if ( !rotate.imageBeingRotated ) return; | |
var imageCentre = rotate.getCenter( imgr ); | |
var mouseXFromCentre = e.pageX - imageCentre[0]; | |
var mouseYFromCentre = e.pageY - imageCentre[1]; | |
var mouseAngle = Math.atan2( mouseYFromCentre, mouseXFromCentre ); | |
var rotateAngle = mouseAngle - rotate.mouseStartAngle + rotate.imageStartAngle; | |
rotateAngle = Math.round(parseFloat(rotateAngle) * 180 / Math.PI); | |
if ( e.shiftKey ) { | |
rotateAngle = Math.round((rotateAngle + 6)/15) * 15; | |
} | |
imgr.rotate(rotateAngle); | |
rotateAngle = rotateAngle % 360; | |
if (rotateAngle < 0) { | |
rotateAngle += 360; | |
} | |
degree.val(rotateAngle); | |
uidegslider.slider('value', degree.val()); | |
return false; | |
}, | |
start : function ( e ) { | |
rotate.imageBeingRotated = true; | |
var imageCentre = rotate.getCenter( imgr ); | |
var mouseStartXFromCentre = e.pageX - imageCentre[0]; | |
var mouseStartYFromCentre = e.pageY - imageCentre[1]; | |
rotate.mouseStartAngle = Math.atan2( mouseStartYFromCentre, mouseStartXFromCentre ); | |
rotate.imageStartAngle = parseFloat(imgr.rotate()) * Math.PI / 180.0; | |
$(document).mousemove( rotate.execute ); | |
return false; | |
}, | |
stop : function ( e ) { | |
if ( !rotate.imageBeingRotated ) return; | |
$(document).unbind( 'mousemove' , rotate.execute); | |
setTimeout( function() { rotate.imageBeingRotated = false; }, 10 ); | |
return false; | |
}, | |
getCenter : function ( image ) { | |
var currentRotation = imgr.rotate(); | |
imgr.rotate(0); | |
var imageOffset = imgr.offset(); | |
var imageCentreX = imageOffset.left + imgr.width() / 2; | |
var imageCentreY = imageOffset.top + imgr.height() / 2; | |
imgr.rotate(currentRotation); | |
return Array( imageCentreX, imageCentreY ); | |
} | |
}, | |
resizable = function(destroy) { | |
if ($.fn.resizable) { | |
if (destroy) { | |
rhandle.filter(':ui-resizable').resizable('destroy'); | |
rhandle.hide(); | |
} | |
else { | |
rhandle.show(); | |
rhandle.resizable({ | |
alsoResize : img, | |
aspectRatio : cratio, | |
resize : resize.update, | |
stop : resize.fixHeight | |
}); | |
} | |
} | |
}, | |
croppable = function(destroy) { | |
if ($.fn.draggable && $.fn.resizable) { | |
if (destroy) { | |
rhandlec.filter(':ui-resizable').resizable('destroy') | |
.filter(':ui-draggable').draggable('destroy'); | |
basec.hide(); | |
} | |
else { | |
basec.show() | |
.width(img.width()) | |
.height(img.height()); | |
imgc | |
.width(img.width()) | |
.height(img.height()); | |
coverc | |
.width(img.width()) | |
.height(img.height()); | |
rhandlec | |
.width(imgc.width()) | |
.height(imgc.height()) | |
.offset(imgc.offset()) | |
.resizable({ | |
containment : basec, | |
resize : crop.resize_update, | |
handles : 'all' | |
}) | |
.draggable({ | |
handle : coverc, | |
containment : imgc, | |
drag : crop.drag_update | |
}); | |
crop.update(); | |
} | |
} | |
}, | |
rotateable = function(destroy) { | |
if ($.fn.draggable && $.fn.resizable) { | |
if (destroy) { | |
imgr.hide(); | |
} | |
else { | |
imgr.show() | |
.width(rwidth) | |
.height(rheight) | |
.css('margin-top', (pheight-rheight)/2 + 'px') | |
.css('margin-left', (pwidth-rwidth)/2 + 'px'); | |
} | |
} | |
}, | |
save = function() { | |
var w, h, x, y, d, q; | |
var mode = $('input:checked', uitype).val(); | |
//width.add(height).change(); // may be unnecessary | |
if (mode == 'resize') { | |
w = parseInt(width.val()) || 0; | |
h = parseInt(height.val()) || 0; | |
} else if (mode == 'crop') { | |
w = parseInt(offsetX.val()) || 0; | |
h = parseInt(offsetY.val()) || 0; | |
x = parseInt(pointX.val()) || 0; | |
y = parseInt(pointY.val()) || 0; | |
} else if (mode == 'rotate') { | |
w = owidth; | |
h = oheight; | |
d = parseInt(degree.val()) || 0; | |
if (d < 0 || d > 360) { | |
return fm.error('Invalid rotate degree'); | |
} | |
if (d == 0 || d == 360) { | |
return fm.error('Image dose not rotated'); | |
} | |
} | |
q = quality? parseInt(quality.val()) : 0; | |
if (mode != 'rotate') { | |
if (w <= 0 || h <= 0) { | |
return fm.error('Invalid image size'); | |
} | |
if (w == owidth && h == oheight) { | |
return fm.error('Image size not changed'); | |
} | |
} | |
dialog.elfinderdialog('close'); | |
fm.request({ | |
data : { | |
cmd : 'resize', | |
target : file.hash, | |
width : w, | |
height : h, | |
x : x, | |
y : y, | |
degree : d, | |
quality: q, | |
mode : mode | |
}, | |
notify : {type : 'resize', cnt : 1} | |
}) | |
.fail(function(error) { | |
dfrd.reject(error); | |
}) | |
.done(function() { | |
dfrd.resolve(); | |
}); | |
}, | |
buttons = {}, | |
hline = 'elfinder-resize-handle-hline', | |
vline = 'elfinder-resize-handle-vline', | |
rpoint = 'elfinder-resize-handle-point', | |
src = fm.openUrl(file.hash) | |
; | |
imgr.mousedown( rotate.start ); | |
$(document).mouseup( rotate.stop ); | |
uiresize.append( | |
$(row).append($(label).text(fm.i18n('width')), width, reset), | |
$(row).append($(label).text(fm.i18n('height')), height), | |
$(row).append($('<label/>').text(fm.i18n('aspectRatio')).prepend(constr)), | |
(quality? $(row).append($(label).text(fm.i18n('quality')), quality, $('<span/>').text(' (1-100)')) : $()), | |
$(row).append($(label).text(fm.i18n('scale')), uiprop) | |
); | |
uicrop.append( | |
$(row).append($(label).text('X'), pointX), | |
$(row).append($(label).text('Y')).append(pointY), | |
$(row).append($(label).text(fm.i18n('width')), offsetX), | |
$(row).append($(label).text(fm.i18n('height')), offsetY), | |
(quality? $(row).append($(label).text(fm.i18n('quality')), quality.clone(true), $('<span/>').text(' (1-100)')) : $()) | |
); | |
uirotate.append( | |
$(row).append( | |
$(label).text(fm.i18n('rotate')), | |
degree, | |
$('<span/>').text(fm.i18n('degree')), | |
$(uibuttonset).append(uideg270, $(uiseparator), uideg90) | |
), | |
$(row).css('height', '20px').append(uidegslider), | |
(quality? $(row).append($(label).text(fm.i18n('quality')), quality.clone(true), $('<span/>').text(' (1-100)')) : $()) | |
); | |
dialog.append(uitype).on('resize', function(e){ | |
e.stopPropagation(); | |
}); | |
control.append($(row), uiresize, uicrop.hide(), uirotate.hide()) | |
.find('input,select').attr('disabled', 'disabled'); | |
rhandle.append('<div class="'+hline+' '+hline+'-top"/>', | |
'<div class="'+hline+' '+hline+'-bottom"/>', | |
'<div class="'+vline+' '+vline+'-left"/>', | |
'<div class="'+vline+' '+vline+'-right"/>', | |
'<div class="'+rpoint+' '+rpoint+'-e"/>', | |
'<div class="'+rpoint+' '+rpoint+'-se"/>', | |
'<div class="'+rpoint+' '+rpoint+'-s"/>'); | |
preview.append(spinner).append(rhandle.hide()).append(img.hide()); | |
rhandlec.css('position', 'absolute') | |
.append('<div class="'+hline+' '+hline+'-top"/>', | |
'<div class="'+hline+' '+hline+'-bottom"/>', | |
'<div class="'+vline+' '+vline+'-left"/>', | |
'<div class="'+vline+' '+vline+'-right"/>', | |
'<div class="'+rpoint+' '+rpoint+'-n"/>', | |
'<div class="'+rpoint+' '+rpoint+'-e"/>', | |
'<div class="'+rpoint+' '+rpoint+'-s"/>', | |
'<div class="'+rpoint+' '+rpoint+'-w"/>', | |
'<div class="'+rpoint+' '+rpoint+'-ne"/>', | |
'<div class="'+rpoint+' '+rpoint+'-se"/>', | |
'<div class="'+rpoint+' '+rpoint+'-sw"/>', | |
'<div class="'+rpoint+' '+rpoint+'-nw"/>'); | |
preview.append(basec.css('position', 'absolute').hide().append(imgc, rhandlec.append(coverc))); | |
preview.append(imgr.hide()); | |
preview.css('overflow', 'hidden'); | |
dialog.append(preview, control); | |
buttons[fm.i18n('btnApply')] = save; | |
buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); }; | |
fm.dialog(dialog, { | |
title : fm.escape(file.name), | |
width : 650, | |
resizable : false, | |
destroyOnClose : true, | |
buttons : buttons, | |
open : function() { | |
var dw = dialog.width() - 20; | |
(preview.width() > dw) && preview.width(dw); | |
pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width()); | |
pheight = preview.height() - (rhandle.outerHeight() - rhandle.height()); | |
img.attr('src', src + (src.indexOf('?') === -1 ? '?' : '&')+'_='+Math.random()); | |
imgc.attr('src', img.attr('src')); | |
imgr.attr('src', img.attr('src')); | |
} | |
}).attr('id', id); | |
// for IE < 9 dialog mising at open second+ time. | |
if (fm.UA.ltIE8) { | |
$('.elfinder-dialog').css('filter', ''); | |
} | |
reset.css('left', width.position().left + width.width() + 12); | |
coverc.css({ 'opacity': 0.2, 'background-color': '#fff', 'position': 'absolute'}), | |
rhandlec.css('cursor', 'move'); | |
rhandlec.find('.elfinder-resize-handle-point').css({ | |
'background-color' : '#fff', | |
'opacity': 0.5, | |
'border-color':'#000' | |
}); | |
imgr.css('cursor', 'pointer'); | |
uitype.controlgroup? uitype.controlgroup() : uitype.buttonset(); | |
}, | |
id, dialog | |
; | |
if (!files.length || files[0].mime.indexOf('image/') === -1) { | |
return dfrd.reject(); | |
} | |
id = 'resize-'+fm.namespace+'-'+files[0].hash; | |
dialog = fm.getUI().find('#'+id); | |
if (dialog.length) { | |
dialog.elfinderdialog('toTop'); | |
return dfrd.resolve(); | |
} | |
open(files[0], id); | |
return dfrd; | |
}; | |
}; | |
(function ($) { | |
var findProperty = function (styleObject, styleArgs) { | |
var i = 0 ; | |
for( i in styleArgs) { | |
if (typeof styleObject[styleArgs[i]] != 'undefined') | |
return styleArgs[i]; | |
} | |
styleObject[styleArgs[i]] = ''; | |
return styleArgs[i]; | |
}; | |
$.cssHooks.rotate = { | |
get: function(elem, computed, extra) { | |
return $(elem).rotate(); | |
}, | |
set: function(elem, value) { | |
$(elem).rotate(value); | |
return value; | |
} | |
}; | |
$.cssHooks.transform = { | |
get: function(elem, computed, extra) { | |
var name = findProperty( elem.style , | |
['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] ); | |
return elem.style[name]; | |
}, | |
set: function(elem, value) { | |
var name = findProperty( elem.style , | |
['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] ); | |
elem.style[name] = value; | |
return value; | |
} | |
}; | |
$.fn.rotate = function(val) { | |
if (typeof val == 'undefined') { | |
if (!!window.opera) { | |
var r = this.css('transform').match(/rotate\((.*?)\)/); | |
return ( r && r[1])? | |
Math.round(parseFloat(r[1]) * 180 / Math.PI) : 0; | |
} else { | |
var r = this.css('transform').match(/rotate\((.*?)\)/); | |
return ( r && r[1])? parseInt(r[1]) : 0; | |
} | |
} | |
this.css('transform', | |
this.css('transform').replace(/none|rotate\(.*?\)/, '') + 'rotate(' + parseInt(val) + 'deg)'); | |
return this; | |
}; | |
$.fx.step.rotate = function(fx) { | |
if ( fx.state == 0 ) { | |
fx.start = $(fx.elem).rotate(); | |
fx.now = fx.start; | |
} | |
$(fx.elem).rotate(fx.now); | |
}; | |
if (typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined") { // IE & IE<9 | |
var GetAbsoluteXY = function(element) { | |
var pnode = element; | |
var x = pnode.offsetLeft; | |
var y = pnode.offsetTop; | |
while ( pnode.offsetParent ) { | |
pnode = pnode.offsetParent; | |
if (pnode != document.body && pnode.currentStyle['position'] != 'static') { | |
break; | |
} | |
if (pnode != document.body && pnode != document.documentElement) { | |
x -= pnode.scrollLeft; | |
y -= pnode.scrollTop; | |
} | |
x += pnode.offsetLeft; | |
y += pnode.offsetTop; | |
} | |
return { x: x, y: y }; | |
}; | |
var StaticToAbsolute = function (element) { | |
if ( element.currentStyle['position'] != 'static') { | |
return ; | |
} | |
var xy = GetAbsoluteXY(element); | |
element.style.position = 'absolute' ; | |
element.style.left = xy.x + 'px'; | |
element.style.top = xy.y + 'px'; | |
}; | |
var IETransform = function(element,transform){ | |
var r; | |
var m11 = 1; | |
var m12 = 1; | |
var m21 = 1; | |
var m22 = 1; | |
if (typeof element.style['msTransform'] != 'undefined'){ | |
return true; | |
} | |
StaticToAbsolute(element); | |
r = transform.match(/rotate\((.*?)\)/); | |
var rotate = ( r && r[1]) ? parseInt(r[1]) : 0; | |
rotate = rotate % 360; | |
if (rotate < 0) rotate = 360 + rotate; | |
var radian= rotate * Math.PI / 180; | |
var cosX =Math.cos(radian); | |
var sinY =Math.sin(radian); | |
m11 *= cosX; | |
m12 *= -sinY; | |
m21 *= sinY; | |
m22 *= cosX; | |
element.style.filter = (element.style.filter || '').replace(/progid:DXImageTransform\.Microsoft\.Matrix\([^)]*\)/, "" ) + | |
("progid:DXImageTransform.Microsoft.Matrix(" + | |
"M11=" + m11 + | |
",M12=" + m12 + | |
",M21=" + m21 + | |
",M22=" + m22 + | |
",FilterType='bilinear',sizingMethod='auto expand')") | |
; | |
var ow = parseInt(element.style.width || element.width || 0 ); | |
var oh = parseInt(element.style.height || element.height || 0 ); | |
var radian = rotate * Math.PI / 180; | |
var absCosX =Math.abs(Math.cos(radian)); | |
var absSinY =Math.abs(Math.sin(radian)); | |
var dx = (ow - (ow * absCosX + oh * absSinY)) / 2; | |
var dy = (oh - (ow * absSinY + oh * absCosX)) / 2; | |
element.style.marginLeft = Math.floor(dx) + "px"; | |
element.style.marginTop = Math.floor(dy) + "px"; | |
return(true); | |
}; | |
var transform_set = $.cssHooks.transform.set; | |
$.cssHooks.transform.set = function(elem, value) { | |
transform_set.apply(this, [elem, value] ); | |
IETransform(elem,value); | |
return value; | |
}; | |
} | |
})(jQuery); | |
/* | |
* File: /js/commands/rm.js | |
*/ | |
/** | |
* @class elFinder command "rm" | |
* Delete files | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.rm = function() { | |
this.shortcuts = [{ | |
pattern : 'delete ctrl+backspace' | |
}]; | |
this.getstate = function(sel) { | |
var fm = this.fm; | |
sel = sel || fm.selected(); | |
return !this._disabled && sel.length && $.map(sel, function(h) { var f = fm.file(h); return f && f.phash && !f.locked ? h : null }).length == sel.length | |
? 0 : -1; | |
} | |
this.exec = function(hashes) { | |
var self = this, | |
fm = this.fm, | |
dfrd = $.Deferred() | |
.fail(function(error) { | |
error && fm.error(error); | |
}), | |
files = this.files(hashes), | |
cnt = files.length, | |
cwd = fm.cwd().hash, | |
tpl = '<div class="ui-helper-clearfix elfinder-rm-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}<div class="elfinder-rm-desc">{desc}</div></div>', | |
targets, text, f, fname, size, tmb, descs, dialog; | |
if (!cnt || this._disabled) { | |
return dfrd.reject(); | |
} | |
$.each(files, function(i, file) { | |
if (!file.phash) { | |
return !dfrd.reject(['errRm', file.name, 'errPerm']); | |
} | |
if (file.locked) { | |
return !dfrd.reject(['errLocked', file.name]); | |
} | |
}); | |
if (dfrd.state() == 'pending') { | |
targets = this.hashes(hashes); | |
cnt = files.length; | |
descs = []; | |
if (cnt > 1) { | |
if (!$.map(files, function(f) { return f.mime == 'directory' ? 1 : null ; }).length) { | |
size = 0; | |
$.each(files, function(h, f) { | |
if (f.size && f.size != 'unknown') { | |
var s = parseInt(f.size); | |
if (s >= 0 && size >= 0) { | |
size += s; | |
} | |
} else { | |
size = 'unknown'; | |
return false; | |
} | |
}); | |
descs.push(fm.i18n('size')+': '+fm.formatSize(size)); | |
} | |
text = [$(tpl.replace('{class}', 'elfinder-cwd-icon-group').replace('{title}', '<strong>' + fm.i18n('items')+ ': ' + cnt + '</strong>').replace('{desc}', descs.join('<br>')))]; | |
} else { | |
f = files[0]; | |
if (f.tmb) { | |
tmb = fm.option('tmbUrl')+f.tmb; | |
} | |
if (f.size) { | |
descs.push(fm.i18n('size')+': '+fm.formatSize(f.size)); | |
} | |
descs.push(fm.i18n('modify')+': '+fm.formatDate(f)); | |
fname = fm.escape(f.i18 || f.name).replace(/([_.])/g, '​$1'); | |
text = [$(tpl.replace('{class}', fm.mime2class(f.mime)).replace('{title}', '<strong>' + fname + '</strong>').replace('{desc}', descs.join('<br>')))]; | |
} | |
text.push('confirmRm'); | |
fm.lockfiles({files : targets}); | |
dialog = fm.confirm({ | |
title : self.title, | |
text : text, | |
accept : { | |
label : 'btnRm', | |
callback : function() { | |
fm.request({ | |
data : {cmd : 'rm', targets : targets}, | |
notify : {type : 'rm', cnt : cnt}, | |
preventFail : true | |
}) | |
.fail(function(error) { | |
dfrd.reject(error); | |
}) | |
.done(function(data) { | |
dfrd.done(data); | |
}) | |
.always(function() { | |
fm.unlockfiles({files : targets}); | |
}); | |
} | |
}, | |
cancel : { | |
label : 'btnCancel', | |
callback : function() { | |
fm.unlockfiles({files : targets}); | |
fm.selectfiles({files : targets}); | |
dfrd.reject(); | |
} | |
} | |
}); | |
// load thumbnail | |
if (tmb) { | |
$('<img/>') | |
.load(function() { dialog.find('.elfinder-cwd-icon').css('background', 'url("'+tmb+'") center center no-repeat'); }) | |
.attr('src', tmb); | |
} | |
} | |
return dfrd; | |
} | |
} | |
/* | |
* File: /js/commands/search.js | |
*/ | |
/** | |
* @class elFinder command "search" | |
* Find files | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.search = function() { | |
this.title = 'Find files'; | |
this.options = {ui : 'searchbutton'} | |
this.alwaysEnabled = true; | |
this.updateOnSelect = false; | |
/** | |
* Return command status. | |
* Search does not support old api. | |
* | |
* @return Number | |
**/ | |
this.getstate = function() { | |
return 0; | |
} | |
/** | |
* Send search request to backend. | |
* | |
* @param String search string | |
* @return $.Deferred | |
**/ | |
this.exec = function(q, target, mime) { | |
var fm = this.fm, | |
reqDef; | |
if (typeof q == 'string' && q) { | |
if (typeof target == 'object') { | |
mime = target.mime || ''; | |
target = target.target || ''; | |
} | |
target = target? target : ''; | |
mime = mime? $.trim(mime).replace(',', ' ').split(' ') : []; | |
$.each(mime, function(){ return $.trim(this); }); | |
fm.trigger('searchstart', {query : q, target : target, mimes : mime}); | |
reqDef = fm.request({ | |
data : {cmd : 'search', q : q, target : target, mimes : mime}, | |
notify : {type : 'search', cnt : 1, hideCnt : true}, | |
cancel : true | |
}); | |
return reqDef; | |
} | |
fm.getUI('toolbar').find('.'+fm.res('class', 'searchbtn')+' :text').focus(); | |
return $.Deferred().reject(); | |
} | |
} | |
/* | |
* File: /js/commands/sort.js | |
*/ | |
/** | |
* @class elFinder command "sort" | |
* Change sort files rule | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.sort = function() { | |
var self = this, | |
fm = self.fm, | |
timer; | |
/** | |
* Command options | |
* | |
* @type Object | |
*/ | |
this.options = {ui : 'sortbutton'}; | |
fm.bind('open sortchange', function() { | |
self.variants = []; | |
$.each(fm.sortRules, function(name, value) { | |
var sort = { | |
type : name, | |
order : name == fm.sortType ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder | |
}; | |
var arr = name == fm.sortType ? (sort.order == 'asc'? 'n' : 's') : ''; | |
self.variants.push([sort, (arr? '<span class="ui-icon ui-icon-arrowthick-1-'+arr+'"></span>' : '') + ' ' + fm.i18n('sort'+name)]); | |
}); | |
}); | |
fm.bind('open sortchange viewchange search searchend', function() { | |
timer && clearTimeout(timer); | |
timer = setTimeout(function(){ | |
var cols = $(fm.cwd).find('div.elfinder-cwd-wrapper-list table'); | |
if (cols.length) { | |
$.each(fm.sortRules, function(name, value) { | |
var td = cols.find('thead tr td.elfinder-cwd-view-th-'+name); | |
if (td.length) { | |
var current = ( name == fm.sortType), | |
sort = { | |
type : name, | |
order : current ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder | |
},arr; | |
if (current) { | |
td.addClass('ui-state-active'); | |
arr = fm.sortOrder == 'asc' ? 'n' : 's'; | |
$('<span class="ui-icon ui-icon-triangle-1-'+arr+'"/>').appendTo(td); | |
} | |
$(td).on('click', function(e){ | |
e.stopPropagation(); | |
self.exec([], sort); | |
}) | |
.hover(function() { | |
$(this).addClass('ui-state-hover'); | |
},function() { | |
$(this).removeClass('ui-state-hover'); | |
}); | |
} | |
}); | |
} | |
}, 100); | |
}); | |
this.getstate = function() { | |
return 0; | |
}; | |
this.exec = function(hashes, sortopt) { | |
var fm = this.fm, | |
sort = $.extend({ | |
type : fm.sortType, | |
order : fm.sortOrder, | |
stick : fm.sortStickFolders | |
}, sortopt); | |
this.fm.setSort(sort.type, sort.order, sort.stick); | |
return $.Deferred().resolve(); | |
}; | |
}; | |
/* | |
* File: /js/commands/up.js | |
*/ | |
/** | |
* @class elFinder command "up" | |
* Go into parent directory | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.up = function() { | |
this.alwaysEnabled = true; | |
this.updateOnSelect = false; | |
this.shortcuts = [{ | |
pattern : 'ctrl+up' | |
}]; | |
this.getstate = function() { | |
return this.fm.cwd().phash ? 0 : -1; | |
} | |
this.exec = function() { | |
return this.fm.cwd().phash ? this.fm.exec('open', this.fm.cwd().phash) : $.Deferred().reject(); | |
} | |
} | |
/* | |
* File: /js/commands/upload.js | |
*/ | |
/** | |
* @class elFinder command "upload" | |
* Upload files using iframe or XMLHttpRequest & FormData. | |
* Dialog allow to send files using drag and drop | |
* | |
* @type elFinder.command | |
* @author Dmitry (dio) Levashov | |
*/ | |
elFinder.prototype.commands.upload = function() { | |
var hover = this.fm.res('class', 'hover'); | |
this.disableOnSearch = true; | |
this.updateOnSelect = false; | |
// Shortcut opens dialog | |
this.shortcuts = [{ | |
pattern : 'ctrl+u' | |
}]; | |
/** | |
* Return command state | |
* | |
* @return Number | |
**/ | |
this.getstate = function(sel) { | |
var fm = this.fm, f, | |
sel = fm.directUploadTarget? [fm.directUploadTarget] : (sel || [fm.cwd().hash]); | |
if (!this._disabled && sel.length == 1) { | |
f = fm.file(sel[0]); | |
} | |
return (f && f.mime == 'directory' && f.write)? 0 : -1; | |
}; | |
this.exec = function(data) { | |
var fm = this.fm, | |
targets = data && (data instanceof Array)? data : null, | |
check = !targets && data && data.target? [ data.target ] : targets, | |
fmUpload = function(data) { | |
fm.upload(data) | |
.fail(function(error) { | |
dfrd.reject(error); | |
}) | |
.done(function(data) { | |
var cwd = fm.getUI('cwd'); | |
dfrd.resolve(data); | |
if (data && data.added && data.added[0]) { | |
var newItem = cwd.find('#'+fm.cwdHash2Id(data.added[0].hash)); | |
if (newItem.length) { | |
newItem.trigger('scrolltoview'); | |
} | |
} | |
}); | |
}, | |
upload = function(data) { | |
dialog.elfinderdialog('close'); | |
if (targets) { | |
data.target = targets[0]; | |
} | |
fmUpload(data); | |
}, | |
dfrd = $.Deferred(), | |
dialog, input, button, dropbox, pastebox, dropUpload, paste; | |
if (this.getstate(check) < 0) { | |
return dfrd.reject(); | |
} | |
dropUpload = function(e) { | |
e.stopPropagation(); | |
e.preventDefault(); | |
var file = false, | |
type = '', | |
elfFrom = null, | |
mycwd = '', | |
data = null, | |
target = e._target || null; | |
try { elfFrom = e.dataTransfer.getData('elfinderfrom'); } catch(e) {} | |
if (elfFrom) { | |
mycwd = window.location.href + fm.cwd().hash; | |
if ((!target && elfFrom === mycwd) || target === mycwd) { | |
dfrd.reject(); | |
return; | |
} | |
} | |
try{ | |
data = e.dataTransfer.getData('text/html'); | |
if (!data.match(/<(?:img|a)/i)) { | |
data = ''; | |
} | |
} catch(e) {} | |
if (data) { | |
file = [ data ]; | |
type = 'html'; | |
} else if (e.dataTransfer && e.dataTransfer.items && e.dataTransfer.items.length) { | |
file = e.dataTransfer; | |
type = 'data'; | |
} else if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length) { | |
file = e.dataTransfer.files; | |
type = 'files'; | |
} else if (data = e.dataTransfer.getData('text')) { | |
file = [ data ]; | |
type = 'text'; | |
} | |
if (file) { | |
fmUpload({files : file, type : type, target : target}); | |
} else { | |
dfrd.reject(); | |
} | |
}; | |
if (!targets && data) { | |
if (data.input || data.files) { | |
data.type = 'files'; | |
fmUpload(data); | |
} else if (data.dropEvt) { | |
dropUpload(data.dropEvt); | |
} | |
return dfrd; | |
} | |
paste = function(e) { | |
var e = e.originalEvent || e; | |
var files = [], items = []; | |
var file; | |
if (e.clipboardData) { | |
if (e.clipboardData.items && e.clipboardData.items.length){ | |
items = e.clipboardData.items; | |
for (var i=0; i < items.length; i++) { | |
if (e.clipboardData.items[i].kind == 'file') { | |
file = e.clipboardData.items[i].getAsFile(); | |
files.push(file); | |
} | |
} | |
} else if (e.clipboardData.files && e.clipboardData.files.length) { | |
files = e.clipboardData.files; | |
} | |
if (files.length) { | |
upload({files : files, type : 'files'}); | |
return; | |
} | |
} | |
var my = e.target || e.srcElement; | |
setTimeout(function () { | |
if (my.innerHTML) { | |
$(my).find('img').each(function(i, v){ | |
if (v.src.match(/^webkit-fake-url:\/\//)) { | |
// For Safari's bug. | |
// ref. https://bugs.webkit.org/show_bug.cgi?id=49141 | |
// https://dev.ckeditor.com/ticket/13029 | |
$(v).remove(); | |
} | |
}); | |
var src = my.innerHTML.replace(/<br[^>]*>/gi, ' '); | |
var type = src.match(/<[^>]+>/)? 'html' : 'text'; | |
my.innerHTML = ''; | |
upload({files : [ src ], type : type}); | |
} | |
}, 1); | |
}; | |
input = $('<input type="file" multiple="true"/>') | |
.change(function() { | |
upload({input : input[0], type : 'files'}); | |
}) | |
.on('dragover', function(e) { | |
e.originalEvent.dataTransfer.dropEffect = 'copy'; | |
}); | |
button = $('<div class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">'+fm.i18n('selectForUpload')+'</span></div>') | |
.append($('<form/>').append(input)) | |
.hover(function() { | |
button.toggleClass(hover) | |
}); | |
dialog = $('<div class="elfinder-upload-dialog-wrapper"/>') | |
.append(button); | |
pastebox = $('<div class="ui-corner-all elfinder-upload-dropbox" contenteditable="true">'+fm.i18n('dropFilesBrowser')+'</div>') | |
.on('paste drop', function(e){ | |
paste(e); | |
}) | |
.on('mousedown click', function(){ | |
$(this).focus(); | |
}) | |
.on('focus', function(){ | |
this.innerHTML = ''; | |
}) | |
.on('dragenter mouseover', function(){ | |
pastebox.addClass(hover); | |
}) | |
.on('dragleave mouseout', function(){ | |
pastebox.removeClass(hover); | |
}); | |
if (fm.dragUpload) { | |
dropbox = $('<div class="ui-corner-all elfinder-upload-dropbox" contenteditable="true">'+fm.i18n('dropPasteFiles')+'</div>') | |
.on('paste', function(e){ | |
paste(e); | |
}) | |
.on('mousedown click', function(){ | |
$(this).focus(); | |
}) | |
.on('focus', function(){ | |
this.innerHTML = ''; | |
}) | |
.on('mouseover', function(){ | |
$(this).addClass(hover); | |
}) | |
.on('mouseout', function(){ | |
$(this).removeClass(hover); | |
}) | |
.prependTo(dialog) | |
.after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0]; | |
dropbox.addEventListener('dragenter', function(e) { | |
e.stopPropagation(); | |
e.preventDefault(); | |
$(dropbox).addClass(hover); | |
}, false); | |
dropbox.addEventListener('dragleave', function(e) { | |
e.stopPropagation(); | |
e.preventDefault(); | |
$(dropbox).removeClass(hover); | |
}, false); | |
dropbox.addEventListener('dragover', function(e) { | |
e.stopPropagation(); | |
e.preventDefault(); | |
e.dataTransfer.dropEffect = 'copy'; | |
$(dropbox).addClass(hover); | |
}, false); | |
dropbox.addEventListener('drop', function(e) { | |
dialog.elfinderdialog('close'); | |
targets && (e._target = targets[0]); | |
dropUpload(e); | |
}, false); | |
} else { | |
pastebox | |
.prependTo(dialog) | |
.after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0]; | |
} | |
fm.dialog(dialog, { | |
title : this.title + (targets? ' - ' + fm.escape(fm.file(targets[0]).name) : ''), | |
modal : true, | |
resizable : false, | |
destroyOnClose : true | |
}); | |
return dfrd; | |
}; | |
}; | |
/* | |
* File: /js/commands/view.js | |
*/ | |
/** | |
* @class elFinder command "view" | |
* Change current directory view (icons/list) | |
* | |
* @author Dmitry (dio) Levashov | |
**/ | |
elFinder.prototype.commands.view = function() { | |
this.value = this.fm.viewType; | |
this.alwaysEnabled = true; | |
this.updateOnSelect = false; | |
this.options = { ui : 'viewbutton'}; | |
this.getstate = function() { | |
return 0; | |
} | |
this.exec = function() { | |
var value = this.fm.storage('view', this.value == 'list' ? 'icons' : 'list'); | |
this.fm.viewchange(); | |
this.update(void(0), value); | |
} | |
} | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment