793 lines
17 KiB
JavaScript
793 lines
17 KiB
JavaScript
/*global imce:true*/
|
|
(function ($, Drupal, imce) {
|
|
'use strict';
|
|
|
|
/**
|
|
* @file
|
|
* Defines imce Folder object.
|
|
*/
|
|
|
|
/**
|
|
* Folder.
|
|
*/
|
|
imce.Folder = function (name, conf) {
|
|
this.construct(name, conf);
|
|
};
|
|
|
|
/**
|
|
* Item prototype
|
|
*/
|
|
var ItemProto = imce.Item.prototype;
|
|
|
|
/**
|
|
* Folder prototype extends Item prototype.
|
|
*/
|
|
var Folder = $.extend(imce.Folder.prototype, ItemProto);
|
|
|
|
/**
|
|
* Constructs the Folder.
|
|
*/
|
|
Folder.construct = function (name, conf) {
|
|
var Folder = this;
|
|
Folder.isFolder = true;
|
|
Folder.type = 'folder';
|
|
Folder.items = [];
|
|
Folder.files = {};
|
|
Folder.subfolders = {};
|
|
ItemProto.construct.apply(Folder, arguments);
|
|
Folder.setConf(conf);
|
|
};
|
|
|
|
/**
|
|
* Creates folder elements.
|
|
*/
|
|
Folder.createEl = function () {
|
|
var nameEl;
|
|
var toggleEl;
|
|
var branchEl;
|
|
var Folder = this;
|
|
if (!Folder.el) {
|
|
// Item elements.
|
|
ItemProto.createEl.apply(Folder, arguments);
|
|
Folder.el.className += ' folder';
|
|
// Folder elements
|
|
Folder.contentEl = imce.createEl('<div class="imce-folder-content clearfix"></div>');
|
|
Folder.subtreeEl = imce.createEl('<div class="imce-subtree"></div>');
|
|
branchEl = Folder.branchEl = imce.createEl('<div class="imce-branch"><span class="imce-branch-toggle"></span><span class="imce-branch-name imce-ficon"></span></div>');
|
|
toggleEl = Folder.branchToggleEl = branchEl.firstChild;
|
|
toggleEl.onclick = imce.eBranchToggleClick;
|
|
nameEl = Folder.branchNameEl = branchEl.children[1];
|
|
nameEl.onclick = imce.eBranchNameClick;
|
|
branchEl.Folder = nameEl.Folder = toggleEl.Folder = Folder;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sets the folder content.
|
|
*/
|
|
Folder.setContent = function (content) {
|
|
var i;
|
|
var Item;
|
|
var list;
|
|
var Folder = this;
|
|
var items = Folder.getItems();
|
|
// Remove the items that no longer exist.
|
|
for (i in items) {
|
|
if (!imce.owns(items, i)) {
|
|
continue;
|
|
}
|
|
Item = items[i];
|
|
list = Item.isFolder ? content.subfolders : content.files;
|
|
// Existing item is not in the list
|
|
if (!list || !imce.owns(list, Item.name)) {
|
|
// Make sure it's not (parent of) a predefined folder
|
|
if (!Item.isFolder || !Item.hasPredefinedPath()) {
|
|
Folder.removeItem(Item);
|
|
}
|
|
}
|
|
}
|
|
Folder.extend(content.props);
|
|
Folder.addContent(content);
|
|
Folder.content = content;
|
|
Folder.updateSubtree();
|
|
};
|
|
|
|
/**
|
|
* Adds new files and subfolders.
|
|
*/
|
|
Folder.addContent = function (content, selectNew) {
|
|
var Folder = this;
|
|
var files = content.files;
|
|
var subfolders = content.subfolders;
|
|
if (!files && !subfolders) {
|
|
return;
|
|
}
|
|
// Add items
|
|
Folder.addItems(files, 'file');
|
|
Folder.addItems(subfolders, 'folder');
|
|
// Update sort
|
|
if (Folder.active) {
|
|
Folder.sortItems();
|
|
}
|
|
else {
|
|
Folder.needSort = 1;
|
|
}
|
|
Folder.sortTree();
|
|
// Select new items.
|
|
if (selectNew && Folder.active) {
|
|
var name;
|
|
var fname;
|
|
var sname;
|
|
imce.deselectAll();
|
|
if (files) {
|
|
for (fname in files) {
|
|
if (!imce.owns(files, fname)) {
|
|
continue;
|
|
}
|
|
Folder.getItem(fname).select();
|
|
}
|
|
}
|
|
if (subfolders) {
|
|
for (sname in subfolders) {
|
|
if (!imce.owns(subfolders, sname)) {
|
|
continue;
|
|
}
|
|
Folder.getItem(sname).select();
|
|
}
|
|
}
|
|
// Scroll the last item into view
|
|
if (name = (fname || sname)) {
|
|
Folder.getItem(name).scrollIntoView();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Add a list of items of a specific type.
|
|
*/
|
|
Folder.addItems = function (items, type) {
|
|
var Item;
|
|
var name;
|
|
var Type = type === 'folder' ? imce.Folder : imce.File;
|
|
if (items) {
|
|
for (name in items) {
|
|
// Update
|
|
if (Item = this.getItem(name)) {
|
|
Item.extend(items[name]);
|
|
this.updateStatus();
|
|
}
|
|
// Insert
|
|
else {
|
|
Item = new Type(name);
|
|
Item.extend(items[name]);
|
|
this.appendItem(Item);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns a copy of items array.
|
|
*/
|
|
Folder.getItems = function () {
|
|
return this.items.slice(0);
|
|
};
|
|
|
|
/**
|
|
* Append an item to the folder.
|
|
*/
|
|
Folder.appendItem = function (Item) {
|
|
var Folder = this;
|
|
var name = Item.name;
|
|
var existing;
|
|
if (!Folder.validateAppend(Item)) {
|
|
return;
|
|
}
|
|
// Remove the item from old parent
|
|
Item.remove(true);
|
|
// Remove existing item with the same name
|
|
if (existing = Folder.getItem(name)) {
|
|
existing.remove();
|
|
}
|
|
// Append item.
|
|
Folder.items.push(Item);
|
|
Item.parent = Folder;
|
|
Folder.contentEl.appendChild(Item.el);
|
|
// Append subfolder
|
|
if (Item.isFolder) {
|
|
Folder.prepareSubtree();
|
|
Folder.subtreeEl.appendChild(Item.branchEl);
|
|
Folder.subfolders[name] = Item;
|
|
Item.setPath((Folder.parent ? Folder.path + '/' : '') + Item.name);
|
|
}
|
|
// Append file
|
|
else {
|
|
Folder.files[name] = Item;
|
|
}
|
|
// Update status.
|
|
Folder.updateStatus();
|
|
};
|
|
|
|
/**
|
|
* Remove an item from the folder.
|
|
*/
|
|
Folder.removeItem = function (Item, shallow, i) {
|
|
var name = Item.name;
|
|
var Folder = this;
|
|
// Check if the item is a child
|
|
if (Item.parent !== Folder) {
|
|
return;
|
|
}
|
|
// Deep removal
|
|
if (!shallow) {
|
|
// Remove all descendants of the subfolder.
|
|
if (Item.isFolder) {
|
|
for (i in Item.items) {
|
|
if (!imce.owns(Item.items, i)) {
|
|
continue;
|
|
}
|
|
Item.removeItem(Item.items[i]);
|
|
}
|
|
}
|
|
}
|
|
// Set item free.
|
|
Item.deselect();
|
|
Item.setBusy(false);
|
|
// Remove subfolder
|
|
if (Item.isFolder) {
|
|
if (Item.active) {
|
|
Folder.activate();
|
|
}
|
|
Item.setPath(null);
|
|
delete Folder.subfolders[name];
|
|
imce.removeEl(Item.branchEl);
|
|
Folder.updateSubtree();
|
|
}
|
|
// Remove file
|
|
else {
|
|
delete Folder.files[name];
|
|
}
|
|
// Remove item
|
|
Folder.items.splice(!i ? Folder.indexOf(Item) : i, 1);
|
|
delete Item.parent;
|
|
imce.removeEl(Item.el);
|
|
Folder.updateStatus();
|
|
};
|
|
|
|
/**
|
|
* Set folder path.
|
|
* Register the folder to the tree.
|
|
*/
|
|
Folder.setPath = function (newpath) {
|
|
var i;
|
|
var Folder = this;
|
|
var oldpath = Folder.path;
|
|
var subfolders = Folder.subfolders;
|
|
if (oldpath !== newpath) {
|
|
// Remove
|
|
if (newpath == null) {
|
|
for (i in subfolders) {
|
|
if (!imce.owns(subfolders, i)) {
|
|
continue;
|
|
}
|
|
subfolders[i].setPath(null);
|
|
}
|
|
delete imce.tree[oldpath];
|
|
delete Folder.path;
|
|
}
|
|
// Add
|
|
else {
|
|
Folder.path = newpath;
|
|
imce.tree[newpath] = Folder;
|
|
Folder.setDisabled(!Folder.getConf());
|
|
for (i in subfolders) {
|
|
if (!imce.owns(subfolders, i)) {
|
|
continue;
|
|
}
|
|
subfolders[i].setPath(newpath + '/' + subfolders[i].name);
|
|
}
|
|
Folder.updateStatus();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns a permission value.
|
|
*/
|
|
Folder.getPermission = function (name) {
|
|
return imce.permissionInFolderConf(name, this.getConf());
|
|
};
|
|
|
|
/**
|
|
* Returns folder configuration.
|
|
*/
|
|
Folder.getConf = function () {
|
|
var conf = this.conf;
|
|
var parent;
|
|
if (conf) {
|
|
return conf;
|
|
}
|
|
if (parent = this.parent) {
|
|
if (conf = parent.getConf()) {
|
|
if (imce.permissionInFolderConf('browse_subfolders', conf)) {
|
|
return $.extend({inherited: true}, conf);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sets folder configuration.
|
|
*/
|
|
Folder.setConf = function (conf) {
|
|
if (this.conf !== conf) {
|
|
this.conf = conf;
|
|
this.setDisabled(!this.getConf());
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Open folder.
|
|
*/
|
|
Folder.open = function (refresh) {
|
|
if (refresh || !this.content) {
|
|
this.load();
|
|
}
|
|
this.activate();
|
|
};
|
|
|
|
/**
|
|
* Dynamically load folder contents.
|
|
*/
|
|
Folder.load = function () {
|
|
if (this.isReady()) {
|
|
this.setLoading(true);
|
|
imce.ajax('browse', {
|
|
activeFolder: this,
|
|
customComplete: imce.xFolderLoadComplete
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Activate folder.
|
|
*/
|
|
Folder.activate = function () {
|
|
var Folder = this;
|
|
var oldFolder = imce.activeFolder;
|
|
var parent = Folder.parent;
|
|
if (!Folder.active) {
|
|
// Deactivate the old dir.
|
|
if (oldFolder) {
|
|
oldFolder.deactivate();
|
|
}
|
|
// Check sort
|
|
if (Folder.needSort) {
|
|
Folder.sortItems();
|
|
}
|
|
imce.activeFolder = Folder;
|
|
Folder.active = true;
|
|
$(Folder.branchEl).addClass('active');
|
|
// Add the content to the dom if it is fully loaded.
|
|
if (!Folder.loading) {
|
|
Folder.addContentToDom();
|
|
}
|
|
Folder.setContentVisibility(true);
|
|
// Expand parents if collapsed.
|
|
while (parent) {
|
|
parent.expand();
|
|
parent = parent.parent;
|
|
}
|
|
// Update status and header
|
|
Folder.updateHeader();
|
|
Folder.updateStatus();
|
|
// Trigger activateFolder handlers.
|
|
imce.trigger('activateFolder', Folder, oldFolder);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Deactivate folder.
|
|
*/
|
|
Folder.deactivate = function () {
|
|
var Folder = this;
|
|
if (Folder.active) {
|
|
Folder.setContentVisibility(false);
|
|
imce.deselectAll();
|
|
imce.activeFolder = null;
|
|
Folder.active = false;
|
|
$(Folder.branchEl).removeClass('active');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set loading state.
|
|
*/
|
|
Folder.setLoading = function (state) {
|
|
var Folder = this;
|
|
if (state) {
|
|
if (!Folder.loading) {
|
|
Folder.setBusy(true);
|
|
Folder.setState('loading');
|
|
if (Folder.active) {
|
|
imce.deselectAll();
|
|
}
|
|
}
|
|
}
|
|
else if (Folder.loading) {
|
|
Folder.setBusy(false);
|
|
Folder.unsetState('loading');
|
|
if (Folder.active) {
|
|
Folder.addContentToDom();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns an item by name.
|
|
*/
|
|
Folder.getItem = function (name) {
|
|
var Folder = this;
|
|
if (imce.owns(Folder.files, name)) {
|
|
return Folder.files[name];
|
|
}
|
|
if (imce.owns(Folder.subfolders, name)) {
|
|
return Folder.subfolders[name];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns an item by index.
|
|
*/
|
|
Folder.getItemAt = function (i) {
|
|
return this.items[i];
|
|
};
|
|
|
|
/**
|
|
* Returns the index of an item.
|
|
*/
|
|
Folder.indexOf = function (Item) {
|
|
return $.inArray(Item, this.items);
|
|
};
|
|
|
|
/**
|
|
* Selects all items.
|
|
*/
|
|
Folder.selectAll = function () {
|
|
for (var i in this.items) {
|
|
if (!imce.owns(this.items, i)) {
|
|
continue;
|
|
}
|
|
this.items[i].select();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns the number of items.
|
|
*/
|
|
Folder.countItems = function () {
|
|
return this.items.length;
|
|
};
|
|
|
|
/**
|
|
* Returns the number of subfolders.
|
|
*/
|
|
Folder.countSubfolders = function () {
|
|
var i;
|
|
var count = 0;
|
|
for (i in this.subfolders) {
|
|
if (!imce.owns(this.subfolders, i)) {
|
|
continue;
|
|
}
|
|
count++;
|
|
}
|
|
return count;
|
|
};
|
|
|
|
/**
|
|
* Name change handler.
|
|
*/
|
|
Folder.onNameChange = function (oldval) {
|
|
ItemProto.onNameChange.apply(this, arguments);
|
|
this.branchNameEl.innerHTML = Drupal.checkPlain(this.name);
|
|
this.branchNameEl.title = this.name;
|
|
};
|
|
|
|
/**
|
|
* Item name change handler.
|
|
* Triggered by imce.Item.onNameChange()
|
|
*/
|
|
Folder.onItemNameChange = function (Item, oldname) {
|
|
var Folder = this;
|
|
var name = Item.name;
|
|
var group = Item.isFolder ? Folder.subfolders : Folder.files;
|
|
delete group[oldname];
|
|
group[name] = Item;
|
|
// Set folder path
|
|
if (Item.isFolder) {
|
|
Item.setPath((Folder.parent ? Folder.path + '/' : '') + name);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Double-click handler.
|
|
*/
|
|
Folder.dblClick = function () {
|
|
this.open();
|
|
};
|
|
|
|
/**
|
|
* Inserts the content element into the main content area.
|
|
*/
|
|
Folder.addContentToDom = function () {
|
|
var el = this.contentEl;
|
|
var parentEl = imce.contentEl;
|
|
if (el.parentNode !== parentEl) {
|
|
parentEl.appendChild(el);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sets visibility of the content element.
|
|
*/
|
|
Folder.setContentVisibility = function (show) {
|
|
var el = this.contentEl;
|
|
el.style.display = show ? '' : 'none';
|
|
if (el.scrollTop) {
|
|
el.scrollTop = 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Prepares for subfolder appending.
|
|
*/
|
|
Folder.prepareSubtree = function () {
|
|
var Folder = this;
|
|
if (Folder.subtreeEl.parentNode !== Folder.branchEl) {
|
|
Folder.branchEl.appendChild(Folder.subtreeEl);
|
|
$(Folder.branchEl).removeClass('leaf');
|
|
// Prevent expanding of inactive dirs except the first activated dir on init
|
|
if (!Folder.active && imce.activeFolder) {
|
|
Folder.expanded = true;
|
|
Folder.shrink();
|
|
}
|
|
else {
|
|
Folder.expand();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Check and remove subtree element if it's empty.
|
|
*/
|
|
Folder.updateSubtree = function () {
|
|
if (!this.countSubfolders()) {
|
|
this.shrink();
|
|
imce.removeEl(this.subtreeEl);
|
|
$(this.branchEl).addClass('leaf');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Expands the subtree.
|
|
*/
|
|
Folder.expand = function () {
|
|
if (!this.expanded) {
|
|
this.expanded = true;
|
|
$(this.branchEl).addClass('expanded');
|
|
$(this.subtreeEl).show();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Shrinks the subtree.
|
|
*/
|
|
Folder.shrink = function () {
|
|
if (this.expanded) {
|
|
this.expanded = false;
|
|
$(this.branchEl).removeClass('expanded');
|
|
$(this.subtreeEl).hide();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Update folder status.
|
|
*/
|
|
Folder.updateStatus = function () {
|
|
if (this.active) {
|
|
imce.updateStatus();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Update header sort.
|
|
*/
|
|
Folder.updateHeader = function () {
|
|
if (this.active) {
|
|
imce.updateHeader();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sort folder items by an item property.
|
|
*/
|
|
Folder.sortItems = function (key, desc) {
|
|
var i;
|
|
var sorter;
|
|
var Folder = this;
|
|
var items = Folder.items;
|
|
var active = Folder.activeSort || imce.activeSort || imce.local.activeSort || {};
|
|
if (key == null) {
|
|
key = active.key || 'name';
|
|
}
|
|
if (desc == null) {
|
|
desc = !!active.desc;
|
|
}
|
|
// Remove lazy sort flag.
|
|
Folder.needSort = 0;
|
|
// Check sorter
|
|
if (sorter = imce.sorters[key]) {
|
|
items.sort(sorter);
|
|
if (desc) {
|
|
items.reverse();
|
|
}
|
|
for (i in items) {
|
|
if (!imce.owns(items, i)) {
|
|
continue;
|
|
}
|
|
this.contentEl.appendChild(items[i].el);
|
|
}
|
|
Folder.activeSort = {key: key, desc: desc};
|
|
Folder.updateHeader();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sorts folder tree elements by name.
|
|
*/
|
|
Folder.sortTree = function () {
|
|
var i;
|
|
var Folder = this;
|
|
var subfolders = Folder.subfolders;
|
|
var arr = [];
|
|
for (i in subfolders) {
|
|
if (!imce.owns(subfolders, i)) {
|
|
continue;
|
|
}
|
|
arr.push(subfolders[i]);
|
|
}
|
|
if (arr.length > 1) {
|
|
arr.sort(imce.sortBranchName);
|
|
for (i in arr) {
|
|
if (!imce.owns(arr, i)) {
|
|
continue;
|
|
}
|
|
Folder.subtreeEl.appendChild(arr[i].branchEl);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Check if the item can be appended to the folder.
|
|
*/
|
|
Folder.validateAppend = function (Item, copy) {
|
|
// Disallow self appending
|
|
if (Item === this) {
|
|
return false;
|
|
}
|
|
var parent = Item.parent;
|
|
// Allow orphan appending
|
|
if (!parent) {
|
|
return true;
|
|
}
|
|
// Disallow re-appending children
|
|
if (!copy && parent === this) {
|
|
return false;
|
|
}
|
|
// Disallow (grand)parents appending
|
|
if (Item.isFolder) {
|
|
for (parent = this.parent; parent; parent = parent.parent) {
|
|
if (parent === Item) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Checks if the folder is predefined.
|
|
*/
|
|
Folder.isPredefined = function () {
|
|
return !!this.conf;
|
|
};
|
|
|
|
/**
|
|
* Returns the first predefined descendent including itself.
|
|
*/
|
|
Folder.hasPredefinedPath = function () {
|
|
if (this.isPredefined()) {
|
|
return this;
|
|
}
|
|
var i;
|
|
var Folder;
|
|
var subfolders = this.subfolders;
|
|
for (i in subfolders) {
|
|
if (Folder = subfolders[i].hasPredefinedPath()) {
|
|
return Folder;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Returns status text.
|
|
*/
|
|
Folder.formatStatus = function () {
|
|
return '<div class="items">' + imce.formatItemsStatus(this.countItems(), this.getSize()) + '</div>';
|
|
};
|
|
|
|
/**
|
|
* Returns the size of the folder.
|
|
*/
|
|
Folder.getSize = function () {
|
|
var i;
|
|
var size = 0;
|
|
var files = this.files;
|
|
for (i in files) {
|
|
if (!imce.owns(files, i)) {
|
|
continue;
|
|
}
|
|
size += files[i].size || 0;
|
|
}
|
|
return size;
|
|
};
|
|
|
|
|
|
/**
|
|
* Click event for branch name.
|
|
*/
|
|
imce.eBranchNameClick = function (event) {
|
|
this.Folder.open();
|
|
return false;
|
|
};
|
|
|
|
|
|
/**
|
|
* Click event for branch toggle.
|
|
*/
|
|
imce.eBranchToggleClick = function (event) {
|
|
var Folder = this.Folder;
|
|
if (Folder.countSubfolders()) {
|
|
if (Folder.expanded) {
|
|
Folder.shrink();
|
|
}
|
|
else {
|
|
Folder.expand();
|
|
}
|
|
}
|
|
else {
|
|
Folder.open();
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Ajax complete handler for folder loading.
|
|
*/
|
|
imce.xFolderLoadComplete = function (xhr, status) {
|
|
var content;
|
|
var opt = this;
|
|
var Folder = opt.activeFolder;
|
|
var response = opt.response;
|
|
if (response && (content = response.content)) {
|
|
Folder.setContent(content);
|
|
}
|
|
Folder.setLoading(false);
|
|
if (Folder.countSubfolders()) {
|
|
Folder.expand();
|
|
}
|
|
};
|
|
|
|
})(jQuery, Drupal, imce);
|