2019 lines
50 KiB
JavaScript
2019 lines
50 KiB
JavaScript
(function ($, Drupal) {
|
|
'use strict';
|
|
|
|
/**
|
|
* @file
|
|
* Defines Imce File Manager.
|
|
*/
|
|
|
|
/**
|
|
* Global container.
|
|
*/
|
|
var imce = window.imce = {
|
|
// Configuration options
|
|
conf: {},
|
|
// Locally stored data
|
|
local: {},
|
|
// Events
|
|
events: {},
|
|
// Shortcuts
|
|
shortcuts: {fm: {}, tree: {}, content: {}},
|
|
// Toolbar buttons
|
|
toolbarButtons: {},
|
|
// Folder tree
|
|
tree: {},
|
|
// Currently selected items
|
|
selection: [],
|
|
// Message queue
|
|
messageQueue: [],
|
|
// Sort handlers
|
|
sorters: {}
|
|
};
|
|
|
|
/**
|
|
* Initiate imce on document ready.
|
|
*/
|
|
$(document).ready(function () {
|
|
|
|
var settings = window.drupalSettings;
|
|
var conf = settings && settings.imce;
|
|
var body = document.body;
|
|
if (conf && !imce.activeFolder && $(body).hasClass('imce-page')) {
|
|
if (!conf.ajax_url) {
|
|
conf.ajax_url = Drupal.url(settings.path.currentPath);
|
|
}
|
|
imce.init(conf, body);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Initialize imce.
|
|
*/
|
|
imce.init = function (conf, parentEl) {
|
|
// Set conf
|
|
conf = $.extend(imce.conf, conf);
|
|
if (!conf.ajax_url || !conf.folders || !conf.root_url) {
|
|
return;
|
|
}
|
|
imce.parentEl = parentEl;
|
|
// Get stored data
|
|
try {
|
|
$.extend(imce.local, JSON.parse(localStorage.getItem('imce.local')));
|
|
}
|
|
catch (err) {
|
|
imce.delayError(err);
|
|
}
|
|
// Init
|
|
imce.trigger('preinit');
|
|
imce.checkIntegration();
|
|
imce.initUI();
|
|
imce.initTree();
|
|
// Add shortcuts
|
|
imce.addShortcut('ENTER', imce.eTreeEnter, 'tree');
|
|
imce.addShortcut('UP', imce.eTreeUp, 'tree');
|
|
imce.addShortcut('DOWN', imce.eTreeDown, 'tree');
|
|
imce.addShortcut('LEFT', imce.eTreeLR, 'tree');
|
|
imce.addShortcut('RIGHT', imce.eTreeLR, 'tree');
|
|
imce.addShortcut('ENTER', imce.eContentEnter, 'content');
|
|
imce.addShortcut('UP', imce.eContentArrow, 'content');
|
|
imce.addShortcut('DOWN', imce.eContentArrow, 'content');
|
|
imce.addShortcut('LEFT', imce.eContentArrow, 'content');
|
|
imce.addShortcut('RIGHT', imce.eContentArrow, 'content');
|
|
imce.addShortcut('Ctrl+A', imce.eContentCtrlA, 'content');
|
|
// Add refresh button
|
|
imce.addTbb('refresh', {
|
|
title: Drupal.t('Refresh'),
|
|
permission: 'browse_files|browse_subfolders',
|
|
handler: imce.eFmRefresh,
|
|
shortcut: 'F5',
|
|
icon: 'refresh'
|
|
});
|
|
// Register default activeFolder handler
|
|
imce.bind('activateFolder', imce.defaultActivateFolder);
|
|
// Trigger init handlers
|
|
imce.trigger('init');
|
|
// Create sendto toolbar button if needed.
|
|
imce.createSendtoTbb();
|
|
// Add the file manager to the page
|
|
parentEl.appendChild(imce.fmEl);
|
|
// Set window events
|
|
$(window).bind('beforeunload', imce.eWinBeforeunload).bind('resize', imce.eWinResize);
|
|
imce.eWinResize();
|
|
// Content focus
|
|
imce.contentEl.focus();
|
|
// Set initial messages
|
|
imce.ajaxProcessMessages(conf);
|
|
// Open active path
|
|
var path = conf.active_path;
|
|
var Folder = path && imce.addFolder(path);
|
|
if (!Folder) {
|
|
for (path in conf.folders) {
|
|
if (Folder = imce.getFolder(path)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (Folder) {
|
|
Folder.open();
|
|
}
|
|
// Triger postinit
|
|
imce.trigger('postinit');
|
|
};
|
|
|
|
/**
|
|
* Init UI elements.
|
|
*/
|
|
imce.initUI = function () {
|
|
var el = imce.fmEl;
|
|
var createEl = imce.createEl;
|
|
if (el) {
|
|
return el;
|
|
}
|
|
el = imce.fmEl = createEl('<div id="imce-fm"></div>');
|
|
el.onkeydown = imce.eFmKeydown;
|
|
el.tabIndex = 0;
|
|
// Toolbar
|
|
el.appendChild(imce.toolbarEl = createEl('<div id="imce-toolbar" aria-label="Operations" role="toolbar"></div>'));
|
|
// Body
|
|
el.appendChild(imce.bodyEl = createEl('<div id="imce-body"></div>'));
|
|
// Tree
|
|
el = imce.treeEl = createEl('<div id="imce-tree"></div>');
|
|
el.onkeydown = imce.eTreeKeydown;
|
|
el.onmousedown = imce.eTreeMousedown;
|
|
el.ontouchstart = imce.eTreeTouchstart;
|
|
el.tabIndex = 0;
|
|
imce.bodyEl.appendChild(el);
|
|
// Tree resizer
|
|
el = imce.treeResizerEl = createEl('<div id="imce-tree-resizer"></div>');
|
|
el.onmousedown = imce.eTreeResizerMousedown;
|
|
el.ontouchstart = imce.eTreeResizerTouchstart;
|
|
imce.bodyEl.appendChild(el);
|
|
// Content
|
|
el = imce.contentEl = createEl('<div id="imce-content"></div>');
|
|
if (imce.conf.thumbnail_grid_style) {
|
|
el.className = 'thumbnail-grid';
|
|
}
|
|
el.onmousedown = imce.eContentMousedown;
|
|
el.ontouchstart = imce.eContentTouchstart;
|
|
el.onkeydown = imce.eContentKeydown;
|
|
el.onscroll = imce.eContentScroll;
|
|
el.tabIndex = 0;
|
|
imce.bodyEl.appendChild(el);
|
|
// Content header
|
|
el = imce.contentHeaderEl = imce.createEl('<div class="imce-content-header"><div class="imce-item"><div class="imce-item-date" data-sort="date">' + Drupal.t('Date') + '</div><div class="imce-item-height" data-sort="height">' + Drupal.t('Height') + '</div><div class="imce-item-width" data-sort="width">' + Drupal.t('Width') + '</div><div class="imce-item-size" data-sort="size">' + Drupal.t('Size') + '</div><div class="imce-item-icon imce-ficon" data-sort="ext"></div><div class="imce-item-name" data-sort="name">' + Drupal.t('Name') + '</div></div></div>');
|
|
el.onclick = imce.eContentHeaderClick;
|
|
imce.contentEl.appendChild(el);
|
|
// Content status
|
|
el = imce.contentStatusEl = imce.createEl('<div class="imce-content-status"></div>');
|
|
imce.contentEl.appendChild(el);
|
|
// Body resizer
|
|
el = imce.bodyResizerEl = createEl('<div id="imce-body-resizer"></div>');
|
|
el.onmousedown = imce.eBodyResizerMousedown;
|
|
el.ontouchstart = imce.eBodyResizerTouchstart;
|
|
imce.fmEl.appendChild(el);
|
|
// Preview
|
|
imce.fmEl.appendChild(imce.previewEl = createEl('<div id="imce-preview"></div>'));
|
|
return el;
|
|
};
|
|
|
|
/**
|
|
* Init folder tree.
|
|
*/
|
|
imce.initTree = function () {
|
|
var path;
|
|
var folders = imce.getConf('folders');
|
|
// Create root
|
|
var scheme = imce.getConf('scheme');
|
|
var root = new imce.Folder(scheme ? scheme + '://' : '<' + Drupal.t('root') + '>');
|
|
root.setPath('.');
|
|
root.branchEl.className += ' root';
|
|
imce.treeEl.appendChild(root.branchEl);
|
|
// Create predefined folders in alphabetical order.
|
|
var paths = [];
|
|
for (path in folders) {
|
|
if (imce.owns(folders, path)) {
|
|
paths.push(path);
|
|
}
|
|
}
|
|
paths.sort();
|
|
for (var i = 0; path = paths[i]; i++) {
|
|
imce.addFolder(path, folders[path]);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns a folder by path.
|
|
*/
|
|
imce.getFolder = function (path) {
|
|
if (imce.owns(imce.tree, path)) {
|
|
return imce.tree[path];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns an item by path.
|
|
*/
|
|
imce.getItem = function (path) {
|
|
var Folder;
|
|
var parts = imce.splitPath(path);
|
|
if (parts) {
|
|
if (Folder = imce.getFolder(parts[0])) {
|
|
return Folder.getItem(parts[1]);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Adds a folder to the tree.
|
|
*/
|
|
imce.addFolder = function (path, conf) {
|
|
var parts;
|
|
var parent;
|
|
var Folder = imce.getFolder(path);
|
|
// Existing
|
|
if (Folder) {
|
|
if (conf) {
|
|
Folder.setConf(conf);
|
|
}
|
|
return Folder;
|
|
}
|
|
// New. Append to the parent.
|
|
if (parts = imce.splitPath(path)) {
|
|
if (parent = imce.addFolder(parts[0])) {
|
|
Folder = new imce.Folder(parts[1], conf);
|
|
parent.appendItem(Folder);
|
|
return Folder;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Add a toolbar button.
|
|
*/
|
|
imce.addTbb = function (id, opt) {
|
|
return imce.getTbb(id) || new imce.Tbb(id, opt);
|
|
};
|
|
|
|
/**
|
|
* Returns a toolbar button.
|
|
*/
|
|
imce.getTbb = function (id) {
|
|
return imce.toolbarButtons[id];
|
|
};
|
|
|
|
/**
|
|
* Returns a configuration option.
|
|
*/
|
|
imce.getConf = function (name, defaultValue) {
|
|
var value;
|
|
var conf = imce.conf;
|
|
if (!name) {
|
|
return conf;
|
|
}
|
|
value = conf[name];
|
|
return value == null ? defaultValue : value;
|
|
};
|
|
|
|
|
|
/**
|
|
* Returns a copy of selected items.
|
|
*/
|
|
imce.getSelection = function () {
|
|
return imce.selection.slice(0);
|
|
};
|
|
|
|
/**
|
|
* Counts selected items.
|
|
*/
|
|
imce.countSelection = function () {
|
|
return this.selection.length;
|
|
};
|
|
|
|
/**
|
|
* Returns the selected items grouped by parent folder and type.
|
|
*/
|
|
imce.groupSelection = function () {
|
|
return imce.groupItems(imce.selection);
|
|
};
|
|
|
|
/**
|
|
* Select all items in the active folder.
|
|
*/
|
|
imce.selectAll = function () {
|
|
var Folder = imce.activeFolder;
|
|
if (Folder) {
|
|
Folder.selectAll();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Deselects all items.
|
|
*/
|
|
imce.deselectAll = function () {
|
|
var i;
|
|
var arr = imce.getSelection();
|
|
for (i in arr) {
|
|
if (imce.owns(arr, i)) {
|
|
arr[i].deselect();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns last selected item.
|
|
*/
|
|
imce.getLastSelected = function () {
|
|
var arr = imce.selection;
|
|
var len = arr.length;
|
|
if (len) {
|
|
return arr[len - 1];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Adds an item to the selection.
|
|
*/
|
|
imce.selectItem = function (Item) {
|
|
if (!Item.selected) {
|
|
var arr = imce.selection;
|
|
var oldlen = arr.length;
|
|
arr.push(Item);
|
|
Item.setState('selected');
|
|
if (oldlen < 2) {
|
|
imce.updatePreview();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Removes an item from the selection.
|
|
*/
|
|
imce.deselectItem = function (Item) {
|
|
if (Item.selected) {
|
|
var arr = imce.selection;
|
|
var i = $.inArray(Item, arr);
|
|
Item.unsetState('selected');
|
|
if (i !== -1) {
|
|
arr.splice(i, 1);
|
|
if (arr.length < 2) {
|
|
imce.updatePreview();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Loads item uuids by ajax.
|
|
*/
|
|
imce.loadItemUuids = function (items, callback) {
|
|
var i;
|
|
var Item;
|
|
var missing = [];
|
|
var loaded = [];
|
|
for (i in items) {
|
|
Item = items[i];
|
|
if (Item && Item.isFile) {
|
|
if (Item.uuid) {
|
|
loaded.push(Item);
|
|
}
|
|
else {
|
|
missing.push(Item);
|
|
}
|
|
}
|
|
}
|
|
// All loaded
|
|
if (!missing.length) {
|
|
if (callback) {
|
|
callback(loaded);
|
|
}
|
|
return loaded;
|
|
}
|
|
// Load missing uuids
|
|
return imce.ajaxItems('uuid', missing, {
|
|
customComplete: function(xhr, status) {
|
|
var path;
|
|
var Item;
|
|
var response = this.response;
|
|
if (response && response.uuids) {
|
|
for (path in response.uuids) {
|
|
if (Item = imce.getItem(path)) {
|
|
Item.uuid = response.uuids[path];
|
|
loaded.push(Item);
|
|
}
|
|
}
|
|
}
|
|
if (callback) {
|
|
callback(loaded);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Checks external application integration by URL parameters.
|
|
*
|
|
* Ex-1: http://example.com/imce?sendto=HANDLER
|
|
* Creates a sendto operation that calls HANDLER(File, imceWin) of the parent window.
|
|
* Ex-2: http://example.com/imce?urlField=FIELD-ID
|
|
* Creates a sendto operation that fills the field in parent window with the selected file's URL.
|
|
* Ex-3: http://example.com/imce?oninit=HANDLER
|
|
* Calls HANDLER() with imce context when the UI is ready.
|
|
*/
|
|
imce.checkIntegration = function () {
|
|
var query = imce.getQuery();
|
|
var handler;
|
|
var urlField;
|
|
var parentWin = window.opener || window.parent;
|
|
if (imce.parentWin = parentWin) {
|
|
// Check sendto handler
|
|
if (handler = imce.resolveHandler(query.sendto, parentWin)) {
|
|
imce.sendtoHandler = handler;
|
|
}
|
|
// Check url field
|
|
else if (urlField = query.urlField) {
|
|
if (urlField = parentWin.document.getElementById(urlField)) {
|
|
imce.sendtoHandler = function (Item, win) {
|
|
try {
|
|
imce.parentWin.focus();
|
|
(imce.parentWin.jQuery||$)(urlField).val(Item.getUrl()).blur().change().focus();
|
|
}
|
|
catch (err) {
|
|
imce.delayError(err);
|
|
}
|
|
win.close();
|
|
};
|
|
}
|
|
}
|
|
// Check init handler
|
|
if (handler = imce.resolveHandler(query.oninit, parentWin)) {
|
|
imce.bind('init', handler);
|
|
}
|
|
// Store sendto type
|
|
if (imce.sendtoHandler && query.type) {
|
|
imce.sendtoType = query.type;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates the sendto toolbar button.
|
|
*/
|
|
imce.createSendtoTbb = function (title, desc) {
|
|
if (imce.sendtoHandler && !imce.getTbb('sendto')) {
|
|
return imce.addTbb('sendto', {
|
|
title: title || Drupal.t('Select'),
|
|
tooltip: desc || Drupal.t('Use the selected file.'),
|
|
handler: function () {
|
|
imce.runSendtoHandler();
|
|
},
|
|
icon: 'check'
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Runs custom sendto handler on the first selected item.
|
|
*/
|
|
imce.runSendtoHandler = function (items) {
|
|
var handler = imce.sendtoHandler;
|
|
if (handler) {
|
|
var Item;
|
|
var imgType = imce.sendtoType === 'image';
|
|
items = items || imce.getSelection();
|
|
for (var i in items) {
|
|
if (imce.owns(items, i)) {
|
|
Item = items[i];
|
|
if (imgType ? Item.isImageSource() : Item.isFile) {
|
|
return handler(Item, window);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Default handler for activateFolder event.
|
|
*/
|
|
imce.defaultActivateFolder = function (Folder, oldFolder) {
|
|
// Enable/disable toolbar buttons by permission.
|
|
var i;
|
|
var j;
|
|
var Tbb;
|
|
var perm;
|
|
var disabled;
|
|
var buttons = imce.toolbarButtons;
|
|
for (i in buttons) {
|
|
if (!imce.owns(buttons, i)) {
|
|
continue;
|
|
}
|
|
Tbb = buttons[i];
|
|
if (perm = Tbb.permission) {
|
|
perm = perm.split('|');
|
|
disabled = true;
|
|
for (j in perm) {
|
|
if (Folder.getPermission(perm[j])) {
|
|
disabled = false;
|
|
break;
|
|
}
|
|
}
|
|
Tbb.setDisabled(disabled);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Updates the active sort state in the content header.
|
|
*/
|
|
imce.updateHeader = function () {
|
|
var newsort = imce.activeFolder.activeSort;
|
|
var oldsort = imce.activeSort || {};
|
|
var el = imce.contentHeaderEl;
|
|
// Check if the sort has changed.
|
|
if (newsort && (newsort.key !== oldsort.key || newsort.desc !== oldsort.desc)) {
|
|
// Deactivate existing column
|
|
if (oldsort.key) {
|
|
$('[data-sort="' + oldsort.key + '"]', el).removeClass('sorted ' + (oldsort.desc ? 'desc' : 'asc'));
|
|
}
|
|
// Activate new column
|
|
$('[data-sort="' + newsort.key + '"]', el).addClass('sorted ' + (newsort.desc ? 'desc' : 'asc'));
|
|
// Store the values
|
|
imce.activeSort = newsort;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Schedule preview updating.
|
|
*/
|
|
imce.updatePreview = function () {
|
|
clearTimeout(imce.previewTimer);
|
|
imce.previewTimer = setTimeout(imce.doUpdatePreview, 100);
|
|
};
|
|
|
|
/**
|
|
* Set preview of currently selected item.
|
|
*/
|
|
imce.doUpdatePreview = function () {
|
|
imce.previewItem(imce.countSelection() === 1 ? imce.getLastSelected() : null);
|
|
};
|
|
|
|
/**
|
|
* Sets/clears item preview.
|
|
*/
|
|
imce.previewItem = function (Item) {
|
|
var currentItem = imce.previewingItem;
|
|
if (imce.previewingItem = Item) {
|
|
$(imce.previewEl).html(Item.createPreviewEl());
|
|
imce.trigger('previewItem', Item);
|
|
}
|
|
else if (currentItem) {
|
|
imce.previewEl.innerHTML = '';
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Schedule status update.
|
|
*/
|
|
imce.updateStatus = function () {
|
|
clearTimeout(imce.statusTimer);
|
|
imce.statusTimer = setTimeout(imce.doUpdateStatus, 100);
|
|
};
|
|
|
|
/**
|
|
* Updates active folder status.
|
|
*/
|
|
imce.doUpdateStatus = function () {
|
|
var Folder = imce.activeFolder;
|
|
if (Folder) {
|
|
$(imce.contentStatusEl).html(Folder.formatStatus());
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Returns name filtering regexp.
|
|
*/
|
|
imce.getNameFilter = function () {
|
|
var filters = imce.getConf('name_filters', []);
|
|
// Dot files
|
|
if (!imce.getConf('allow_dot_files')) {
|
|
filters.push('^\\.|\\.$');
|
|
}
|
|
return filters.length ? new RegExp(filters.join('|')) : false;
|
|
};
|
|
|
|
/**
|
|
* Groups an array of items by parent folder and type.
|
|
*/
|
|
imce.groupItems = function (items) {
|
|
var i;
|
|
var Item;
|
|
var path;
|
|
var selected;
|
|
var key;
|
|
var names;
|
|
var group = {};
|
|
for (i in items) {
|
|
if (!imce.owns(items, i)) {
|
|
continue;
|
|
}
|
|
Item = items[i];
|
|
path = Item.parent.getPath();
|
|
selected = group[path] = imce.owns(group, path) ? group[path] : {};
|
|
key = Item.isFolder ? 'subfolders' : 'files';
|
|
names = selected[key] = selected[key] || {};
|
|
names[Item.name] = Item;
|
|
}
|
|
return group;
|
|
};
|
|
|
|
/**
|
|
* Checks parent folder permissions of the given items.
|
|
*/
|
|
imce.validatePermissions = function (items, filePerm, subfolderPerm) {
|
|
var path;
|
|
var Folder;
|
|
var selected;
|
|
var groups = imce.groupItems(items);
|
|
for (path in groups) {
|
|
if (!imce.owns(groups, path)) {
|
|
continue;
|
|
}
|
|
Folder = imce.getFolder(path);
|
|
selected = groups[path];
|
|
// Check file permission if the selection contains files
|
|
if (selected.files && (filePerm == null || !Folder.getPermission(filePerm))) {
|
|
return false;
|
|
}
|
|
// Check folder permission if the selection contains folders
|
|
if (selected.subfolders && (subfolderPerm == null || !Folder.getPermission(subfolderPerm))) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Checks if items contain any predefined folder.
|
|
*/
|
|
imce.validatePredefinedPath = function (items) {
|
|
var i;
|
|
var Item;
|
|
var Folder;
|
|
for (i in items) {
|
|
if (!imce.owns(items, i)) {
|
|
continue;
|
|
}
|
|
Item = items[i];
|
|
if (Item.isFolder) {
|
|
if (Folder = Item.hasPredefinedPath()) {
|
|
imce.setMessage(Drupal.t('%path is a predefined path and can not be modified.', {'%path': Folder.getPath()}));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Validates the number of items.
|
|
*/
|
|
imce.validateCount = function (items) {
|
|
if (!items.length) {
|
|
imce.setMessage(Drupal.t('Please select a file.'));
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Validates item extensions against an allowed list.
|
|
*/
|
|
imce.validateExtensions = function (items, exts) {
|
|
for (var i in items) {
|
|
if (imce.owns(items, i) && !imce.validateExtension(items[i].ext, exts)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Validates an extension against an allowed list.
|
|
*/
|
|
imce.validateExtension = function (ext, exts) {
|
|
if (!ext || $.inArray(ext.toLowerCase(), exts.toLowerCase().split(/[\s,]+/)) === -1) {
|
|
imce.setMessage(Drupal.t('Only files with the following extensions are allowed: %files-allowed.', {'%files-allowed': exts}));
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Validates a file name.
|
|
*/
|
|
imce.validateFileName = function (name) {
|
|
// Basic validation
|
|
if (!name || name === '.' || name === '..' || !name.length || name.length > 240) {
|
|
return false;
|
|
}
|
|
// Test name filters
|
|
var regex = imce.getNameFilter();
|
|
if (regex && regex.test(name)) {
|
|
imce.setMessage(Drupal.t('%filename is not allowed.', {'%filename': name}));
|
|
return false;
|
|
}
|
|
// Test chars forbidden in various operating systems.
|
|
if (/^\s|\s$|[\/\\:\*\?\x22<>\|\x00-\x1F]/.test(name)) {
|
|
imce.setMessage(Drupal.t('%filename contains invalid characters. Use only alphanumeric characters for better portability.', {'%filename': name}));
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Validates min/max image dimensions.
|
|
*/
|
|
imce.validateDimensions = function (items, width, height) {
|
|
// Check min dimensions
|
|
if (width < 1 || height < 1) {
|
|
return false;
|
|
}
|
|
// Check max dimensions.
|
|
var maxwidth = imce.getConf('maxwidth');
|
|
var maxheight = imce.getConf('maxheight');
|
|
if (maxwidth && width > maxwidth || maxheight && height > maxheight) {
|
|
imce.setMessage(Drupal.t('Image dimensions must be smaller than %dimensions pixels.', {'%dimensions': maxwidth + 'x' + maxwidth}));
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Checks if all the selected items are images.
|
|
*/
|
|
imce.validateImageTypes = function (items) {
|
|
var Item = imce.getFirstItem(items, 'width', false);
|
|
if (Item) {
|
|
imce.setMessage(Drupal.t('%name is not an image.', {'%name': Item.name}));
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
|
|
/**
|
|
* Keydown event for the file manager.
|
|
*/
|
|
imce.eFmKeydown = function (event) {
|
|
return imce.eFireShortcut.call(this, event);
|
|
};
|
|
|
|
/**
|
|
* Refresh handler for the file manager.
|
|
*/
|
|
imce.eFmRefresh = function () {
|
|
imce.activeFolder.load();
|
|
};
|
|
|
|
/**
|
|
* Mousedown event for folder tree.
|
|
*/
|
|
imce.eTreeMousedown = function (event) {
|
|
// Manually focus as the browser default might have been prevented.
|
|
this.focus();
|
|
};
|
|
|
|
/**
|
|
* Touchstart event for folder tree.
|
|
*/
|
|
imce.eTreeTouchstart = function (event) {
|
|
this.focus();
|
|
};
|
|
|
|
/**
|
|
* Keydown event for folder tree.
|
|
*/
|
|
imce.eTreeKeydown = function (event) {
|
|
return imce.eFireShortcut.call(this, event, 'tree');
|
|
};
|
|
|
|
/**
|
|
* Tree shortcut: Enter.
|
|
*/
|
|
imce.eTreeEnter = function () {
|
|
imce.activeFolder.open();
|
|
};
|
|
|
|
/**
|
|
* Tree shortcut: UP.
|
|
*/
|
|
imce.eTreeUp = function (e) {
|
|
var Folder = imce.activeFolder;
|
|
var prvEl;
|
|
var prvFolder;
|
|
if (prvEl = Folder.branchEl.previousSibling) {
|
|
if (prvFolder = prvEl.Folder) {
|
|
while (prvFolder.expanded) {
|
|
if (prvEl = prvFolder.subtreeEl.lastChild) {
|
|
prvFolder = prvEl.Folder;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
prvFolder = Folder.parent;
|
|
}
|
|
if (prvFolder) {
|
|
prvFolder.activate();
|
|
imce.scrollToEl(prvFolder.branchEl, imce.treeEl);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Tree shortcut: DOWN.
|
|
*/
|
|
imce.eTreeDown = function (e) {
|
|
var Folder = imce.activeFolder;
|
|
var nextEl;
|
|
var nextFolder;
|
|
if (Folder.expanded && (nextEl = Folder.subtreeEl.firstChild)) {
|
|
nextFolder = nextEl.Folder;
|
|
}
|
|
else {
|
|
// noinspection Eslint
|
|
do {
|
|
if (nextEl = Folder.branchEl.nextSibling) {
|
|
nextFolder = nextEl.Folder;
|
|
break;
|
|
}
|
|
} while (Folder = Folder.parent);
|
|
}
|
|
if (nextFolder) {
|
|
nextFolder.activate();
|
|
imce.scrollToEl(nextFolder.branchEl, imce.treeEl);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Tree shortcut: LEFT/RIGHT.
|
|
*/
|
|
imce.eTreeLR = function (e) {
|
|
var Folder = imce.activeFolder;
|
|
if (e.keyCode === 39 ^ Folder.expanded) {
|
|
$(Folder.branchToggleEl).click();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Mousedown event for tree resizer.
|
|
*/
|
|
imce.eTreeResizerMousedown = function (event) {
|
|
return imce.eTreeResizerDown.call(this, imce.eFix(event));
|
|
};
|
|
|
|
/**
|
|
* Touch start event for tree resizer.
|
|
*/
|
|
imce.eTreeResizerTouchstart = function (event) {
|
|
return imce.eCommonTouchstart(event, imce.eTreeResizerDown, this);
|
|
};
|
|
|
|
/**
|
|
* Common Down event for tree resizer.
|
|
*/
|
|
imce.eTreeResizerDown = function (e, isTouch) {
|
|
this.startX = e.pageX;
|
|
this.startW = $(imce.treeEl).width();
|
|
this.maxW = this.startW + $(imce.contentEl).width();
|
|
imce.bindDragDrop(imce.eTreeResizerDrag, null, null, isTouch);
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Drag event for tree resizer.
|
|
*/
|
|
imce.eTreeResizerDrag = function (e) {
|
|
var el = imce.treeResizerEl;
|
|
$(imce.treeEl).width(Math.min(el.maxW, Math.max(el.startW + e.pageX - el.startX, 0)));
|
|
e.preventDefault();
|
|
};
|
|
|
|
/**
|
|
* Mousedown event for content area.
|
|
*/
|
|
imce.eContentMousedown = function (event) {
|
|
// Manually focus as the browser default might have been prevented.
|
|
this.focus();
|
|
};
|
|
|
|
/**
|
|
* Touchstart event for content area.
|
|
*/
|
|
imce.eContentTouchstart = function (event) {
|
|
this.focus();
|
|
};
|
|
|
|
/**
|
|
* Keydown event for content area.
|
|
*/
|
|
imce.eContentKeydown = function (event) {
|
|
return imce.eFireShortcut.call(this, event, 'content');
|
|
};
|
|
|
|
/**
|
|
* Scroll event for content area.
|
|
*/
|
|
imce.eContentScroll = function (event) {
|
|
imce.updateContentPositions();
|
|
setTimeout(imce.updateContentPositions);
|
|
};
|
|
|
|
/**
|
|
* Click event for content header.
|
|
*/
|
|
imce.eContentHeaderClick = function (event) {
|
|
var key;
|
|
var e = imce.eFix(event);
|
|
var Folder = imce.activeFolder;
|
|
var sort = Folder.activeSort || {};
|
|
if (key = e.target.getAttribute('data-sort')) {
|
|
Folder.sortItems(key, key === sort.key ? !sort.desc : sort.desc);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Update content header position on content scroll.
|
|
*/
|
|
imce.updateContentPositions = function () {
|
|
var top = imce.contentEl.scrollTop;
|
|
imce.contentHeaderEl.style.top = top + 'px';
|
|
imce.contentStatusEl.style.bottom = -top + 'px';
|
|
};
|
|
|
|
/**
|
|
* Content shortcut: ENTER.
|
|
*/
|
|
imce.eContentEnter = function (e) {
|
|
var Item = imce.getLastSelected();
|
|
if (Item) {
|
|
Item.dblClick();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Content shortcut: Ctrl+A.
|
|
*/
|
|
imce.eContentCtrlA = function (e) {
|
|
imce.selectAll();
|
|
};
|
|
|
|
/**
|
|
* Content shortcut: LEFT/RIGHT/UP/DOWN
|
|
*/
|
|
imce.eContentArrow = function (e) {
|
|
var Item;
|
|
var i = 0;
|
|
var Folder = imce.activeFolder;
|
|
var key = e.keyCode;
|
|
if (Item = imce.getLastSelected()) {
|
|
i = Folder.indexOf(Item) + (key % 2 ? key - 38 : imce.countElPerRow(Item.el) * (key - 39));
|
|
}
|
|
if (Item = Folder.getItemAt(i)) {
|
|
Item.click(e);
|
|
Item.scrollIntoView();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Mousedown event for body resizer.
|
|
*/
|
|
imce.eBodyResizerMousedown = function (event) {
|
|
return imce.eBodyResizerDown.call(this, imce.eFix(event));
|
|
};
|
|
|
|
/**
|
|
* Touch start event for body resizer.
|
|
*/
|
|
imce.eBodyResizerTouchstart = function (event) {
|
|
return imce.eCommonTouchstart(event, imce.eBodyResizerDown, this);
|
|
};
|
|
|
|
/**
|
|
* Common Down event for body resizer.
|
|
*/
|
|
imce.eBodyResizerDown = function (e, isTouch) {
|
|
this.startY = e.pageY;
|
|
this.startH = $(imce.bodyEl).height();
|
|
this.maxH = this.startH + $(imce.previewEl).height();
|
|
imce.bindDragDrop(imce.eBodyResizerDrag, null, null, isTouch);
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Drag event for body resizer.
|
|
*/
|
|
imce.eBodyResizerDrag = function (e) {
|
|
var el = imce.bodyResizerEl;
|
|
var bodyH = Math.min(Math.max(el.startH + e.pageY - el.startY, 0), el.maxH);
|
|
$(imce.bodyEl).height(bodyH);
|
|
$(imce.previewEl).height(el.maxH - bodyH);
|
|
e.preventDefault();
|
|
};
|
|
|
|
/**
|
|
* Beforeunload event for window.
|
|
*/
|
|
imce.eWinBeforeunload = function (e) {
|
|
// Store active sort.
|
|
var data = {};
|
|
if (data.activeSort = imce.activeSort) {
|
|
imce.trigger('storeLocalData', data);
|
|
try {
|
|
localStorage.setItem('imce.local', JSON.stringify(data));
|
|
}
|
|
catch (err) {
|
|
imce.delayError(err);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Resize event for window.
|
|
*/
|
|
imce.eWinResize = function (e) {
|
|
var pdiff;
|
|
var diff = imce.getWindowSize().height - imce.fmEl.offsetHeight;
|
|
// Distribute the excess space to the body and preview elements.
|
|
if (diff) {
|
|
var $bodyEl = $(imce.bodyEl);
|
|
var $prvEl = $(imce.previewEl);
|
|
if ($prvEl[0].offsetHeight) {
|
|
pdiff = parseInt(diff / 2);
|
|
diff -= pdiff;
|
|
$prvEl.height($prvEl.height() + pdiff);
|
|
}
|
|
$bodyEl.height($bodyEl.height() + diff);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Binds an handler by type.
|
|
*/
|
|
imce.bind = function (type, handler) {
|
|
var events = imce.events;
|
|
var handlers = events[type];
|
|
if (!handlers) {
|
|
handlers = events[type] = {};
|
|
}
|
|
handlers['' + handler] = handler;
|
|
};
|
|
|
|
/**
|
|
* Unbinds an handler by type.
|
|
*/
|
|
imce.unbind = function (type, handler) {
|
|
var events = imce.events;
|
|
var handlers = events[type];
|
|
if (handlers) {
|
|
if (1 in arguments) {
|
|
delete handlers['' + handler];
|
|
}
|
|
else {
|
|
delete events[type];
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Triggers handlers by type.
|
|
*/
|
|
imce.trigger = function (type) {
|
|
var i;
|
|
var handler;
|
|
var handlers = imce.events[type];
|
|
var ret = [];
|
|
if (handlers) {
|
|
for (i in handlers) {
|
|
if (handler = handlers[i]) {
|
|
if (handler.apply) {
|
|
ret.push(handler.apply(imce, Array.prototype.slice.call(arguments, 1)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
|
|
/**
|
|
* Adds a shortcut handler to an area.
|
|
*/
|
|
imce.addShortcut = function (shortcut, handler, area) {
|
|
var shortcuts;
|
|
if (shortcuts = imce.getAreaShortcuts(area)) {
|
|
shortcuts[shortcut.toUpperCase()] = handler;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns a shortcut handler.
|
|
*/
|
|
imce.getShortcut = function (shortcut, area) {
|
|
var shortcuts;
|
|
if (shortcuts = imce.getAreaShortcuts(area)) {
|
|
return shortcuts[shortcut.toUpperCase()];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Removes a shortcut handler.
|
|
*/
|
|
imce.removeShortcut = function (shortcut, area) {
|
|
var shortcuts;
|
|
if (shortcuts = imce.getAreaShortcuts(area)) {
|
|
delete shortcuts[shortcut.toUpperCase()];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Executes a shortcut handler.
|
|
* Returns true if shortcut exists and is executed successfully.
|
|
*/
|
|
imce.fireShortcut = function (shortcut, area) {
|
|
var handler = imce.getShortcut(shortcut, area);
|
|
if (handler) {
|
|
// DOM element
|
|
if (handler.click) {
|
|
handler.click();
|
|
return true;
|
|
}
|
|
// Callback
|
|
if (handler.apply) {
|
|
// Shortcuts returning false are considered disabled.
|
|
return handler.apply(this, Array.prototype.slice.call(arguments, 2)) !== false;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns a shortcut handler.
|
|
*/
|
|
imce.getAreaShortcuts = function (area) {
|
|
if (!area) {
|
|
area = 'fm';
|
|
}
|
|
return imce.shortcuts[area];
|
|
};
|
|
|
|
|
|
/**
|
|
* Builds a shortcut string from an event.
|
|
*/
|
|
imce.eBuildShortcut = function (e) {
|
|
var symbol;
|
|
var key = e.keyCode;
|
|
var shortcut = '';
|
|
if (key && (symbol = imce.getKeySymbols(key))) {
|
|
if (e.ctrlKey) {
|
|
shortcut += 'CTRL+';
|
|
}
|
|
if (e.altKey) {
|
|
shortcut += 'ALT+';
|
|
}
|
|
if (e.shiftKey) {
|
|
shortcut += 'SHIFT+';
|
|
}
|
|
shortcut += symbol;
|
|
}
|
|
return shortcut;
|
|
};
|
|
|
|
/**
|
|
* Event helper for imce shortcut firing.
|
|
*/
|
|
imce.eFireShortcut = function (event, area) {
|
|
var e = event || window.event;
|
|
var shortcut = imce.eBuildShortcut(e);
|
|
// Prevent default if shortcut is executed.
|
|
if (shortcut) {
|
|
e = $.event.fix(e);
|
|
if (imce.fireShortcut.call(this, shortcut, area, e)) {
|
|
e.stopPropagation();
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns key symbols allowed in shortcuts.
|
|
*/
|
|
imce.getKeySymbols = function (key) {
|
|
var i;
|
|
var symbols = imce.keySymbols;
|
|
if (!symbols) {
|
|
// Custom keys
|
|
symbols = imce.keySymbols = {
|
|
8: 'BACKSPACE',
|
|
9: 'TAB',
|
|
13: 'ENTER',
|
|
27: 'ESC',
|
|
32: 'SPACE',
|
|
37: 'LEFT',
|
|
38: 'UP',
|
|
39: 'RIGHT',
|
|
40: 'DOWN',
|
|
46: 'DEL'
|
|
};
|
|
// Add numbers
|
|
for (i = 0; i < 10; i++) {
|
|
symbols[48 + i] = '' + i;
|
|
}
|
|
// Add letters
|
|
for (i = 65; i < 91; i++) {
|
|
symbols[i] = String.fromCharCode(i);
|
|
}
|
|
// Add function keys
|
|
for (i = 1; i < 13; i++) {
|
|
symbols[111 + i] = 'F' + i;
|
|
}
|
|
}
|
|
return (0 in arguments) ? symbols[key] : symbols;
|
|
};
|
|
|
|
|
|
/**
|
|
* Creates an ajax request for a specific operation.
|
|
*/
|
|
imce.ajax = function (jsop, opt) {
|
|
return $.ajax(imce.ajaxPrepare(jsop, opt));
|
|
};
|
|
|
|
/**
|
|
* Prepares ajax options.
|
|
*/
|
|
imce.ajaxPrepare = function (jsop, opt) {
|
|
// Prepare data
|
|
var path;
|
|
var Folder = opt && opt.activeFolder != null ? opt.activeFolder : imce.activeFolder;
|
|
var data = {jsop: jsop, token: imce.getConf('token')};
|
|
if (Folder) {
|
|
if (path = Folder.getPath()) {
|
|
data.active_path = path;
|
|
}
|
|
}
|
|
// Extend defaults
|
|
return $.extend(true, imce.ajaxDefaults(), {data: data, activeFolder: Folder}, opt);
|
|
};
|
|
|
|
/**
|
|
* Returns ajax default options.
|
|
*/
|
|
imce.ajaxDefaults = function () {
|
|
return {
|
|
url: imce.getConf('ajax_url'),
|
|
type: 'POST',
|
|
dataType: 'json',
|
|
beforeSend: imce.ajaxBeforeSend,
|
|
success: imce.ajaxSuccess,
|
|
error: imce.ajaxError,
|
|
complete: imce.ajaxComplete
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Creates an ajax request for a specific operation on the given items.
|
|
*/
|
|
imce.ajaxItems = function (jsop, items, opt) {
|
|
return imce.ajax(jsop, $.extend(true, imce.ajaxItemsOpt(items), opt));
|
|
};
|
|
|
|
/**
|
|
* Creates an ajax options object including the items as the selection data.
|
|
*/
|
|
imce.ajaxItemsOpt = function (items) {
|
|
return {data: {selection: imce.getItemPaths(items)}};
|
|
};
|
|
|
|
/**
|
|
* Default before send handler.
|
|
*/
|
|
imce.ajaxBeforeSend = function (xhr, opt) {
|
|
var handler;
|
|
var Folder;
|
|
if (handler = opt.customBeforeSend) {
|
|
if (handler.apply(this, arguments) === false) {
|
|
opt.activeFolder = null;
|
|
return false;
|
|
}
|
|
}
|
|
if (Folder = opt.activeFolder) {
|
|
Folder.setBusy(true);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Default ajax success handler.
|
|
*/
|
|
imce.ajaxSuccess = function (response, status) {
|
|
var handler;
|
|
var opt = this;
|
|
// Make the response available in complete handlers.
|
|
opt.response = response;
|
|
imce.ajaxProcessResponse(response);
|
|
if (handler = opt.customSuccess) {
|
|
handler.apply(opt, arguments);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Default ajax complete handler.
|
|
*/
|
|
imce.ajaxComplete = function (xhr, status) {
|
|
var Folder;
|
|
var handler;
|
|
var opt = this;
|
|
if (Folder = opt.activeFolder) {
|
|
Folder.setBusy(false);
|
|
}
|
|
if (handler = opt.customComplete) {
|
|
handler.apply(opt, arguments);
|
|
}
|
|
opt.response = opt.activeFolder = null;
|
|
};
|
|
|
|
/**
|
|
* Default ajax error handler.
|
|
*/
|
|
imce.ajaxError = function (xhr, status, e) {
|
|
if (status !== 'abort') {
|
|
imce.setMessage('<pre class="imce-ajax-error">' + Drupal.checkPlain(imce.ajaxErrorMessage(xhr, this.url)) + '</pre>');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Processes the ajax response.
|
|
*/
|
|
imce.ajaxProcessResponse = function (response) {
|
|
if (response) {
|
|
imce.ajaxProcessRemoved(response);
|
|
imce.ajaxProcessAdded(response);
|
|
imce.ajaxProcessMessages(response);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Processes the added items in the response.
|
|
*/
|
|
imce.ajaxProcessAdded = function (response) {
|
|
var path;
|
|
var Folder;
|
|
var added;
|
|
if (added = response.added) {
|
|
for (path in added) {
|
|
if (Folder = imce.addFolder(path)) {
|
|
Folder.addContent(added[path], true);
|
|
imce.contentEl.focus();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Processes the removed items in the response.
|
|
*/
|
|
imce.ajaxProcessRemoved = function (response) {
|
|
var i;
|
|
var Item;
|
|
var paths = response.removed;
|
|
if (paths) {
|
|
for (i in paths) {
|
|
if (Item = imce.getItem(paths[i])) {
|
|
Item.remove();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Processes the messages in the response.
|
|
*/
|
|
imce.ajaxProcessMessages = function (response) {
|
|
var i;
|
|
var type;
|
|
var msgs = response.messages;
|
|
if (msgs) {
|
|
for (type in msgs) {
|
|
if (imce.owns(msgs, type)) {
|
|
for (i in msgs[type]) {
|
|
if (msgs[type].hasOwnProperty(i)) {
|
|
imce.setMessage(msgs[type][i], type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Generates an ajax error message.
|
|
*/
|
|
imce.ajaxErrorMessage = function (xhr, url) {
|
|
var msg = Drupal.t('An AJAX HTTP error occurred.');
|
|
msg += '\n' + Drupal.t('Path: !uri', {'!uri': url});
|
|
msg += '\n' + Drupal.t('HTTP Result Code: !status', {'!status': xhr.status || 0});
|
|
msg += '\n' + Drupal.t('StatusText: !statusText', {'!statusText': xhr.statusText || 'N/A'});
|
|
msg += '\n' + Drupal.t('ResponseText: !responseText', {'!responseText': xhr.responseText || 'N/A'});
|
|
return msg;
|
|
};
|
|
|
|
/**
|
|
* Returns an array of item paths.
|
|
*/
|
|
imce.getItemPaths = function (items) {
|
|
return $.map(items, imce.getItemPath);
|
|
};
|
|
|
|
/**
|
|
* Returns the path of an item.
|
|
*/
|
|
imce.getItemPath = function (Item) {
|
|
return Item.getPath();
|
|
};
|
|
|
|
|
|
/**
|
|
* Set a status message.
|
|
*/
|
|
imce.setMessage = function (msg, type) {
|
|
if (!type) {
|
|
type = 'error';
|
|
}
|
|
var mq = imce.messageQueue;
|
|
var len = mq.length;
|
|
var msgId = msg + ':' + type;
|
|
// Skip if it's identical to the last message
|
|
if (len && mq[len - 1].msgId === msgId) {
|
|
return false;
|
|
}
|
|
// Add the message
|
|
mq[len] = imce.createMessageEl(msg, type);
|
|
mq[len].msgId = msgId;
|
|
// Schedule the processing at a later time to queue new messages.
|
|
if (!imce.pmqScheduled) {
|
|
imce.pmqScheduled = setTimeout(imce.processMessageQueue, 100);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Process message queue.
|
|
*/
|
|
imce.processMessageQueue = function () {
|
|
var mq = imce.messageQueue;
|
|
if (mq.length) {
|
|
// Display all messages
|
|
$(imce.createMessagePopupEl()).html(mq).fadeIn(200);
|
|
// Empty array.
|
|
mq.length = 0;
|
|
// Mousedown close
|
|
$(document).bind('mousedown', imce.eMPopDocMousedown);
|
|
// Auto close
|
|
imce.mPopCloseTimer = setTimeout(imce.mPopClose, 2500);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Closes currently open message popup.
|
|
*/
|
|
imce.mPopClose = function () {
|
|
// Time up but still hovering. Do not close. A new timer will be set on mouseout.
|
|
if (imce.mPopHovering) {
|
|
imce.mPopCloseTimerUp = 1;
|
|
return imce.mPopCloseTimerUp;
|
|
}
|
|
// Time up or mousedown
|
|
clearTimeout(imce.mPopCloseTimer);
|
|
imce.mPopCloseTimerUp = 0;
|
|
$(document).unbind('mousedown', imce.eMPopDocMousedown);
|
|
$(imce.messagePopupEl).fadeOut(400, imce.processMessageQueueNext);
|
|
};
|
|
|
|
/**
|
|
* Continue processing the remaining messages.
|
|
*/
|
|
imce.processMessageQueueNext = function () {
|
|
imce.pmqScheduled = 0;
|
|
if (imce.messageQueue.length) {
|
|
imce.pmqScheduled = setTimeout(imce.processMessageQueue, 250);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Mouseover event for message popup.
|
|
*/
|
|
imce.eMPopMouseenter = function (e) {
|
|
imce.mPopHovering = 1;
|
|
// Clear the shorter timer set on mouseleave
|
|
if (imce.mPopCloseTimerUp) {
|
|
clearTimeout(imce.mPopCloseTimer);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Mouseout event for message popup.
|
|
*/
|
|
imce.eMPopMouseleave = function (e) {
|
|
imce.mPopHovering = 0;
|
|
// Set a shorter close timer if the long time is up
|
|
if (imce.mPopCloseTimerUp) {
|
|
imce.mPopCloseTimer = setTimeout(imce.mPopClose, 2000);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Mousedown event for document in order to close message popup.
|
|
*/
|
|
imce.eMPopDocMousedown = function (e) {
|
|
// Close the popup if the mousedown is outside of it.
|
|
if (!imce.mPopHovering) {
|
|
imce.mPopClose();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates a message element.
|
|
*/
|
|
imce.createMessageEl = function (msg, type) {
|
|
var el = imce.createEl('<div class="imce-message imce-ficon"><div class="imce-message-content"></div></div>');
|
|
el.className += ' ' + type;
|
|
el.firstChild.innerHTML = msg;
|
|
return el;
|
|
};
|
|
|
|
/**
|
|
* Creates the message popup element.
|
|
*/
|
|
imce.createMessagePopupEl = function () {
|
|
var el = imce.messagePopupEl;
|
|
if (!el) {
|
|
el = imce.messagePopupEl = imce.createLayer('imce-message-popup', imce.fmEl);
|
|
$(el).hover(imce.eMPopMouseenter, imce.eMPopMouseleave);
|
|
}
|
|
return el;
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks a permission in a folder conf.
|
|
*/
|
|
imce.permissionInFolderConf = function (permission, folderConf) {
|
|
var permissions = folderConf && folderConf.permissions;
|
|
return !!(permissions && ((permission in permissions) ? permissions[permission] : permissions.all));
|
|
};
|
|
|
|
/**
|
|
* Checks if a permission exists in any of the predefined folders.
|
|
*/
|
|
imce.hasPermission = function (permission, conf) {
|
|
var i;
|
|
var folders = (conf || imce.conf).folders;
|
|
if (folders) {
|
|
for (i in folders) {
|
|
if (imce.permissionInFolderConf(permission, folders[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Sorting helpers.
|
|
*/
|
|
imce.sortText = function (a, b) {
|
|
return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
|
|
};
|
|
imce.sortNum = function (a, b) {
|
|
return (a || 0) - (b || 0);
|
|
};
|
|
imce.sortNumericProperty = function (a, b, prop) {
|
|
// Do not change sort within folders
|
|
var result = (a.isFolder ? -1 : 1);
|
|
if (a.isFolder === b.isFolder) {
|
|
result = imce.sortNum(a[prop] || 0, b[prop] || 0);
|
|
}
|
|
return result;
|
|
};
|
|
imce.sortBranchName = function (a, b) {
|
|
return imce.sortText(a.name, b.name);
|
|
};
|
|
|
|
/**
|
|
* Property sorters.
|
|
*/
|
|
imce.sorters.name = function (a, b) {
|
|
var result = (a.isFolder ? -1 : 1);
|
|
if (a.isFolder === b.isFolder) {
|
|
result = imce.sortText(a.name, b.name);
|
|
}
|
|
return result;
|
|
};
|
|
imce.sorters.date = function (a, b) {
|
|
return imce.sortNumericProperty(a, b, 'date');
|
|
};
|
|
imce.sorters.size = function (a, b) {
|
|
return imce.sortNumericProperty(a, b, 'size');
|
|
};
|
|
imce.sorters.width = function (a, b) {
|
|
return imce.sortNumericProperty(a, b, 'width');
|
|
};
|
|
imce.sorters.height = function (a, b) {
|
|
return imce.sortNumericProperty(a, b, 'height');
|
|
};
|
|
imce.sorters.ext = function (a, b) {
|
|
var result = (a.isFolder ? -1 : 1);
|
|
if (a.isFolder === b.isFolder) {
|
|
result = (a.isFolder ? 0 : imce.sortText(a.ext || '', b.ext || ''));
|
|
}
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* Splits a path into dirpath and filename.
|
|
*/
|
|
imce.splitPath = function (path) {
|
|
if (typeof path === 'string' && path !== '') {
|
|
var parts = path.split('/');
|
|
var filename = parts.pop();
|
|
var dirpath = parts.join('/');
|
|
if (filename !== '') {
|
|
return [dirpath === '' ? '.' : dirpath, filename];
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates a file path by joining a folder path and a filename.
|
|
*/
|
|
imce.joinPaths = function (dirpath, filename) {
|
|
if (dirpath === '.') {
|
|
return filename;
|
|
}
|
|
if (filename === '.') {
|
|
return dirpath;
|
|
}
|
|
if (dirpath.substr(-1) !== '/') {
|
|
dirpath += '/';
|
|
}
|
|
return dirpath + filename;
|
|
};
|
|
|
|
/**
|
|
* Returns query parameters from the current URL.
|
|
*/
|
|
imce.getQuery = function (name) {
|
|
var i;
|
|
var part;
|
|
var parts;
|
|
var str;
|
|
var query = imce.query;
|
|
if (!query) {
|
|
query = imce.query = {};
|
|
if (str = location.search) {
|
|
parts = str.substr(1).split('&');
|
|
for (i in parts) {
|
|
if (imce.owns(parts, i)) {
|
|
part = parts[i].split('=');
|
|
query[imce.decode(part[0])] = part[1] ? imce.decode(part[1]) : '';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return name ? query[name] : query;
|
|
};
|
|
|
|
/**
|
|
* Wrapper around decodeURIComponent.
|
|
* Avoids malformed uri exception.
|
|
*/
|
|
imce.decode = function (str) {
|
|
try {
|
|
str = decodeURIComponent(str);
|
|
}
|
|
catch (err) {
|
|
imce.delayError(err);
|
|
}
|
|
return str;
|
|
};
|
|
|
|
/**
|
|
* Throws an error after a minimum delay.
|
|
*/
|
|
imce.delayError = function (err) {
|
|
setTimeout(function () {
|
|
throw err;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Formats item date.
|
|
*/
|
|
imce.formatDate = function (timestamp, dayOnly) {
|
|
var D;
|
|
var p0;
|
|
var ret = '';
|
|
if (timestamp) {
|
|
D = new Date(timestamp * 1000);
|
|
p0 = imce.prependZero;
|
|
ret = D.getFullYear() + '-' + p0(D.getMonth() + 1) + '-' + p0(D.getDate());
|
|
if (!dayOnly) {
|
|
ret += ' ' + p0(D.getHours()) + ':' + p0(D.getMinutes());
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
/**
|
|
* Formats item size.
|
|
*/
|
|
imce.formatSize = function (size) {
|
|
if (size == null) {
|
|
return '';
|
|
}
|
|
if (!size || size < 100) {
|
|
return Drupal.formatPlural(size, '1 byte', '@count bytes', {'@count': size || 0});
|
|
}
|
|
if (size < 1048576) {
|
|
return Drupal.t('@size KB', {'@size': imce.round(size / 1024, 1)});
|
|
}
|
|
return Drupal.t('@size MB', {'@size': imce.round(size / 1048576, 1)});
|
|
};
|
|
|
|
/**
|
|
* Formats content items status.
|
|
*/
|
|
imce.formatItemsStatus = function (count, size) {
|
|
return Drupal.t('!items (!size)', {
|
|
'!items': Drupal.formatPlural(count, '1 item', '@count items'),
|
|
'!size': imce.formatSize(size)
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Prepends 0 to numbers smaller than 10.
|
|
*/
|
|
imce.prependZero = function (num) {
|
|
return num < 10 ? '0' + num : num;
|
|
};
|
|
|
|
/**
|
|
* Rounds a number to the given precision
|
|
*/
|
|
imce.round = function (num, precision) {
|
|
var n = Math.pow(10, precision);
|
|
return Math.round(num * n) / n;
|
|
};
|
|
|
|
/**
|
|
* Returns the extension of a file name.
|
|
*/
|
|
imce.getExt = function (name) {
|
|
var pos = name.lastIndexOf('.');
|
|
return pos === -1 ? '' : name.substr(pos + 1);
|
|
};
|
|
|
|
/**
|
|
* Checks if an object owns a property.
|
|
*/
|
|
imce.owns = function (object, property) {
|
|
return Object.prototype.hasOwnProperty.call(object, property);
|
|
};
|
|
|
|
/**
|
|
* Returns the first item that has a property.
|
|
*/
|
|
imce.getFirstItem = function (items, prop, state) {
|
|
var i;
|
|
var item;
|
|
if (typeof state === "undefined") {
|
|
state = true;
|
|
}
|
|
for (i in items) {
|
|
if (imce.owns(items, i)) {
|
|
item = items[i];
|
|
if (!prop || (item[prop] ? state : !state)) {
|
|
return item;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Resolves a string to a handler under the given scope.
|
|
*/
|
|
imce.resolveHandler = function (str, scope) {
|
|
if (!str) {
|
|
return;
|
|
}
|
|
var i;
|
|
var obj = scope || window;
|
|
var parts = str.split('.');
|
|
var len = parts.length;
|
|
for (i = 0; i < len && (obj = obj[parts[i]]); i++) {
|
|
// empty
|
|
}
|
|
return i === len && obj && obj.call && obj.apply ? obj : false;
|
|
};
|
|
|
|
/**
|
|
* Creates a DOM element from html string.
|
|
*/
|
|
imce.createEl = function (html) {
|
|
var el;
|
|
var div = imce._div;
|
|
if (!div) {
|
|
div = imce._div = document.createElement('div');
|
|
}
|
|
div.innerHTML = html;
|
|
el = div.firstChild;
|
|
div.removeChild(el);
|
|
return el;
|
|
};
|
|
|
|
/**
|
|
* Creates a layer element.
|
|
*/
|
|
imce.createLayer = function (cname, parent) {
|
|
var layer = imce.createEl('<div class="imce-layer"></div>');
|
|
if (cname) {
|
|
layer.className += ' ' + cname;
|
|
}
|
|
if (parent !== false) {
|
|
(parent || document.body).appendChild(layer);
|
|
}
|
|
return layer;
|
|
};
|
|
|
|
/**
|
|
* Removes element without removing events.
|
|
*/
|
|
imce.removeEl = function (el) {
|
|
var parent = el.parentNode;
|
|
if (parent) {
|
|
parent.removeChild(el);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns window inner size.
|
|
*/
|
|
imce.getWindowSize = function () {
|
|
return {
|
|
width: window.innerWidth || document.documentElement.clientWidth,
|
|
height: window.innerHeight || document.documentElement.clientHeight
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Returns scroll values of the window.
|
|
*/
|
|
imce.getWindowScroll = function () {
|
|
if (typeof window.pageXOffset === "undefined") {
|
|
var el = document.documentElement;
|
|
return {left: el.scrollLeft, top: el.scrollTop};
|
|
}
|
|
return {left: window.pageXOffset, top: window.pageYOffset};
|
|
};
|
|
|
|
/**
|
|
* Fixes and converts an event into jQuery event.
|
|
*/
|
|
imce.eFix = function (event) {
|
|
return $.event.fix(event || window.event);
|
|
};
|
|
|
|
/**
|
|
* Scroll an element into view inside the scrollable wrapper.
|
|
*/
|
|
imce.scrollToEl = function (el, wrpEl, diffTop, diffBottom) {
|
|
if (el.offsetWidth && wrpEl.scrollHeight > wrpEl.clientHeight) {
|
|
var elTop = $(el).offset().top;
|
|
var elBottom = elTop + el.offsetHeight;
|
|
var wrpTop = $(wrpEl).offset().top;
|
|
var wrpBottom = wrpTop + wrpEl.offsetHeight;
|
|
wrpTop += diffTop || 0;
|
|
wrpBottom -= diffBottom || 0;
|
|
// Check top positions
|
|
if (elTop < wrpTop) {
|
|
wrpEl.scrollTop -= wrpTop - elTop + 10;
|
|
}
|
|
else if (wrpBottom < elBottom) {
|
|
// Consider el height might be bigger than the wrapper height.
|
|
// Get the minimum among top space and required scroll.
|
|
wrpEl.scrollTop += Math.min(elBottom - wrpBottom + 10, elTop - wrpTop - 10);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns number of the elements that can fit in a row inside the parent.
|
|
*/
|
|
imce.countElPerRow = function (el) {
|
|
return Math.max(1, parseInt(el.parentNode.clientWidth / $(el).outerWidth(true)));
|
|
};
|
|
|
|
/**
|
|
* Makes the element stay inside window boundaries.
|
|
*/
|
|
imce.fixPosition = function (el) {
|
|
var pos = $(el).offset();
|
|
var winSize = imce.getWindowSize();
|
|
var winScroll = imce.getWindowScroll();
|
|
var scrollbar = 18;
|
|
var extraX = pos.left - winScroll.left + el.offsetWidth - winSize.width + scrollbar;
|
|
var extraY = pos.top - winScroll.top + el.offsetHeight - winSize.height + scrollbar;
|
|
// Shift to left
|
|
if (extraX > 0) {
|
|
el.style.left = Math.max(0, pos.left - extraX) + 'px';
|
|
}
|
|
// Shift to top
|
|
if (extraY > 0) {
|
|
el.style.top = Math.max(0, pos.top - extraY) + 'px';
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Bind drag drop callbacks to the document
|
|
*/
|
|
imce.bindDragDrop = function (drag, drop, data, isTouch) {
|
|
var edata = {drag: drag, drop: drop, data: data, isTouch: isTouch};
|
|
$(document).bind(isTouch ? 'touchmove' : 'mousemove', edata, imce.eDocDrag).bind(isTouch ? 'touchend' : 'mouseup', edata, imce.eDocDrop);
|
|
};
|
|
|
|
/**
|
|
* Default drag event for document which is set by imce.bindDragDrop
|
|
*/
|
|
imce.eDocDrag = function (e) {
|
|
var edata = e.data;
|
|
// Call custom drag event if set.
|
|
if (edata.drag) {
|
|
// Fix touch event
|
|
if (edata.isTouch) {
|
|
e = imce.eTouchFix(e, e.originalEvent.changedTouches[0]);
|
|
}
|
|
return edata.drag.call(this, e);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Default drop event for document which is set by imce.bindDragDrop
|
|
*/
|
|
imce.eDocDrop = function (e) {
|
|
var edata = e.data;
|
|
$(document).unbind(edata.isTouch ? 'touchmove' : 'mousemove', imce.eDocDrag).unbind(edata.isTouch ? 'touchend' : 'mouseup', imce.eDocDrop);
|
|
// Call custom drop event if set.
|
|
if (edata.drop) {
|
|
// Fix touch event
|
|
if (edata.isTouch) {
|
|
e = imce.eTouchFix(e, e.originalEvent.changedTouches[0]);
|
|
}
|
|
return edata.drop.call(this, e);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Fix touch events
|
|
*/
|
|
imce.eTouchFix = function (e, touch) {
|
|
// Make sure e is a jquery event object that is writable.
|
|
e = $.event.fix(e);
|
|
if (touch && typeof touch.pageX !== "undefined") {
|
|
e.pageX = touch.pageX;
|
|
e.pageY = touch.pageY;
|
|
e.clientX = touch.clientX;
|
|
e.clientY = touch.clientY;
|
|
}
|
|
return e;
|
|
};
|
|
|
|
/**
|
|
* Common touchstart event.
|
|
*/
|
|
imce.eCommonTouchstart = function (event, callback, context) {
|
|
var touch;
|
|
var touches = event.changedTouches;
|
|
// Skip event for multi-touch
|
|
if (touches && (touch = touches[0]) && !touches[1]) {
|
|
if (callback && callback.call) {
|
|
return callback.call(context || this, imce.eTouchFix(event, touch), true);
|
|
}
|
|
// Prevent default.
|
|
return false;
|
|
}
|
|
};
|
|
|
|
})(jQuery, Drupal);
|