You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cryptpad/www/common/drive-ui.js

4890 lines
210 KiB
JavaScript

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

define([
'jquery',
'/api/config',
'/common/toolbar.js',
'json.sortify',
'/common/common-util.js',
'/common/common-hash.js',
'/common/common-ui-elements.js',
'/common/common-interface.js',
'/common/common-constants.js',
'/common/common-feedback.js',
'/common/inner/share.js',
'/common/inner/access.js',
'/common/inner/properties.js',
'/bower_components/nthen/index.js',
'/common/hyperscript.js',
'/common/proxy-manager.js',
'/customize/application_config.js',
'/customize/messages.js',
], function (
$,
ApiConfig,
Toolbar,
JSONSortify,
Util,
Hash,
UIElements,
UI,
Constants,
Feedback,
Share,
Access,
Properties,
nThen,
h,
ProxyManager,
AppConfig,
Messages)
{
var APP = window.APP = {
editable: false,
online: true,
mobile: function () {
if (window.matchMedia) { return !window.matchMedia('(any-pointer:fine)').matches; }
else { return $('body').width() <= 600; }
},
isMac: navigator.platform === "MacIntel",
allowFolderUpload: File.prototype.hasOwnProperty("webkitRelativePath"),
};
var stringify = function (obj) {
return JSONSortify(obj);
};
var E_OVER_LIMIT = 'E_OVER_LIMIT';
var ROOT = "root";
var ROOT_NAME = Messages.fm_rootName;
var SEARCH = "search";
var SEARCH_NAME = Messages.fm_searchName;
var TRASH = "trash";
var TRASH_NAME = Messages.fm_trashName;
var FILES_DATA = Constants.storageKey;
var FILES_DATA_NAME = Messages.fm_filesDataName;
var TEMPLATE = "template";
var TEMPLATE_NAME = Messages.fm_templateName;
var RECENT = "recent";
var RECENT_NAME = Messages.fm_recentPadsName;
var OWNED = "owned";
var OWNED_NAME = Messages.fm_ownedPadsName;
var TAGS = "tags";
var TAGS_NAME = Messages.fm_tagsName;
var SHARED_FOLDER = 'sf';
var SHARED_FOLDER_NAME = Messages.fm_sharedFolderName;
// Icons
var faFolder = 'cptools-folder';
var faFolderOpen = 'cptools-folder-open';
var faSharedFolder = 'cptools-shared-folder';
var faSharedFolderOpen = 'cptools-shared-folder-open';
var faExpandAll = 'fa-plus-square-o';
var faCollapseAll = 'fa-minus-square-o';
var faShared = 'fa-shhare-alt';
var faReadOnly = 'fa-eye';
var faPreview = 'fa-eye';
var faOpenInCode = 'cptools-code';
var faRename = 'fa-pencil';
var faColor = 'cptools-palette';
var faTrash = 'fa-trash';
var faCopy = 'fa-files-o';
var faDelete = 'cptools-destroy';
var faAccess = 'fa-unlock-alt';
var faProperties = 'fa-info-circle';
var faTags = 'fa-hashtag';
var faUploadFiles = 'cptools-file-upload';
var faUploadFolder = 'cptools-folder-upload';
var faEmpty = 'fa-trash-o';
var faRestore = 'fa-repeat';
var faShowParent = 'fa-location-arrow';
var faDownload = 'fa-download';
var $folderIcon = $('<span>', {
"class": faFolder + " cptools cp-app-drive-icon-folder cp-app-drive-content-icon"
});
//var $folderIcon = $('<img>', {src: "/customize/images/icons/folder.svg", "class": "folder icon"});
var $folderEmptyIcon = $folderIcon.clone();
var $folderOpenedIcon = $('<span>', {"class": faFolderOpen + " cptools cp-app-drive-icon-folder"});
//var $folderOpenedIcon = $('<img>', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"});
var $folderOpenedEmptyIcon = $folderOpenedIcon.clone();
var $sharedFolderIcon = $('<span>', {"class": faSharedFolder + " cptools cp-app-drive-icon-folder"});
var $sharedFolderOpenedIcon = $('<span>', {"class": faSharedFolderOpen + " cptools cp-app-drive-icon-folder"});
//var $upIcon = $('<span>', {"class": "fa fa-arrow-circle-up"});
var $unsortedIcon = $('<span>', {"class": "fa fa-files-o"});
var $templateIcon = $('<span>', {"class": "cptools cptools-template"});
var $recentIcon = $('<span>', {"class": "fa fa-clock-o"});
var $trashIcon = $('<span>', {"class": "fa " + faTrash});
var $trashEmptyIcon = $('<span>', {"class": "fa fa-trash-o"});
//var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o cp-app-drive-icon-expcol"});
var $expandIcon = $('<span>', {"class": "fa fa-plus-square-o cp-app-drive-icon-expcol"});
var $listIcon = $('<button>', {"class": "fa fa-list"});
var $gridIcon = $('<button>', {"class": "fa fa-th-large"});
var $sortAscIcon = $('<span>', {"class": "fa fa-angle-up sortasc"});
var $sortDescIcon = $('<span>', {"class": "fa fa-angle-down sortdesc"});
var $closeIcon = $('<span>', {"class": "fa fa-window-close"});
//var $backupIcon = $('<span>', {"class": "fa fa-life-ring"});
var $searchIcon = $('<span>', {"class": "fa fa-search cp-app-drive-tree-search-icon"});
var $addIcon = $('<span>', {"class": "fa fa-plus"});
var $renamedIcon = $('<span>', {"class": "fa fa-flag"});
var $readonlyIcon = $('<span>', {"class": "fa " + faReadOnly});
var $ownedIcon = $('<span>', {"class": "fa fa-id-badge"});
var $sharedIcon = $('<span>', {"class": "fa " + faShared});
//var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
var $tagsIcon = $('<span>', {"class": "fa " + faTags});
var $passwordIcon = $('<span>', {"class": "fa fa-lock"});
var $restrictedIcon = $('<span>', {"class": "fa fa-ban"});
var $expirableIcon = $('<span>', {"class": "fa fa-clock-o"});
var $separator = $('<div>', {"class": "dropdown-divider"});
var LS_VIEWMODE = "app-drive-viewMode";
var FOLDER_CONTENT_ID = "cp-app-drive-content-folder";
var config = {};
var DEBUG = config.DEBUG = false;
var debug = config.debug = DEBUG ? function () {
console.log.apply(console, arguments);
} : function () { return; };
var logError = config.logError = function () {
console.error.apply(console, arguments);
};
var log = config.log = UI.log;
var localStore = window.cryptpadStore;
APP.store = {};
$(window).keydown(function (e) {
if (e.which === 70 && e.ctrlKey) {
e.preventDefault();
e.stopPropagation();
if (APP.displayDirectory) {
APP.displayDirectory([SEARCH]);
}
return;
}
});
var makeLS = function (teamId) {
var suffix = teamId ? ('-' + teamId) : '';
var LS_LAST = "app-drive-lastOpened" + suffix;
var LS_OPENED = "app-drive-openedFolders" + suffix;
var LS = {};
LS.getLastOpenedFolder = function () {
var path;
try {
path = APP.store[LS_LAST] ? JSON.parse(APP.store[LS_LAST]) : [ROOT];
} catch (e) {
path = [ROOT];
}
return path;
};
LS.setLastOpenedFolder = function (path) {
if (path[0] === SEARCH) { return; }
APP.store[LS_LAST] = JSON.stringify(path);
localStore.put(LS_LAST, JSON.stringify(path));
};
LS.wasFolderOpened = function (path) {
var stored = JSON.parse(APP.store[LS_OPENED] || '[]');
return stored.indexOf(JSON.stringify(path)) !== -1;
};
LS.setFolderOpened = function (path, opened) {
var s = JSON.stringify(path);
var stored = JSON.parse(APP.store[LS_OPENED] || '[]');
if (opened && stored.indexOf(s) === -1) {
stored.push(s);
}
if (!opened) {
var idx = stored.indexOf(s);
if (idx !== -1) {
stored.splice(idx, 1);
}
}
APP.store[LS_OPENED] = JSON.stringify(stored);
localStore.put(LS_OPENED, JSON.stringify(stored));
};
LS.removeFoldersOpened = function (parentPath) {
var stored = JSON.parse(APP.store[LS_OPENED] || '[]');
var s = JSON.stringify(parentPath).slice(0, -1);
for (var i = stored.length - 1 ; i >= 0 ; i--) {
if (stored[i].indexOf(s) === 0) {
stored.splice(i, 1);
}
}
APP.store[LS_OPENED] = JSON.stringify(stored);
localStore.put(LS_OPENED, JSON.stringify(stored));
};
LS.renameFoldersOpened = function (parentPath, newName) {
var stored = JSON.parse(APP.store[LS_OPENED] || '[]');
var s = JSON.stringify(parentPath).slice(0, -1);
var newParentPath = parentPath.slice();
newParentPath[newParentPath.length - 1] = newName;
var sNew = JSON.stringify(newParentPath).slice(0, -1);
for (var i = 0 ; i < stored.length ; i++) {
if (stored[i].indexOf(s) === 0) {
stored[i] = stored[i].replace(s, sNew);
}
}
APP.store[LS_OPENED] = JSON.stringify(stored);
localStore.put(LS_OPENED, JSON.stringify(stored));
};
LS.moveFoldersOpened = function (previousPath, newPath) {
var stored = JSON.parse(APP.store[LS_OPENED] || '[]');
var s = JSON.stringify(previousPath).slice(0, -1);
var sNew = JSON.stringify(newPath).slice(0, -1);
if (s === sNew ) { return; } // move to itself
if (sNew.indexOf(s) === 0) { return; } // move to subfolder
sNew = JSON.stringify(newPath.concat(previousPath[previousPath.length - 1])).slice(0, -1);
for (var i = 0 ; i < stored.length ; i++) {
if (stored[i].indexOf(s) === 0) {
stored[i] = stored[i].replace(s, sNew);
}
}
APP.store[LS_OPENED] = JSON.stringify(stored);
localStore.put(LS_OPENED, JSON.stringify(stored));
};
return LS;
};
var getViewModeClass = function (forceList) {
var mode = APP.store[LS_VIEWMODE];
if (mode === 'list' || forceList) { return 'cp-app-drive-content-list'; }
return 'cp-app-drive-content-grid';
};
var getViewMode = function () {
return APP.store[LS_VIEWMODE] || 'grid';
};
var setViewMode = function (mode) {
if (typeof(mode) !== "string") {
logError("Incorrect view mode: ", mode);
return;
}
APP.store[LS_VIEWMODE] = mode;
localStore.put(LS_VIEWMODE, mode);
};
// Handle disconnect/reconnect
// If isHistory and isSf are both false, update the "APP.online" flag
// If isHistory is true, update the "APP.history" flag
// isSf is used to detect offline shared folders: setEditable is called on displayDirectory
var setEditable = function (state, isHistory, isSf) {
if (APP.closed || !APP.$content || !$.contains(document.documentElement, APP.$content[0])) { return; }
if (isHistory) {
APP.history = !state;
} else if (!isSf) {
APP.online = state;
}
state = APP.online && !APP.history && state;
APP.editable = !APP.readOnly && state;
if (!state) {
APP.$content.addClass('cp-app-drive-readonly');
if (!APP.history || !APP.online) {
$('#cp-app-drive-connection-state').show();
} else {
$('#cp-app-drive-connection-state').hide();
}
$('[draggable="true"]').attr('draggable', false);
}
else {
APP.$content.removeClass('cp-app-drive-readonly');
$('#cp-app-drive-connection-state').hide();
$('[draggable="false"]').attr('draggable', true);
}
};
var copyObjectValue = function (objRef, objToCopy) {
for (var k in objRef) { delete objRef[k]; }
$.extend(true, objRef, objToCopy);
};
APP.selectedFiles = [];
var isElementSelected = function ($element) {
var elementId = $element.data("path").slice(-1)[0];
return APP.selectedFiles.indexOf(elementId) !== -1;
};
var selectElement = function ($element) {
var elementId = $element.data("path").slice(-1)[0];
if (APP.selectedFiles.indexOf(elementId) === -1) {
APP.selectedFiles.push(elementId);
}
$element.addClass("cp-app-drive-element-selected");
};
var unselectElement = function ($element) {
var elementId = $element.data("path").slice(-1)[0];
var index = APP.selectedFiles.indexOf(elementId);
if (index !== -1) {
APP.selectedFiles.splice(index, 1);
}
$element.removeClass("cp-app-drive-element-selected");
};
var findSelectedElements = function () {
return $(".cp-app-drive-element-selected");
};
var createContextMenu = function () {
var menu = h('div.cp-contextmenu.dropdown.cp-unselectable', [
h('ul.dropdown-menu', {
'role': 'menu',
'aria-labelledby': 'dropdownMenu',
'style': 'display:block;position:static;margin-bottom:5px;'
}, [
h('span.cp-app-drive-context-noAction.dropdown-item.disabled', Messages.fc_noAction || "No action possible"),
h('li', h('a.cp-app-drive-context-preview.dropdown-item', {
'tabindex': '-1',
'data-icon': faPreview,
}, Messages.pad_mediatagPreview)),
h('li', h('a.cp-app-drive-context-open.dropdown-item', {
'tabindex': '-1',
'data-icon': faFolderOpen,
}, Messages.fc_open)),
h('li', h('a.cp-app-drive-context-openro.dropdown-item', {
'tabindex': '-1',
'data-icon': faReadOnly,
}, Messages.fc_open_ro)),
h('li', h('a.cp-app-drive-context-openincode.dropdown-item', {
'tabindex': '-1',
'data-icon': faOpenInCode,
}, Messages.fc_openInCode)),
h('li', h('a.cp-app-drive-context-savelocal.dropdown-item', {
'tabindex': '-1',
'data-icon': 'fa-cloud-upload',
}, Messages.pad_mediatagImport)), // Save in your CryptDrive
$separator.clone()[0],
h('li', h('a.cp-app-drive-context-expandall.dropdown-item', {
'tabindex': '-1',
'data-icon': faExpandAll,
}, Messages.fc_expandAll)),
h('li', h('a.cp-app-drive-context-collapseall.dropdown-item', {
'tabindex': '-1',
'data-icon': faCollapseAll,
}, Messages.fc_collapseAll)),
$separator.clone()[0],
h('li', h('a.cp-app-drive-context-openparent.dropdown-item', {
'tabindex': '-1',
'data-icon': faShowParent,
}, Messages.fm_openParent)),
$separator.clone()[0],
h('li', h('a.cp-app-drive-context-share.dropdown-item', {
'tabindex': '-1',
'data-icon': 'fa-shhare-alt',
}, Messages.shareButton)),
h('li', h('a.cp-app-drive-context-access.dropdown-item', {
'tabindex': '-1',
'data-icon': faAccess,
}, Messages.accessButton)),
$separator.clone()[0],
h('li', h('a.cp-app-drive-context-newfolder.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faFolder,
}, Messages.fc_newfolder)),
h('li', h('a.cp-app-drive-context-newsharedfolder.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faSharedFolder,
}, Messages.fc_newsharedfolder)),
$separator.clone()[0],
h('li', h('a.cp-app-drive-context-uploadfiles.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faUploadFiles,
}, Messages.uploadButton)),
h('li', h('a.cp-app-drive-context-uploadfolder.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faUploadFolder,
}, Messages.uploadFolderButton)),
$separator.clone()[0],
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.pad,
'data-type': 'pad'
}, Messages.button_newpad)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.code,
'data-type': 'code'
}, Messages.button_newcode)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.slide,
'data-type': 'slide'
}, Messages.button_newslide)),
h('li.dropdown-submenu', [
h('a.cp-app-drive-context-newdocmenu.dropdown-item', {
'tabindex': '-1',
'data-icon': "fa-plus",
}, Messages.fm_morePads),
h("ul.dropdown-menu", [
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.sheet,
'data-type': 'sheet'
}, Messages.button_newsheet)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.whiteboard,
'data-type': 'whiteboard'
}, Messages.button_newwhiteboard)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.kanban,
'data-type': 'kanban'
}, Messages.button_newkanban)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.poll,
'data-type': 'poll'
}, Messages.button_newpoll)),
]),
]),
$separator.clone()[0],
h('li', h('a.cp-app-drive-context-empty.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faEmpty,
}, Messages.fc_empty)),
h('li', h('a.cp-app-drive-context-restore.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faRestore,
}, Messages.fc_restore)),
$separator.clone()[0],
h('li', h('a.cp-app-drive-context-rename.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faRename,
}, Messages.fc_rename)),
h('li', h('a.cp-app-drive-context-color.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faColor,
}, Messages.fc_color)),
h('li', h('a.cp-app-drive-context-hashtag.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faTags,
}, Messages.fc_hashtag)),
$separator.clone()[0],
h('li', h('a.cp-app-drive-context-makeacopy.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faCopy,
}, Messages.makeACopy)),
h('li', h('a.cp-app-drive-context-download.dropdown-item', {
'tabindex': '-1',
'data-icon': faDownload,
}, Messages.download_mt_button)),
h('li', h('a.cp-app-drive-context-delete.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faTrash,
}, Messages.fc_delete)), // "Move to trash"
h('li', h('a.cp-app-drive-context-deleteowned.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faDelete,
}, Messages.fc_delete_owned)),
h('li', h('a.cp-app-drive-context-remove.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faTrash,
}, Messages.fc_remove)),
h('li', h('a.cp-app-drive-context-removesf.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faTrash,
}, Messages.fc_remove_sharedfolder)),
$separator.clone()[0],
h('li', h('a.cp-app-drive-context-properties.dropdown-item', {
'tabindex': '-1',
'data-icon': faProperties,
}, Messages.fc_prop))
])
]);
// add icons to the contextmenu options
$(menu).find("li a.dropdown-item").each(function (i, el) {
var $icon = $("<span>");
if ($(el).attr('data-icon')) {
var font = $(el).attr('data-icon').indexOf('cptools') === 0 ? 'cptools' : 'fa';
$icon.addClass(font).addClass($(el).attr('data-icon'));
} else {
$icon.text($(el).text());
}
$(el).prepend($icon);
});
// add events handlers for the contextmenu submenus
$(menu).find(".dropdown-submenu").each(function (i, el) {
var $el = $(el);
var $a = $el.children().filter("a");
var $sub = $el.find(".dropdown-menu").first();
var left, bottomOffset;
var timeoutId;
var showSubmenu = function () {
clearTimeout(timeoutId);
left = $el.offset().left + $el.outerWidth() + $sub.outerWidth() > $(window).width();
bottomOffset = $el.offset().top + $el.outerHeight() + $sub.outerHeight() - $(window).height();
$sub.css("left", left ? "0%": "100%");
$sub.css("transform", "translate("
+ (left ? "-100%" : "0")
+ "," + (bottomOffset > 0 ? -(bottomOffset - $el.outerHeight()) : 0) + "px)");
$el.siblings().find(".dropdown-menu").hide();
$sub.show();
};
var hideSubmenu = function () {
$sub.hide();
$sub.removeClass("left");
};
var mouseOutSubmenu = function () {
// don't hide immediately the submenu
timeoutId = setTimeout(hideSubmenu, 100);
};
// Add submenu expand icon
$a.append(h("span.dropdown-toggle"));
// Show / hide submenu
$el.hover(function () {
showSubmenu();
}, function () {
mouseOutSubmenu();
});
// handle click event
$el.click(function (e) {
var targetItem = $(e.target).closest(".dropdown-item")[0]; // don't close contextmenu if open submenu
var elTarget = $el.children(".dropdown-item")[0];
if (targetItem === elTarget) { e.stopPropagation(); }
if ($el.children().filter(".dropdown-menu:visible").length !== 0) {
$el.find(".dropdown-menu").hide();
hideSubmenu();
}
else {
showSubmenu();
}
});
});
return $(menu);
};
var create = function (common, driveConfig) { //proxy, folders) {
var metadataMgr = common.getMetadataMgr();
var sframeChan = common.getSframeChannel();
var priv = metadataMgr.getPrivateData();
// Initialization
Util.extend(APP, driveConfig.APP);
APP.$limit = driveConfig.$limit;
var proxy = driveConfig.proxy;
var folders = driveConfig.folders;
var files = proxy.drive;
var history = driveConfig.history || {};
var edPublic = driveConfig.edPublic || priv.edPublic;
config.editKey = driveConfig.editKey;
APP.origin = priv.origin;
APP.hideDuplicateOwned = Util.find(priv, ['settings', 'drive', 'hideDuplicate']);
APP.closed = false;
APP.toolbar = driveConfig.toolbar;
var $readOnly = $(h('div#cp-app-drive-edition-state.cp-app-drive-content-info-box', Messages.readonly));
var updateObject = driveConfig.updateObject;
var updateSharedFolders = driveConfig.updateSharedFolders;
// manager
config.loggedIn = APP.loggedIn;
config.sframeChan = sframeChan;
var manager = ProxyManager.createInner(files, sframeChan, edPublic, config);
var LS = makeLS(APP.team);
Object.keys(folders).forEach(function (id) {
var f = folders[id];
var sfData = files.sharedFolders[id] || {};
var href = manager.user.userObject.getHref(sfData);
var parsed = Hash.parsePadUrl(href);
var secret = Hash.getSecrets('drive', parsed.hash, sfData.password);
manager.addProxy(id, {proxy: f}, null, secret.keys.secondaryKey);
});
// UI containers
var $tree = APP.$tree = $("#cp-app-drive-tree");
var $content = APP.$content = $("#cp-app-drive-content");
var $appContainer = $(".cp-app-drive-container");
var $driveToolbar = APP.toolbar.$bottom;
var $contextMenu = createContextMenu().appendTo($appContainer);
var $contentContextMenu = $("#cp-app-drive-context-content");
var $defaultContextMenu = $("#cp-app-drive-context-default");
var $trashTreeContextMenu = $("#cp-app-drive-context-trashtree");
var $trashContextMenu = $("#cp-app-drive-context-trash");
// TOOLBAR
// DRIVE
var currentPath = APP.currentPath = LS.getLastOpenedFolder();
if (APP.newSharedFolder) {
var newSFPaths = manager.findFile(APP.newSharedFolder);
if (newSFPaths.length) {
currentPath = newSFPaths[0];
}
}
// Categories dislayed in the menu
var displayedCategories = [ROOT, TRASH, SEARCH, RECENT];
// PCS enabled: display owned pads
//if (AppConfig.displayCreationScreen) { displayedCategories.push(OWNED); }
// Templates enabled: display template category
if (AppConfig.enableTemplates) { displayedCategories.push(TEMPLATE); }
// Tags used: display Tags category
if (Object.keys(manager.getTagsList()).length) { displayedCategories.push(TAGS); }
var virtualCategories = [SEARCH, RECENT, OWNED, TAGS];
if (!APP.loggedIn) {
$tree.hide();
if (APP.newSharedFolder) {
// ANON_SHARED_FOLDER
displayedCategories = [SHARED_FOLDER];
virtualCategories.push(SHARED_FOLDER);
currentPath = [SHARED_FOLDER, ROOT];
} else {
displayedCategories = [FILES_DATA];
currentPath = [FILES_DATA];
if (Object.keys(files.root).length && !proxy.anonymousAlert) {
var msg = common.fixLinks($('<div>').html(Messages.fm_alert_anonymous));
UI.alert(msg);
proxy.anonymousAlert = true;
}
}
}
APP.editable = !APP.readOnly;
var appStatus = {
isReady: true,
_onReady: [],
onReady: function (handler) {
if (appStatus.isReady) {
handler();
return;
}
appStatus._onReady.push(handler);
},
ready: function (state) {
appStatus.isReady = state;
if (state) {
appStatus._onReady.forEach(function (h) {
h();
});
appStatus._onReady = [];
}
}
};
var findDataHolder = function ($el) {
return $el.is('.cp-app-drive-element-row') ? $el : $el.closest('.cp-app-drive-element-row');
};
// Selection
var sel = {};
var removeSelected = function (keepObj) {
APP.selectedFiles = [];
findSelectedElements().removeClass("cp-app-drive-element-selected");
var $container = $driveToolbar.find('#cp-app-drive-toolbar-contextbuttons');
if (!$container.length) { return; }
$container.html('');
if (!keepObj) {
delete sel.startSelected;
delete sel.endSelected;
delete sel.oldSelection;
}
};
sel.refresh = 50;
sel.$selectBox = $('<div>', {'class': 'cp-app-drive-content-select-box'}).appendTo($content);
var checkSelected = function () {
if (!sel.down) { return; }
var pos = sel.pos;
var l = $content[0].querySelectorAll('.cp-app-drive-element:not(.cp-app-drive-element-selected):not(.cp-app-drive-element-header)');
var p, el;
var offset = getViewMode() === "grid" ? 10 : 0;
for (var i = 0; i < l.length; i++) {
el = l[i];
p = $(el).position();
p.top += offset + $content.scrollTop();
p.left += offset;
p.bottom = p.top + $(el).outerHeight();
p.right = p.left + $(el).outerWidth();
if (p.right < pos.left || p.left > pos.right
|| p.top > pos.bottom || p.bottom < pos.top) {
$(el).removeClass('cp-app-drive-element-selected-tmp');
} else {
$(el).addClass('cp-app-drive-element-selected-tmp');
}
}
};
$content.on('mousedown', function (e) {
if (e.which !== 1) { return; }
$content.focus();
sel.down = true;
if (!e.ctrlKey) { removeSelected(); }
var rect = e.currentTarget.getBoundingClientRect();
sel.startX = e.clientX - rect.left;
sel.startY = e.clientY - rect.top + $content.scrollTop();
sel.$selectBox.show().css({
left: sel.startX + 'px',
top: sel.startY + 'px',
width: '0px',
height: '0px'
});
APP.hideMenu(e);
if (sel.move) { return; }
sel.move = function (ev) {
var rectMove = ev.currentTarget.getBoundingClientRect(),
offX = ev.clientX - rectMove.left,
offY = ev.clientY - rectMove.top + $content.scrollTop();
var left = sel.startX,
top = sel.startY;
var width = offX - sel.startX;
if (width < 0) {
left = Math.max(0, offX);
var diffX = left-offX;
width = Math.abs(width) - diffX;
}
var height = offY - sel.startY;
if (height < 0) {
top = Math.max(0, offY);
var diffY = top-offY;
height = Math.abs(height) - diffY;
}
sel.$selectBox.css({
width: width + 'px',
left: left + 'px',
height: height + 'px',
top: top + 'px'
});
sel.pos = {
top: top,
left: left,
bottom: top + height,
right: left + width
};
var diffT = sel.update ? +new Date() - sel.update : sel.refresh;
if (diffT < sel.refresh) {
if (!sel.to) {
sel.to = window.setTimeout(function () {
sel.update = +new Date();
checkSelected();
sel.to = undefined;
}, (sel.refresh - diffT));
}
return;
}
sel.update = +new Date();
checkSelected();
};
$content.mousemove(sel.move);
});
var onWindowMouseUp = function (e) {
if (!sel.down) { return; }
if (e.which !== 1) { return; }
sel.down = false;
sel.$selectBox.hide();
$content.off('mousemove', sel.move);
delete sel.move;
$content.find('.cp-app-drive-element-selected-tmp')
.removeClass('cp-app-drive-element-selected-tmp')
.each(function (idx, element) {
selectElement($(element));
});
e.stopPropagation();
};
var getSelectedPaths = function ($element) {
var paths = [];
if (!$element || $element.length === 0) { return paths; }
if (findSelectedElements().length > 1) {
var $selected = findSelectedElements();
$selected.each(function (idx, elmt) {
var ePath = $(elmt).data('path');
if (ePath) {
paths.push({
path: ePath,
element: $(elmt)
});
}
});
}
if (!paths.length) {
var path = $element.data('path');
if (!path) { return false; }
paths.push({
path: path,
element: $element
});
}
return paths;
};
var removeInput = function (cancel) {
if (!cancel && $('.cp-app-drive-element-row > input').length === 1) {
var $input = $('.cp-app-drive-element-row > input');
manager.rename($input.data('path'), $input.val(), APP.refresh);
}
$('.cp-app-drive-element-row > input').remove();
$('.cp-app-drive-element-row > span:hidden').removeAttr('style');
};
var getFileNameExtension = function (name) {
var matched = /\.[^\. ]+$/.exec(name);
if (matched && matched.length) { return matched[matched.length -1]; }
return '';
};
// Replace a file/folder name by an input to change its value
var displayRenameInput = function ($element, path) {
// NOTE: setTimeout(f, 0) otherwise the "rename" button in the toolbar is not working
window.setTimeout(function () {
if (!APP.editable) { return; }
if (!path || path.length < 2) {
logError("Renaming a top level element (root, trash or filesData) is forbidden.");
return;
}
removeInput();
var $name = $element.find('.cp-app-drive-element-name');
if (!$name.length) {
$name = $element.find('> .cp-app-drive-element');
}
$name.hide();
var isFolder = $element.is(".cp-app-drive-element-folder:not(.cp-app-drive-element-sharedf)");
var el = manager.find(path);
var name = manager.isFile(el) ? manager.getTitle(el) : path[path.length - 1];
if (manager.isSharedFolder(el)) {
name = manager.getSharedFolderData(el).title;
}
var $input = $('<input>', {
placeholder: name,
value: name
}).data('path', path);
// Stop propagation on keydown to avoid issues with arrow keys
$input.on('keydown', function (e) { e.stopPropagation(); });
$input.on('keyup', function (e) {
e.stopPropagation();
if (e.which === 13) {
removeInput(true);
var newName = $input.val();
if (JSON.stringify(path) === JSON.stringify(currentPath)) {
manager.rename(path, $input.val(), function () {
if (isFolder) {
LS.renameFoldersOpened(path, newName);
path[path.length - 1] = newName;
}
APP.displayDirectory(path);
});
}
else {
manager.rename(path, $input.val(), function () {
if (isFolder) {
LS.renameFoldersOpened(path, newName);
unselectElement($element);
$element.data("path", $element.data("path").slice(0, -1).concat(newName));
selectElement($element);
}
APP.refresh();
});
}
return;
}
if (e.which === 27) {
removeInput(true);
}
}).on('keypress', function (e) { e.stopPropagation(); });
//$element.parent().append($input);
$name.after($input);
$input.focus();
var extension = getFileNameExtension(name);
var input = $input[0];
input.selectionStart = 0;
input.selectionEnd = name.length - extension.length;
// We don't want to open the file/folder when clicking on the input
$input.on('click dblclick', function (e) {
e.stopPropagation();
});
// Remove the browser ability to drag text from the input to avoid
// triggering our drag/drop event handlers
$input.on('dragstart dragleave drag drop', function (e) {
e.preventDefault();
e.stopPropagation();
});
// Make the parent element non-draggable when selecting text in the field
// since it would remove the input
$input.on('mousedown', function (e) {
e.stopPropagation();
$input.parents('.cp-app-drive-element-row').attr("draggable", false);
});
$input.on('mouseup', function (e) {
e.stopPropagation();
$input.parents('.cp-app-drive-element-row').attr("draggable", true);
});
},0);
};
// Arrow keys to modify the selection
var onWindowKeydown = function (e) {
if (!$content.is(':visible')) { return; }
var $searchBar = $tree.find('#cp-app-drive-tree-search-input');
if (document.activeElement && document.activeElement.nodeName === 'INPUT') { return; }
if ($searchBar.is(':focus') && $searchBar.val()) { return; }
var $elements = $content.find('.cp-app-drive-element:not(.cp-app-drive-element-header)');
var ev = {};
if (e.ctrlKey) { ev.ctrlKey = true; }
if (e.shiftKey) { ev.shiftKey = true; }
// ESC
if (e.which === 27) {
return void APP.hideMenu();
}
// Enter
if (e.which === 13) {
var $allSelected = $content.find('.cp-app-drive-element.cp-app-drive-element-selected');
if ($allSelected.length === 1) {
// Open the folder or the file
$allSelected.dblclick();
return;
}
// If more than one, open only the files
var $select = $content.find('.cp-app-drive-element-file.cp-app-drive-element-selected');
$select.each(function (idx, el) {
$(el).dblclick();
});
return;
}
// Ctrl+A select all
if (e.which === 65 && (e.ctrlKey || (e.metaKey && APP.isMac))) {
e.preventDefault();
$content.find('.cp-app-drive-element:not(.cp-app-drive-element-selected)')
.each(function (idx, element) {
selectElement($(element));
});
return;
}
// F2: rename selected element
if (e.which === 113) {
var paths = getSelectedPaths(findSelectedElements().first());
if (paths.length !== 1) { return; }
displayRenameInput(paths[0].element, paths[0].path);
}
// [Left, Up, Right, Down]
if ([37, 38, 39, 40].indexOf(e.which) === -1) { return; }
e.preventDefault();
var click = function (el) {
if (!el) { return; }
APP.onElementClick(ev, $(el));
};
var $selection = findSelectedElements();
if ($selection.length === 0) { return void click($elements.first()[0]); }
var lastIndex = typeof sel.endSelected === "number" ? sel.endSelected :
typeof sel.startSelected === "number" ? sel.startSelected :
$elements.index($selection.last()[0]);
var length = $elements.length;
if (length === 0) { return; }
// List mode
if (getViewMode() === "list") {
if (e.which === 40) { click($elements.get(Math.min(lastIndex+1, length -1))); }
if (e.which === 38) { click($elements.get(Math.max(lastIndex-1, 0))); }
return;
}
// Icon mode
// Get the vertical and horizontal position of lastIndex
// Filter all the elements to get those in the same line/column
var pos = $($elements.get(0)).position();
var $line = $elements.filter(function (idx, el) {
return $(el).position().top === pos.top;
});
var cols = $line.length;
var lines = Math.ceil(length/cols);
var lastPos = {
l : Math.floor(lastIndex/cols),
c : lastIndex - Math.floor(lastIndex/cols)*cols
};
if (e.which === 37) {
if (lastPos.c === 0) { return; }
click($elements.get(Math.max(lastIndex-1, 0)));
return;
}
if (e.which === 38) {
if (lastPos.l === 0) { return; }
click($elements.get(Math.max(lastIndex-cols, 0)));
return;
}
if (e.which === 39) {
if (lastPos.c === cols-1) { return; }
click($elements.get(Math.min(lastIndex+1, length-1)));
return;
}
if (e.which === 40) {
if (lastPos.l === lines-1) { return; }
click($elements.get(Math.min(lastIndex+cols, length-1)));
return;
}
};
var compareDays = function (date1, date2) {
var day1 = Date.UTC(date1.getFullYear(), date1.getMonth(), date1.getDate());
var day2 = Date.UTC(date2.getFullYear(), date2.getMonth(), date2.getDate());
var ms = Math.abs(day1-day2);
return Math.floor(ms/1000/60/60/24);
};
var getDate = function (sDate) {
if (!sDate) { return ''; }
var ret = sDate.toString();
try {
var date = new Date(sDate);
var today = new Date();
var diff = compareDays(date, today);
if (diff === 0) {
ret = date.toLocaleTimeString();
} else {
ret = date.toLocaleDateString();
}
} catch (e) {
console.error("Unable to format that string to a date with .toLocaleString", sDate, e);
}
return ret;
};
var previewMediaTag = function (data) {
var mts = [];
$content.find('.cp-app-drive-element.cp-border-color-file').each(function (i, el) {
var path = $(el).data('path');
var id = manager.find(path);
if (!id) { return; }
var _data = manager.getFileData(id);
if (!_data || _data.channel < 48) { return; }
mts.push({
channel: _data.channel,
href: _data.href,
password: _data.password
});
});
// Find initial position
var idx = -1;
mts.some(function (obj, i) {
if (obj.channel === data.channel) {
idx = i;
return true;
}
});
if (idx === -1) {
mts.unshift({
href: data.href,
password: data.password
});
idx = 0;
}
common.getMediaTagPreview(mts, idx);
};
// `app`: true (force open wiht the app), false (force open in preview),
// falsy (open in preview if default is not using the app)
var defaultInApp = ['application/pdf'];
var openFile = function (el, isRo, app) {
var data = manager.getFileData(el);
if (!data || (!data.href && !data.roHref)) {
return void logError("Missing data for the file", el, data);
}
var href = isRo ? data.roHref : (data.href || data.roHref);
var parsed = Hash.parsePadUrl(href);
if (parsed.hashData && parsed.hashData.type === 'file' && !app
&& (defaultInApp.indexOf(data.fileType) === -1 || app === false)) {
return void previewMediaTag(data);
}
var priv = metadataMgr.getPrivateData();
var useUnsafe = Util.find(priv, ['settings', 'security', 'unsafeLinks']);
if (useUnsafe === true) {
return void window.open(APP.origin + href);
}
// Get hidden hash
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
var opts = {};
if (isRo) { opts.view = true; }
var hash = Hash.getHiddenHashFromKeys(parsed.type, secret, opts);
var hiddenHref = Hash.hashToHref(hash, parsed.type);
window.open(APP.origin + hiddenHref);
};
var openIn = function (type, path, team, fData) {
var obj = {
p: path,
t: team,
d: fData
};
var href = Hash.hashToHref('', type);
common.openURL(Hash.getNewPadURL(href, obj));
};
var refresh = APP.refresh = function () {
APP.displayDirectory(currentPath);
};
var pickFolderColor = function ($element, currentColor, cb) {
var colors = ["", "#f23c38", "#ff0073", "#da0eba", "#9d00ac", "#6c19b3", "#4a42b1", "#3d8af0", "#30a0f1", "#1fb9d1", "#009686", "#45b354", "#84c750", "#c6e144", "#faf147", "#fbc423", "#fc9819", "#fd5227", "#775549", "#9c9c9c", "#607a89"];
var colorsElements = [];
var currentElement = null;
colors.forEach(function (color, i) {
var element = h("span.cp-app-drive-color-picker-color", [
h("span.cptools.cp-app-drive-icon-folder.cp-app-drive-content-icon" + (i === 0 ? ".cptools-folder-no-color" : ".cptools-folder")),
h("span.fa.fa-check")
]);
$(element).css("color", colors[i]);
if (colors[i] === currentColor) {
currentElement = element;
$(element).addClass("cp-app-drive-current-color");
}
$(element).on("click", function () {
$(currentElement).removeClass("cp-app-drive-current-color");
currentElement = element;
$(element).addClass("cp-app-drive-current-color");
cb(color);
});
colorsElements.push(element);
});
var content = h("div.cp-app-drive-color-picker", colorsElements);
UI.alert(content);
};
var getFolderColor = function (path) {
if (path.length === 0) { return; }
return manager.getFolderData(path).color || "";
};
var setFolderColor = function ($element, path, color) {
if ($element.length === 0) { return; }
$element.find(".cp-app-drive-icon-folder").css("color", color);
manager.setFolderData({
path: path,
key: "color",
value: color
}, function () {});
};
var filterContextMenu = function (type, paths) {
if (!paths || paths.length === 0) { logError('no paths'); }
$contextMenu.find('li').hide();
var show = [];
var filter;
var editable = true;
if (type === "content") {
if (APP.$content.data('readOnlyFolder')) { editable = false; }
// Return true in filter to hide
filter = function ($el, className) {
if (className === 'newfolder') { return; }
if (className === 'newsharedfolder') {
// Hide the new shared folder menu if we're already in a shared folder
return manager.isInSharedFolder(currentPath) || APP.disableSF;
}
if (className === 'uploadfiles') { return; }
if (className === 'uploadfolder') { return !APP.allowFolderUpload; }
if (className === 'newdoc') {
return AppConfig.availablePadTypes.indexOf($el.attr('data-type')) === -1;
}
};
} else {
// In case of multiple selection, we must hide the option if at least one element
// is not compatible
var containsFolder = false;
var hide = [];
if (!APP.team) {
hide.push('savelocal');
}
paths.forEach(function (p) {
var path = p.path;
var $element = p.element;
if (APP.$content.data('readOnlyFolder') &&
manager.isSubpath(path, currentPath)) { editable = false; }
if (!$element.closest("#cp-app-drive-tree").length) {
hide.push('expandall');
hide.push('collapseall');
}
if (path.length === 1) {
// Can't rename, share, delete, or change the color of categories
hide.push('delete');
hide.push('rename');
hide.push('share');
hide.push('savelocal');
hide.push('color');
}
if (!$element.is('.cp-app-drive-element-owned')) {
hide.push('deleteowned');
}
if ($element.is('.cp-app-drive-element-restricted')) {
hide.push('rename', 'download', 'share', 'access', 'color');
}
if ($element.is('.cp-app-drive-element-notrash')) {
// We can't delete elements in virtual categories
hide.push('delete');
}
if (!$element.is('.cp-border-color-file')) {
//hide.push('download');
hide.push('openincode');
hide.push('preview');
}
if ($element.is('.cp-border-color-sheet')) {
hide.push('download');
}
if ($element.is('.cp-app-drive-element-file')) {
// No folder in files
hide.push('color');
hide.push('newfolder');
if ($element.is('.cp-app-drive-element-readonly')) {
hide.push('open'); // Remove open 'edit' mode
} else if ($element.is('.cp-app-drive-element-noreadonly')) {
hide.push('openro'); // Remove open 'view' mode
}
var metadata = manager.getFileData(manager.find(path));
if (!metadata || !Util.isPlainTextFile(metadata.fileType, metadata.title)) {
hide.push('openincode');
}
if (metadata.channel && metadata.channel.length < 48) {
hide.push('preview');
}
if (!metadata.channel || metadata.channel.length > 32 || metadata.rtChannel) {
hide.push('makeacopy'); // Not for blobs
}
} else if ($element.is('.cp-app-drive-element-sharedf')) {
if (containsFolder) {
// More than 1 folder selected: cannot create a new subfolder
hide.push('newfolder');
hide.push('expandall');
hide.push('collapseall');
}
containsFolder = true;
hide.push('openro');
hide.push('openincode');
hide.push('hashtag');
//hide.push('delete');
hide.push('makeacopy');
//hide.push('deleteowned');
} else { // it's a folder
if (containsFolder) {
// More than 1 folder selected: cannot create a new subfolder
hide.push('newfolder');
hide.push('expandall');
hide.push('collapseall');
}
containsFolder = true;
hide.push('savelocal');
hide.push('openro');
hide.push('openincode');
hide.push('properties', 'access');
hide.push('hashtag');
hide.push('makeacopy');
}
// If we're in the trash, hide restore and properties for non-root elements
if (type === "trash" && path && path.length > 4) {
hide.push('restore');
hide.push('properties');
}
// If we're not in the trash nor in a shared folder, hide "remove"
if (!manager.isInSharedFolder(path)
&& !$element.is('.cp-app-drive-element-sharedf')) {
// This isn't a shared folder: can't delete shared folder
hide.push('removesf');
} else if (type === "tree") {
// This is a shared folder or an element inside a shsared folder
// ==> can't move to trash
hide.push('delete');
}
if ($element.closest('[data-ro]').length) {
editable = false;
}
});
if (paths.length > 1) {
hide.push('restore');
hide.push('properties', 'access');
hide.push('rename');
hide.push('openparent');
hide.push('download');
hide.push('share');
hide.push('savelocal');
hide.push('openincode'); // can't because of race condition
hide.push('makeacopy');
hide.push('preview');
}
if (containsFolder && paths.length > 1) {
// Cannot open multiple folders
hide.push('open');
}
if (!APP.loggedIn) {
hide.push('openparent');
hide.push('rename');
}
filter = function ($el, className) {
if (hide.indexOf(className) !== -1) { return true; }
};
}
switch(type) {
case 'content':
show = ['newfolder', 'newsharedfolder', 'uploadfiles', 'uploadfolder', 'newdoc'];
break;
case 'tree':
show = ['open', 'openro', 'preview', 'openincode', 'expandall', 'collapseall',
'color', 'download', 'share', 'savelocal', 'rename', 'delete', 'makeacopy',
'deleteowned', 'removesf', 'access', 'properties', 'hashtag'];
break;
case 'default':
show = ['open', 'openro', 'preview', 'openincode', 'share', 'download', 'openparent', 'delete', 'deleteowned', 'properties', 'access', 'hashtag', 'makeacopy', 'savelocal', 'rename'];
break;
case 'trashtree': {
show = ['empty'];
break;
}
case 'trash': {
show = ['remove', 'deleteowned', 'restore', 'properties'];
}
}
var filtered = [];
show.forEach(function (className) {
var $el = $contextMenu.find('.cp-app-drive-context-' + className);
if ((!APP.editable || !editable) && $el.is('.cp-app-drive-context-editable')) { return; }
if ((!APP.editable || !editable) && $el.is('.cp-app-drive-context-editable')) { return; }
if (filter($el, className)) { return; }
$el.parent('li').show();
filtered.push('.cp-app-drive-context-' + className);
});
return filtered;
};
var updateContextButton = function () {
if (manager.isPathIn(currentPath, [TRASH])) {
$driveToolbar.find('cp-app-drive-toolbar-emptytrash').show();
} else {
$driveToolbar.find('cp-app-drive-toolbar-emptytrash').hide();
}
var $li = findSelectedElements();
if ($li.length === 0) {
$li = findDataHolder($tree.find('.cp-app-drive-element-active'));
}
var $button = $driveToolbar.find('#cp-app-drive-toolbar-context-mobile');
if ($button.length) { // mobile
if ($li.length !== 1
|| !$._data($li[0], 'events').contextmenu
|| $._data($li[0], 'events').contextmenu.length === 0) {
$button.hide();
return;
}
$button.show();
$button.css({
background: '#63b1f7'
});
window.setTimeout(function () {
$button.css({
background: ''
});
}, 500);
return;
}
// Non mobile
/*
var $container = $driveToolbar.find('#cp-app-drive-toolbar-contextbuttons');
if (!$container.length) { return; }
$container.html('');
var $element = $li.length === 1 ? $li : $($li[0]);
var paths = getSelectedPaths($element);
var menuType = $element.data('context');
if (!menuType) { return; }
//var actions = [];
var toShow = filterContextMenu(menuType, paths);
var $actions = $contextMenu.find('a');
$contextMenu.data('paths', paths);
$actions = $actions.filter(function (i, el) {
return toShow.some(function (className) { return $(el).is(className); });
});
$actions.each(function (i, el) {
var $a = $('<button>', {'class': 'cp-app-drive-element'});
if ($(el).attr('data-icon')) {
var font = $(el).attr('data-icon').indexOf('cptools') === 0 ? 'cptools' : 'fa';
$a.addClass(font).addClass($(el).attr('data-icon'));
$a.attr('title', $(el).text());
} else {
$a.text($(el).text());
}
$container.append($a);
$a.click(function() { $(el).click(); });
});
*/
};
var scrollTo = function ($element) {
// Current scroll position
var st = $content.scrollTop();
// Block height
var h = $content.height();
// Current top position of the element relative to the scroll position
var pos = Math.round($element.offset().top - $content.position().top);
// Element height
var eh = $element.outerHeight();
// New scroll value
var v = st + pos + eh - h;
// If the element is completely visile, don't change the scroll position
if (pos+eh <= h && pos >= 0) { return; }
$content.scrollTop(v);
};
// Add the "selected" class to the "li" corresponding to the clicked element
var onElementClick = APP.onElementClick = function (e, $element) {
// If "Ctrl" is pressed, do not remove the current selection
removeInput();
$element = findDataHolder($element);
// If we're selecting a new element with the left click, hide the menu
if (e) { APP.hideMenu(); }
// Remove the selection if we don't hold ctrl key or if we are right-clicking
if (!e || !e.ctrlKey) {
removeSelected(e && e.shiftKey);
}
if (!$element.length) {
log(Messages.fm_selectError);
return;
}
scrollTo($element);
// Add the selected class to the clicked / right-clicked element
// Remove the class if it already has it
// If ctrlKey, add to the selection
// If shiftKey, select a range of elements
var $elements = $content.find('.cp-app-drive-element:not(.cp-app-drive-element-header)');
var $selection = $elements.filter('.cp-app-drive-element-selected');
if (typeof sel.startSelected !== "number" || !e || (e.ctrlKey && !e.shiftKey)) {
sel.startSelected = $elements.index($element[0]);
sel.oldSelection = [];
$selection.each(function (idx, el) {
sel.oldSelection.push(el);
});
delete sel.endSelected;
}
if (e && e.shiftKey) {
var end = $elements.index($element[0]);
sel.endSelected = end;
var $el;
removeSelected(true);
sel.oldSelection.forEach(function (el) {
if (!isElementSelected($(el))) {
selectElement($(el));
}
});
for (var i = Math.min(sel.startSelected, sel.endSelected);
i <= Math.max(sel.startSelected, sel.endSelected);
i++) {
$el = $($elements.get(i));
if (!isElementSelected($el)) {
selectElement($el);
}
}
} else {
if (!isElementSelected($element)) {
selectElement($element);
} else {
unselectElement($element);
}
}
updateContextButton();
};
// show / hide dropdown separators
var hideSeparators = function ($menu) {
var showSep = false;
var $lastVisibleSep = null;
$menu.children().each(function (i, el) {
var $el = $(el);
if ($el.is(".dropdown-divider")) {
$el.css("display", showSep ? "list-item" : "none");
if (showSep) { $lastVisibleSep = $el; }
showSep = false;
}
else if ($el.is("li") && $el.css("display") !== "none") {
showSep = true;
}
});
if (!showSep && $lastVisibleSep) { $lastVisibleSep.css("display", "none"); } // remove last divider if no options after
};
// prepare and display contextmenu
var displayMenu = function (e) {
var $menu = $contextMenu;
// show / hide submenus
$menu.find(".dropdown-submenu").each(function (i, el) {
var $el = $(el);
$el.children(".dropdown-menu").css("display", "none");
$el.find("li").each(function (i, li) {
if ($(li).css("display") !== "none") {
$el.css("display", "block");
return;
}
});
});
// show / hide separators
$menu.find(".dropdown-menu").each(function (i, menu) {
hideSeparators($(menu));
});
// show contextmenu at cursor position
$menu.css({ display: "block" });
if (APP.mobile()) {
$menu.css({
top: ($("#cp-app-drive-toolbar-context-mobile").offset().top + 32) + 'px',
right: '0px',
left: ''
});
return;
}
var h = $menu.outerHeight();
var w = $menu.outerWidth();
var wH = window.innerHeight;
var wW = window.innerWidth;
if (h > wH) {
$menu.css({
top: '0px',
bottom: ''
});
} else if (e.pageY + h <= wH) {
$menu.css({
top: e.pageY+'px',
bottom: ''
});
} else {
$menu.css({
bottom: '0px',
top: ''
});
}
if(w > wW) {
$menu.css({
left: '0px',
right: ''
});
} else if (e.pageX + w <= wW) {
$menu.css({
left: e.pageX+'px',
right: ''
});
} else {
$menu.css({
left: '',
right: '0px',
});
}
};
// Open the selected context menu on the closest "li" element
var openContextMenu = function (type) {
return function (e) {
APP.hideMenu();
e.stopPropagation();
var paths;
if (type === 'content') {
paths = [{path: $(e.target).closest('#' + FOLDER_CONTENT_ID).data('path')}];
if (!paths) { return; }
removeSelected();
} else {
var $element = findDataHolder($(e.target));
// if clicked from tree
var fromTree = $element.closest("#cp-app-drive-tree").length;
if (fromTree) {
removeSelected();
}
// if clicked on non selected element
if (!isElementSelected($element)) {
removeSelected();
}
if (type === 'trash' && !$element.data('path')) { return; }
if (!$element.length) {
logError("Unable to locate the .element tag", e.target);
log(Messages.fm_contextMenuError);
return false;
}
if (!isElementSelected($element)) {
selectElement($element);
}
paths = getSelectedPaths($element);
}
$contextMenu.attr('data-menu-type', type);
filterContextMenu(type, paths);
displayMenu(e);
$(".cp-app-drive-context-noAction").toggle($contextMenu.find('li:visible').length === 0);
$contextMenu.data('paths', paths);
return false;
};
};
var getElementName = function (path) {
var file = manager.find(path);
if (!file) { return; }
if (manager.isSharedFolder(file)) {
return manager.getSharedFolderData(file).title;
}
return manager.getTitle(file);
};
// moveElements is able to move several paths to a new location
var moveElements = function (paths, newPath, copy, cb) {
if (!APP.editable) { return; }
// Cancel drag&drop from TRASH to TRASH
if (manager.isPathIn(newPath, [TRASH]) && paths.length && paths[0][0] === TRASH) {
return;
}
var newCb = function () {
paths.forEach(function (path) {
LS.moveFoldersOpened(path, newPath);
});
cb();
};
if (paths.some(function (p) { return manager.comparePath(newPath, p); })) { return void cb(); }
manager.move(paths, newPath, newCb, copy);
};
// Delete paths from the drive and/or shared folders (without moving them to the trash)
var deletePaths = function (paths, pathsList) {
pathsList = pathsList || [];
if (paths) {
paths.forEach(function (p) { pathsList.push(p.path); });
}
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [pathsList.length]);
if (pathsList.length === 1) {
msg = Messages.fm_removePermanentlyDialog;
}
UI.confirm(msg, function(res) {
$(window).focus();
if (!res) { return; }
manager.delete(pathsList, function () {
pathsList.forEach(LS.removeFoldersOpened);
removeSelected();
refresh();
});
}, null, true);
};
// Drag & drop
// The data transferred is a stringified JSON containing the path of the dragged element
var onDrag = function (ev, path) {
var paths = [];
var $element = findDataHolder($(ev.target));
if ($element.hasClass('cp-app-drive-element-selected')) {
var $selected = findSelectedElements();
$selected.each(function (idx, elmt) {
var ePath = $(elmt).data('path');
if (ePath) {
var val = manager.find(ePath);
if (!val) { return; } // Error? A ".selected" element is not in the object
paths.push({
path: ePath,
value: {
name: getElementName(ePath),
el: val
}
});
}
});
} else {
removeSelected();
selectElement($element);
var val = manager.find(path);
if (!val) { return; } // The element is not in the object
paths = [{
path: path,
value: {
name: getElementName(path),
el: val
}
}];
}
var data = {
'path': paths
};
ev.dataTransfer.setData("text", stringify(data));
};
var findDropPath = function (target) {
var $target = $(target);
var $el;
if ($target.is(".cp-app-drive-path-element")) {
$el = $target;
}
else {
$el = findDataHolder($target);
}
var newPath = $el.data('path');
var dropEl = newPath && manager.find(newPath);
if (newPath && manager.isSharedFolder(dropEl)) {
newPath.push(manager.user.userObject.ROOT);
} else if ((!newPath || manager.isFile(dropEl))
&& $target.parents('#cp-app-drive-content')) {
newPath = currentPath;
}
return newPath;
};
var onFileDrop = APP.onFileDrop = function (file, e) {
var ev = {
target: e.target,
path: findDropPath(e.target)
};
APP.FM.onFileDrop(file, ev);
};
var onDrop = function (ev) {
ev.preventDefault();
$('.cp-app-drive-element-droppable').removeClass('cp-app-drive-element-droppable');
var data = ev.dataTransfer.getData("text");
var newPath = findDropPath(ev.target);
if (!newPath) { return; }
var sfId = manager.isInSharedFolder(newPath);
if (sfId && folders[sfId] && folders[sfId].readOnly) {
return void UI.warn(Messages.fm_forbidden);
}
// Don't use the normal drop handler for file upload
var fileDrop = ev.dataTransfer.files;
if (fileDrop.length) { return void onFileDrop(fileDrop, ev); }
var oldPaths = JSON.parse(data).path;
if (!oldPaths) { return; }
// A moved element should be removed from its previous location
var movedPaths = [];
var sharedF = false;
oldPaths.forEach(function (p) {
movedPaths.push(p.path);
if (!sharedF && manager.isInSharedFolder(p.path)) {
sharedF = true;
}
});
if (sharedF && manager.isPathIn(newPath, [TRASH])) {
// TODO create a key here?
// You can't move to YOUR trash documents stored in a shared folder
// TODO or keep deletePaths: trigger the "Remove from cryptdrive" modal
return void UI.warn(Messages.error);
//return void deletePaths(null, movedPaths);
}
var copy = false;
if (ev.ctrlKey || (ev.metaKey && APP.isMac)) {
copy = true;
}
if (movedPaths && movedPaths.length) {
moveElements(movedPaths, newPath, copy, refresh);
}
};
var addDragAndDropHandlers = function ($element, path, isFolder, droppable) {
if (!APP.editable) { return; }
// "dragenter" is fired for an element and all its children
// "dragleave" may be fired when entering a child
// --> we use pointer-events: none in CSS, but we still need a counter to avoid some issues
// --> We store the number of enter/leave and the element entered and we remove the
// highlighting only when we have left everything
var counter = 0;
$element.on('dragstart', function (e) {
e.stopPropagation();
counter = 0;
onDrag(e.originalEvent, path);
});
$element.on('mousedown', function (e) {
e.stopPropagation();
});
// Add drop handlers if we are not in the trash and if the element is a folder
if (!droppable || !isFolder) { return; }
$element.on('dragover', function (e) {
e.preventDefault();
});
$element.on('drop', function (e) {
e.preventDefault();
e.stopPropagation();
onDrop(e.originalEvent);
});
$element.on('dragenter', function (e) {
e.preventDefault();
e.stopPropagation();
counter++;
$element.addClass('cp-app-drive-element-droppable');
});
$element.on('dragleave', function (e) {
e.preventDefault();
e.stopPropagation();
counter--;
if (counter <= 0) {
counter = 0;
$element.removeClass('cp-app-drive-element-droppable');
}
});
};
addDragAndDropHandlers($content, null, true, true);
$tree.on('drop dragover', function (e) {
e.preventDefault();
e.stopPropagation();
});
$driveToolbar.on('drop dragover', function (e) {
e.preventDefault();
e.stopPropagation();
});
// In list mode, display metadata from the filesData object
var _addOwnership = function ($span, $state, data) {
if (data && Array.isArray(data.owners) && data.owners.indexOf(edPublic) !== -1) {
var $owned = $ownedIcon.clone().appendTo($state);
$owned.attr('title', Messages.fm_padIsOwned);
$span.addClass('cp-app-drive-element-owned');
} /* else if (data.owners && data.owners.length) {
var $owner = $ownerIcon.clone().appendTo($state);
$owner.attr('title', Messages.fm_padIsOwnedOther);
} */
};
var thumbsUrls = {};
var addFileData = function (element, $element) {
if (!manager.isFile(element)) { return; }
var data = manager.getFileData(element);
if (!Object.keys(data).length) {
return true;
}
var href = data.href || data.roHref;
if (!data) { return void logError("No data for the file", element); }
var hrefData = Hash.parsePadUrl(href);
if (hrefData.type) {
$element.addClass('cp-border-color-'+hrefData.type);
}
var $state = $('<span>', {'class': 'cp-app-drive-element-state'});
if (hrefData.hashData && hrefData.hashData.mode === 'view') {
var $ro = $readonlyIcon.clone().appendTo($state);
$ro.attr('title', Messages.readonly);
}
if (data.filename && data.filename !== data.title) {
var $renamed = $renamedIcon.clone().appendTo($state);
$renamed.attr('data-cptippy-html', 'true');
$renamed.attr('title', Messages._getKey('fm_renamedPad', [Util.fixHTML(data.title)]));
}
if (hrefData.hashData && hrefData.hashData.password) {
var $password = $passwordIcon.clone().appendTo($state);
$password.attr('title', Messages.fm_passwordProtected || '');
}
if (data.expire) {
var $expire = $expirableIcon.clone().appendTo($state);
$expire.attr('title', Messages._getKey('fm_expirablePad', [new Date(data.expire).toLocaleString()]));
}
_addOwnership($element, $state, data);
var name = manager.getTitle(element);
// The element with the class '.name' is underlined when the 'li' is hovered
var $name = $('<span>', {'class': 'cp-app-drive-element-name'}).text(name);
$element.append($name);
$element.append($state);
if (getViewMode() === 'grid') {
$element.attr('title', name);
}
// display the thumbnail
// if the thumbnail has already been displayed once, do not reload it, keep the same url
if (thumbsUrls[element]) {
var img = new Image();
img.src = thumbsUrls[element];
$element.prepend(img);
$(img).addClass('cp-app-drive-element-grid cp-app-drive-element-thumbnail');
$(img).attr("draggable", false);
}
else {
common.displayThumbnail(href || data.roHref, data.channel, data.password, $element, function ($thumb) {
// Called only if the thumbnail exists
// Remove the .hide() added by displayThumnail() because it hides the icon in list mode too
$element.find('.cp-icon').removeAttr('style');
$thumb.addClass('cp-app-drive-element-grid cp-app-drive-element-thumbnail');
$thumb.attr("draggable", false);
thumbsUrls[element] = $thumb[0].src;
});
}
var type = Messages.type[hrefData.type] || hrefData.type;
var $type = $('<span>', {
'class': 'cp-app-drive-element-type cp-app-drive-element-list'
}).text(type);
var $adate = $('<span>', {
'class': 'cp-app-drive-element-atime cp-app-drive-element-list'
}).text(getDate(data.atime));
var $cdate = $('<span>', {
'class': 'cp-app-drive-element-ctime cp-app-drive-element-list'
}).text(getDate(data.ctime));
$element.append($type).append($adate).append($cdate);
};
var addFolderData = function (element, key, $span) {
if (!element || !manager.isFolder(element)) { return; }
// The element with the class '.name' is underlined when the 'li' is hovered
var $state = $('<span>', {'class': 'cp-app-drive-element-state'});
var $ro;
if (manager.isSharedFolder(element)) {
var data = manager.getSharedFolderData(element);
var fId = element;
key = data.title || data.lastTitle || Messages.fm_deletedFolder;
element = Util.find(manager, ['folders', element, 'proxy', manager.user.userObject.ROOT]) || {};
$span.addClass('cp-app-drive-element-sharedf');
_addOwnership($span, $state, data);
var hrefData = Hash.parsePadUrl(data.href || data.roHref);
if (hrefData.hashData && hrefData.hashData.password) {
var $password = $passwordIcon.clone().appendTo($state);
$password.attr('title', Messages.fm_passwordProtected || '');
}
if (hrefData.hashData && hrefData.hashData.mode === 'view') {
$ro = $readonlyIcon.clone().appendTo($state);
$ro.attr('title', Messages.readonly);
}
if (files.restrictedFolders[fId]) {
var $restricted = $restrictedIcon.clone().appendTo($state);
$restricted.attr('title', Messages.fm_restricted);
}
var $shared = $sharedIcon.clone().appendTo($state);
$shared.attr('title', Messages.fm_canBeShared);
} else if ($content.data('readOnlyFolder') || APP.readOnly) {
$ro = $readonlyIcon.clone().appendTo($state);
$ro.attr('title', Messages.readonly);
}
var sf = manager.hasSubfolder(element);
var hasFiles = manager.hasFile(element);
var $name = $('<span>', {'class': 'cp-app-drive-element-name'}).text(key);
var $subfolders = $('<span>', {
'class': 'cp-app-drive-element-folders cp-app-drive-element-list'
}).text(sf);
var $files = $('<span>', {
'class': 'cp-app-drive-element-files cp-app-drive-element-list'
}).text(hasFiles);
var $filler = $('<span>', {
'class': 'cp-app-drive-element-filler cp-app-drive-element-list'
});
if (getViewMode() === 'grid') {
$span.attr('title', key);
}
$span.append($name).append($state).append($subfolders).append($files).append($filler);
};
// This is duplicated in cryptpad-common, it should be unified
var getFileIcon = function (id) {
var data = manager.getFileData(id);
return UI.getFileIcon(data);
};
var getIcon = UI.getIcon;
var createShareButton = function (id, $container) {
var $shareBlock = $('<button>', {
'class': 'cp-toolbar-share-button',
title: Messages.shareButton
});
$sharedIcon.clone().appendTo($shareBlock);
$('<span>').text(Messages.shareButton).appendTo($shareBlock);
var data = manager.getSharedFolderData(id);
var parsed = (data.href && data.href.indexOf('#') !== -1) ? Hash.parsePadUrl(data.href) : {};
var roParsed = Hash.parsePadUrl(data.roHref) || {};
if (!parsed.hash && !roParsed.hash) { return void console.error("Invalid href: "+(data.href || data.roHref)); }
var friends = common.getFriends();
var ro = folders[id] && folders[id].version >= 2;
// If we're a viewer and this is an old shared folder (no read-only mode), we
// can't share the read-only URL and we don't have access to the edit one.
// We should hide the share button.
if (!data.href && !ro) { return; }
$shareBlock.click(function () {
Share.getShareModal(common, {
teamId: APP.team,
origin: APP.origin,
pathname: "/drive/",
friends: friends,
title: data.title,
password: data.password,
sharedFolder: true,
common: common,
hashes: {
editHash: parsed.hash,
viewHash: ro && roParsed.hash,
}
});
});
$container.append($shareBlock);
return $shareBlock;
};
// Create the "li" element corresponding to the file/folder located in "path"
var createElement = function (path, elPath, root, isFolder) {
// Forbid drag&drop inside the trash
var isTrash = path[0] === TRASH;
var newPath = path.slice();
var key;
var element;
if (isTrash && Array.isArray(elPath)) {
key = elPath[0];
elPath.forEach(function (k) { newPath.push(k); });
element = manager.find(newPath);
} else {
key = elPath;
newPath.push(key);
element = root[key];
}
var restricted = files.restrictedFolders[element];
var isSharedFolder = manager.isSharedFolder(element);
var $icon = !isFolder ? getFileIcon(element) : undefined;
var ro = manager.isReadOnlyFile(element);
// ro undefined means it's an old hash which doesn't support read-only
var roClass = typeof(ro) === 'undefined' ? '.cp-app-drive-element-noreadonly' :
ro ? '.cp-app-drive-element-readonly' : '';
var liClass = '.cp-app-drive-element-file';
var restrictedClass = restricted ? '.cp-app-drive-element-restricted' : '';
if (isSharedFolder) {
liClass = '.cp-app-drive-element-folder';
$icon = $sharedFolderIcon.clone();
$icon.css("color", getFolderColor(path.concat(elPath)));
} else if (isFolder) {
liClass = '.cp-app-drive-element-folder';
$icon = manager.isFolderEmpty(root[key]) ? $folderEmptyIcon.clone() : $folderIcon.clone();
$icon.css("color", getFolderColor(path.concat(elPath)));
}
var classes = restrictedClass + roClass + liClass;
var $element = $(h('li.cp-app-drive-element.cp-app-drive-element-row' + classes, {
draggable: true
}));
$element.data('path', newPath);
if (isElementSelected($element)) {
selectElement($element);
}
$element.prepend($icon).dblclick(function () {
if (restricted) {
UI.warn(Messages.fm_restricted);
return;
}
if (isSharedFolder && !manager.folders[element]) {
UI.warn(Messages.fm_deletedFolder);
return;
}
if (isFolder) {
APP.displayDirectory(newPath);
return;
}
if (isTrash) { return; }
openFile(root[key]);
});
var invalid;
if (isFolder) {
invalid = addFolderData(element, key, $element);
} else {
invalid = addFileData(element, $element);
}
if (invalid) {
return;
}
$element.find('.fa').on('mouseenter', function (e) {
if ($element[0] && $element[0]._tippy) {
$element[0]._tippy.destroy();
}
e.stopPropagation();
});
var droppable = !isTrash && !APP.$content.data('readOnlyFolder') && !restricted;
addDragAndDropHandlers($element, newPath, isFolder, droppable);
$element.click(function(e) {
e.stopPropagation();
onElementClick(e, $element);
});
if (!isTrash) {
$element.on('contextmenu', openContextMenu('tree'));
$element.data('context', 'tree');
} else {
$element.contextmenu(openContextMenu('trash'));
$element.data('context', 'trash');
}
var isNewFolder = APP.newFolder && manager.comparePath(newPath, APP.newFolder);
if (isNewFolder) {
appStatus.onReady(function () {
window.setTimeout(function () { displayRenameInput($element, newPath); }, 0);
});
delete APP.newFolder;
}
if (isSharedFolder && APP.convertedFolder === element) {
setTimeout(function () {
var $fakeButton = createShareButton(element, $('<div>'));
if (!$fakeButton) { return; }
$fakeButton.click();
}, 100);
}
return $element;
};
// Display the full path in the title when displaying a directory from the trash
/*
var getTrashTitle = function (path) {
if (!path[0] || path[0] !== TRASH) { return; }
var title = TRASH_NAME;
for (var i=1; i<path.length; i++) {
if (i === 3 && path[i] === 'element') {}
else if (i === 2 && parseInt(path[i]) === path[i]) {
if (path[i] !== 0) {
title += " [" + path[i] + "]";
}
} else {
title += " / " + path[i];
}
}
return title;
}; */
var getPrettyName = function (name) {
var pName;
switch (name) {
case ROOT: pName = ROOT_NAME; break;
case TRASH: pName = TRASH_NAME; break;
case TEMPLATE: pName = TEMPLATE_NAME; break;
case FILES_DATA: pName = FILES_DATA_NAME; break;
case SEARCH: pName = SEARCH_NAME; break;
case RECENT: pName = RECENT_NAME; break;
case OWNED: pName = OWNED_NAME; break;
case TAGS: pName = TAGS_NAME; break;
case SHARED_FOLDER: pName = SHARED_FOLDER_NAME; break;
default: pName = name;
}
return pName;
};
var drivePathOverflowing = function () {
var $container = $(".cp-app-drive-path");
if ($container.length) {
$container.css("overflow", "hidden");
var overflown = $container[0].scrollWidth > $container[0].clientWidth;
$container.css("overflow", "");
return overflown;
}
};
var collapseDrivePath = function () {
var $container = $(".cp-app-drive-path-inner");
var $spanCollapse = $(".cp-app-drive-path-collapse");
$spanCollapse.css("display", "none");
var $pathElements = $container.find(".cp-app-drive-path-element");
$pathElements.not($spanCollapse).css("display", "");
var oneFolder = currentPath.length > 1 + (currentPath[0] === SHARED_FOLDER);
if (oneFolder && drivePathOverflowing()) {
var collapseLevel = 0;
var removeOverflowElement = function () {
if (drivePathOverflowing()) {
if ($pathElements.length <= 3) {
return false;
}
collapseLevel++;
if ($($pathElements.get(-2)).is(".cp-app-drive-path-separator")) {
$($pathElements.get(-2)).css("display", "none");
$pathElements = $pathElements.not($pathElements.get(-2));
}
$($pathElements.get(-2)).css("display", "none");
$pathElements = $pathElements.not($pathElements.get(-2));
return true;
}
};
currentPath.every(removeOverflowElement);
$spanCollapse.css("display", "");
removeOverflowElement();
var tipPath = currentPath.slice(0, collapseLevel);
tipPath[0] = getPrettyName(tipPath[0]);
$spanCollapse.attr("title", tipPath.join(" / "));
$spanCollapse[0].onclick = function () {
APP.displayDirectory(LS.getLastOpenedFolder().slice(0, collapseLevel));
};
}
};
window.addEventListener("resize", collapseDrivePath);
var treeResizeObserver = new MutationObserver(collapseDrivePath);
treeResizeObserver.observe($("#cp-app-drive-tree")[0], {"attributes": true});
// Create the title block with the "parent folder" button
var createTitle = function ($container, path, noStyle) {
if (!path || path.length === 0) { return; }
var isTrash = manager.isPathIn(path, [TRASH]);
if (APP.mobile() && !noStyle) { // noStyle means title in search result
return $container;
}
var isVirtual = virtualCategories.indexOf(path[0]) !== -1;
var el = isVirtual ? undefined : manager.find(path);
path = path[0] === SEARCH ? path.slice(0,1) : path;
var isInTrashRoot = manager.isInTrashRoot(path);
var $outer = $('<div>', {'class': 'cp-app-drive-path'});
var $inner = $('<div>', {'class': 'cp-app-drive-path-inner'});
$outer.append($inner);
$container.prepend($outer);
var skipNext = false; // When encountering a shared folder, skip a key in the path
path.forEach(function (p, idx) {
if (isTrash && [2,3].indexOf(idx) !== -1) { return; }
if (skipNext) { skipNext = false; return; }
var name = p;
if (manager.isFile(el) && isInTrashRoot && idx === 1) {
idx = 3;
}
// Check if the current element is a shared folder. If it is, get its
// name and skip the next itme in the path (it will be "root")
var currentEl = isVirtual ? undefined : manager.find(path.slice(0, idx+1));
// If we're in trash root, check if the "element" is a shared folder
if (isTrash && idx === 1) {
currentEl = manager.find(path.slice(0, idx+3));
}
// Name and skip next...
// "p === SHARED_FOLDER" for anonymous shared folders
if (p === SHARED_FOLDER || (currentEl && manager.isSharedFolder(currentEl))) {
name = manager.getSharedFolderData(currentEl || APP.newSharedFolder).title;
skipNext = true;
}
var $span = $('<span>', {'class': 'cp-app-drive-path-element'});
if (idx < path.length - 1) {
if (!noStyle) {
$span.addClass('cp-app-drive-path-clickable');
$span.click(function (e) {
e.stopPropagation();
var sliceEnd = idx + 1;
if (isTrash && idx === 1) { sliceEnd = 4; } // Make sure we don't show the index or 'element' and 'path'
APP.displayDirectory(path.slice(0, sliceEnd));
});
}
} else if (idx > 0 && manager.isFile(el)) {
name = getElementName(path);
}
$span.data("path", path.slice(0, idx + 1));
addDragAndDropHandlers($span, path.slice(0, idx), true, true);
if (idx === 0) { name = p === SHARED_FOLDER ? name : getPrettyName(p); }
else {
var $span2 = $('<span>', {
'class': 'cp-app-drive-path-element cp-app-drive-path-separator'
}).text(' / ');
$inner.prepend($span2);
}
$span.text(name).prependTo($inner);
});
var $spanCollapse = $('<span>', {
'class': 'cp-app-drive-path-element cp-app-drive-path-collapse'
}).text(' ... ');
$inner.append($spanCollapse);
collapseDrivePath();
};
var createInfoBox = function (path) {
if (APP.readOnly || $content.data('readOnlyFolder')) { return; }
var $box = $('<div>', {'class': 'cp-app-drive-content-info-box'});
var msg;
switch (path[0]) {
case ROOT:
msg = Messages.fm_info_root;
break;
case TEMPLATE:
msg = Messages.fm_info_template;
break;
case TRASH:
msg = Messages.fm_info_trash;
break;
case FILES_DATA:
msg = Messages.fm_info_allFiles;
break;
case RECENT:
msg = Messages.fm_info_recent;
break;
case OWNED:
msg = Messages.fm_info_owned;
break;
case TAGS:
break;
default:
msg = undefined;
}
if (history.isHistoryMode && history.sfId) {
// Shared folder history: always display the warning
var sfName = (manager.getSharedFolderData(history.sfId) || {}).title || Messages.fm_sharedFolderName;
msg = Messages._getKey('fm_info_sharedFolderHistory', [sfName]);
return $(common.fixLinks($box.html(msg)));
}
if (!APP.loggedIn) {
msg = APP.newSharedFolder ? Messages.fm_info_sharedFolder : Messages._getKey('fm_info_anonymous', [ApiConfig.inactiveTime || 90]);
return $(common.fixLinks($box.html(msg)));
}
if (!msg || APP.store['hide-info-' + path[0]] === '1') {
$box.hide();
} else {
$box.text(msg);
var $close = $closeIcon.clone().css({
'cursor': 'pointer',
'margin-left': '10px',
title: Messages.fm_closeInfoBox
}).on('click', function () {
$box.hide();
APP.store['hide-info-' + path[0]] = '1';
localStore.put('hide-info-' + path[0], '1');
});
$box.prepend($close);
}
return $box;
};
// Create the button allowing the user to switch from list to icons modes
var createViewModeButton = function ($container) {
var $listButton = $listIcon.clone();
var $gridButton = $gridIcon.clone();
$listButton.click(function () {
$gridButton.show();
$listButton.hide();
setViewMode('list');
$('#' + FOLDER_CONTENT_ID).removeClass('cp-app-drive-content-grid');
$('#' + FOLDER_CONTENT_ID).addClass('cp-app-drive-content-list');
Feedback.send('DRIVE_LIST_MODE');
});
$gridButton.click(function () {
$listButton.show();
$gridButton.hide();
setViewMode('grid');
$('#' + FOLDER_CONTENT_ID).addClass('cp-app-drive-content-grid');
$('#' + FOLDER_CONTENT_ID).removeClass('cp-app-drive-content-list');
Feedback.send('DRIVE_GRID_MODE');
});
if (getViewMode() === 'list') {
$listButton.hide();
} else {
$gridButton.hide();
}
$listButton.attr('title', Messages.fm_viewListButton);
$gridButton.attr('title', Messages.fm_viewGridButton);
$container.append($listButton).append($gridButton);
};
var emptyTrashModal = function () {
var ownedInTrash = manager.ownedInTrash();
var hasOwned = Array.isArray(ownedInTrash) && ownedInTrash.length;
var content = h('p', [
Messages.fm_emptyTrashDialog,
hasOwned ? h('br') : undefined,
hasOwned ? UI.setHTML(h('span'), Messages.fm_emptyTrashOwned) : undefined
]);
var buttons = [{
className: 'cancel',
name: Messages.cancelButton,
onClick: function () {},
keys: [27]
}];
if (hasOwned) {
buttons.push({
className: 'danger',
iconClass: '.cptools.cptools-destroy',
name: Messages.fc_delete_owned,
onClick: function () {
manager.emptyTrash(true, refresh);
},
keys: []
});
}
buttons.push({
className: 'primary',
// We may want to use a new key here
iconClass: '.fa.fa-trash',
name: hasOwned ? Messages.fc_remove : Messages.okButton,
onClick: function () {
manager.emptyTrash(false, refresh);
},
keys: [13]
});
var m = UI.dialog.customModal(content, {
buttons: buttons
});
UI.openCustomModal(m);
};
var createEmptyTrashButton = function () {
var button = h('button.btn.btn-danger', [
h('i.fa.'+faTrash),
h('span', Messages.fc_empty)
]);
$(button).click(function () {
emptyTrashModal();
});
return $(h('div.cp-app-drive-button', button));
};
// Get the upload options
var addSharedFolderModal = function (cb) {
var docsHref = common.getBounceURL("https://docs.cryptpad.fr/en/user_guide/share_and_access.html#owners");
// Ask for name, password and owner
var content = h('div', [
h('h4', Messages.sharedFolders_create),
h('label', {for: 'cp-app-drive-sf-name'}, Messages.sharedFolders_create_name),
h('input#cp-app-drive-sf-name', {type: 'text', placeholder: Messages.fm_newFolder}),
h('label', {for: 'cp-app-drive-sf-password'}, Messages.sharedFolders_create_password),
UI.passwordInput({id: 'cp-app-drive-sf-password'}),
h('span', {
style: 'display:flex;align-items:center;justify-content:space-between'
}, [
UI.createCheckbox('cp-app-drive-sf-owned', Messages.sharedFolders_create_owned, true),
UI.createHelper(docsHref, Messages.creation_owned1) // TODO
]),
]);
$(content).find('#cp-app-drive-sf-name').keydown(function (e) {
if (e.which === 13) {
UI.findOKButton().click();
}
});
UI.confirm(content, function (yes) {
if (!yes) { return void cb(); }
// Get the values
var newName = $(content).find('#cp-app-drive-sf-name').val();
var password = $(content).find('#cp-app-drive-sf-password').val() || undefined;
var owned = $(content).find('#cp-app-drive-sf-owned').is(':checked');
cb({
name: newName,
password: password,
owned: owned
});
});
};
var getNewPadTypes = function () {
var arr = [];
AppConfig.availablePadTypes.forEach(function (type) {
if (type === 'drive') { return; }
if (type === 'teams') { return; }
if (type === 'contacts') { return; }
if (type === 'todo') { return; }
if (type === 'file') { return; }
if (type === 'accounts') { return; }
if (!APP.loggedIn && AppConfig.registeredOnlyTypes &&
AppConfig.registeredOnlyTypes.indexOf(type) !== -1) {
return;
}
arr.push(type);
});
return arr;
};
var showUploadFilesModal = function () {
var $input = $('<input>', {
'type': 'file',
'style': 'display: none;',
'multiple': 'multiple'
}).on('change', function (e) {
var files = Util.slice(e.target.files);
files.forEach(function (file) {
var ev = {
target: $content[0],
path: findDropPath($content[0])
};
APP.FM.handleFile(file, ev);
});
});
$input.click();
};
// create the folder structure before to upload files from folder
var uploadFolder = function (fileList) {
var currentFolder = currentPath;
// create an array of all the files relative path
var files = Array.prototype.map.call(fileList, function (file) {
return {
file: file,
path: file.webkitRelativePath.split("/"),
};
});
// if folder name already exist in drive, rename it
var uploadedFolderName = files[0].path[0];
var availableName = manager.user.userObject.getAvailableName(manager.find(currentFolder), uploadedFolderName);
// ask for folder name and files options, then upload all the files!
APP.FM.showFolderUploadModal(availableName, function (folderUploadOptions) {
if (!folderUploadOptions) { return; }
// verfify folder name is possible, and update files path
availableName = manager.user.userObject.getAvailableName(manager.find(currentFolder), folderUploadOptions.folderName);
if (uploadedFolderName !== availableName) {
files.forEach(function (file) {
file.path[0] = availableName;
});
}
// uploadSteps is an array of objects {folders: [], files: []}, containing all the folders and files to create safely
// at the index i + 1, the files and folders are children of the folders at the index i
var maxSteps = files.reduce(function (max, file) { return Math.max(max, file.path.length); }, 0);
var uploadSteps = [];
for (var i = 0 ; i < maxSteps ; i++) {
uploadSteps[i] = {
folders: [],
files: [],
};
}
files.forEach(function (file) {
// add steps to create subfolders containing file
for (var depth = 0 ; depth < file.path.length - 1 ; depth++) {
var subfolderStr = file.path.slice(0, depth + 1).join("/");
if (uploadSteps[depth].folders.indexOf(subfolderStr) === -1) {
uploadSteps[depth].folders.push(subfolderStr);
}
}
// add step to upload file (one step later than the step of its direct parent folder)
uploadSteps[file.path.length - 1].files.push(file);
});
// add folders, then add files when theirs folders have been created
// wait for the folders to be created to go to the next step (don't wait for the files)
var stepByStep = function (uploadSteps, i) {
if (i >= uploadSteps.length) { return; }
nThen(function (waitFor) {
// add folders
uploadSteps[i].folders.forEach(function (folder) {
var folderPath = folder.split("/");
var parentFolder = currentFolder.concat(folderPath.slice(0, -1));
var folderName = folderPath.slice(-1);
manager.addFolder(parentFolder, folderName, waitFor(refresh));
});
// upload files
uploadSteps[i].files.forEach(function (file) {
var ev = {
target: $content[0],
path: currentFolder.concat(file.path.slice(0, -1)),
};
APP.FM.handleFile(file.file, ev, folderUploadOptions);
});
}).nThen(function () {
stepByStep(uploadSteps, i + 1);
});
};
stepByStep(uploadSteps, 0);
});
};
var showUploadFolderModal = function () {
var $input = $('<input>', {
'type': 'file',
'style': 'display: none;',
'multiple': 'multiple',
'webkitdirectory': true,
}).on('change', function (e) {
uploadFolder(e.target.files);
});
$input.click();
};
var addNewPadHandlers = function ($block, isInRoot) {
// Handlers
if (isInRoot) {
var onCreated = function (err, info) {
if (err) {
if (err === E_OVER_LIMIT) {
return void UI.alert(Messages.pinLimitDrive, null, true);
}
return void UI.alert(Messages.fm_error_cantPin);
}
APP.newFolder = info.newPath;
refresh();
};
$block.find('a.cp-app-drive-new-folder, li.cp-app-drive-new-folder')
.click(function () {
manager.addFolder(currentPath, null, onCreated);
});
if (!APP.disableSF && !manager.isInSharedFolder(currentPath)) {
$block.find('a.cp-app-drive-new-shared-folder, li.cp-app-drive-new-shared-folder')
.click(function () {
addSharedFolderModal(function (obj) {
if (!obj) { return; }
manager.addSharedFolder(currentPath, obj, refresh);
});
});
}
$block.find('a.cp-app-drive-new-fileupload, li.cp-app-drive-new-fileupload').click(showUploadFilesModal);
$block.find('a.cp-app-drive-new-folderupload, li.cp-app-drive-new-folderupload').click(showUploadFolderModal);
}
$block.find('a.cp-app-drive-new-doc, li.cp-app-drive-new-doc')
.click(function () {
var type = $(this).attr('data-type') || 'pad';
var path = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
openIn(type, path, APP.team);
});
};
var createNewButton = function (isInRoot, $container) {
if (!APP.editable) { return; }
if (!APP.loggedIn) { return; } // Anonymous users can use the + menu in the toolbar
if (!manager.isPathIn(currentPath, [ROOT, 'hrefArray'])) { return; }
// Create dropdown
var options = [];
if (isInRoot) {
options.push({
tag: 'a',
attributes: {'class': 'cp-app-drive-new-folder'},
content: $('<div>').append($folderIcon.clone()).html() + Messages.fm_folder
});
if (!APP.disableSF && !manager.isInSharedFolder(currentPath)) {
options.push({
tag: 'a',
attributes: {'class': 'cp-app-drive-new-shared-folder'},
content: $('<div>').append($sharedFolderIcon.clone()).html() + Messages.fm_sharedFolder
});
}
options.push({tag: 'hr'});
options.push({
tag: 'a',
attributes: {'class': 'cp-app-drive-new-fileupload'},
content: $('<div>').append(getIcon('fileupload')).html() + Messages.uploadButton
});
if (APP.allowFolderUpload) {
options.push({
tag: 'a',
attributes: {'class': 'cp-app-drive-new-folderupload'},
content: $('<div>').append(getIcon('folderupload')).html() + Messages.uploadFolderButton
});
}
options.push({tag: 'hr'});
}
getNewPadTypes().forEach(function (type) {
var attributes = {
'class': 'cp-app-drive-new-doc',
'data-type': type,
'href': '#'
};
options.push({
tag: 'a',
attributes: attributes,
content: $('<div>').append(getIcon(type)).html() + Messages.type[type]
});
});
var $plusIcon = $('<div>').append($('<span>', {'class': 'fa fa-plus'}));
var dropdownConfig = {
text: $plusIcon.html() + '<span>'+Messages.fm_newButton+'</span>',
options: options,
feedback: 'DRIVE_NEWPAD_LOCALFOLDER',
common: common
};
var $block = UIElements.createDropdown(dropdownConfig);
// Custom style:
$block.find('button').addClass('cp-app-drive-toolbar-new');
addNewPadHandlers($block, isInRoot);
$container.append($block);
};
var SORT_FOLDER_DESC = 'sortFoldersDesc';
var SORT_FILE_BY = 'sortFilesBy';
var SORT_FILE_DESC = 'sortFilesDesc';
var getSortFileDesc = function () {
return APP.store[SORT_FILE_DESC]+"" === "true";
};
var getSortFolderDesc = function () {
return APP.store[SORT_FOLDER_DESC]+"" === "true";
};
var onSortByClick = function () {
var $span = $(this);
var value;
if ($span.hasClass('cp-app-drive-sort-foldername')) {
value = getSortFolderDesc();
APP.store[SORT_FOLDER_DESC] = value ? false : true;
localStore.put(SORT_FOLDER_DESC, value ? false : true);
refresh();
return;
}
value = APP.store[SORT_FILE_BY];
var descValue = getSortFileDesc();
if ($span.hasClass('cp-app-drive-sort-filename')) {
if (value === '') {
descValue = descValue ? false : true;
} else {
descValue = false;
value = '';
}
} else {
['cp-app-drive-element-type',
'cp-app-drive-element-atime', 'cp-app-drive-element-ctime'].some(function (c) {
if ($span.hasClass(c)) {
var nValue = c.replace(/cp-app-drive-element-/, '');
if (value === nValue) { descValue = descValue ? false : true; }
else {
// atime and ctime should be ordered in a desc order at the first click
value = nValue;
descValue = value !== 'title';
}
return true;
}
});
}
APP.store[SORT_FILE_BY] = value;
APP.store[SORT_FILE_DESC] = descValue;
localStore.put(SORT_FILE_BY, value);
localStore.put(SORT_FILE_DESC, descValue);
refresh();
};
var addFolderSortIcon = function ($list) {
var $icon = $sortAscIcon.clone();
if (getSortFolderDesc()) {
$icon = $sortDescIcon.clone();
}
if (typeof(APP.store[SORT_FOLDER_DESC]) !== "undefined") {
$list.find('.cp-app-drive-sort-foldername').addClass('cp-app-drive-sort-active').prepend($icon);
}
};
var getSortDropdown = function () {
var $fhSort = $(h('span.cp-dropdown-container.cp-app-drive-element-sort.cp-app-drive-sort-clickable'));
var options = [{
tag: 'a',
attributes: {'class': 'cp-app-drive-element-type'},
content: '<i class="fa fa-minus"></i>' + Messages.fm_type
},{
tag: 'a',
attributes: {'class': 'cp-app-drive-element-atime'},
content: '<i class="fa fa-minus"></i>' + Messages.fm_lastAccess
},{
tag: 'a',
attributes: {'class': 'cp-app-drive-element-ctime'},
content: '<i class="fa fa-minus"></i>' + Messages.fm_creation
}];
var dropdownConfig = {
text: '', // Button initial text
options: options, // Entries displayed in the menu
container: $fhSort,
left: true,
noscroll: true,
common: common
};
var $sortBlock = UIElements.createDropdown(dropdownConfig);
$sortBlock.find('button').append(h('span.fa.fa-sort-amount-desc')).append(h('span', Messages.fm_sort));
$sortBlock.on('click', 'a', onSortByClick);
return $fhSort;
};
var getFolderListHeader = function (clickable, small) {
var $fohElement = $('<li>', {
'class': 'cp-app-drive-element-header cp-app-drive-element-list'
});
var clickCls = clickable ? 'cp-app-drive-sort-clickable ' : '';
var onClick = clickable ? onSortByClick : function () {};
//var $fohElement = $('<span>', {'class': 'element'}).appendTo($folderHeader);
var $fhIcon = $('<span>', {'class': 'cp-app-drive-content-icon'});
var $name = $('<span>', {
'class': 'cp-app-drive-element-name cp-app-drive-sort-foldername ' + clickCls
}).text(Messages.fm_folderName).click(onClick);
var $state = $('<span>', {'class': 'cp-app-drive-element-state'});
var $subfolders, $files;
if (!small) {
$subfolders = $('<span>', {
'class': 'cp-app-drive-element-folders cp-app-drive-element-list'
}).text(Messages.fm_numberOfFolders);
$files = $('<span>', {
'class': 'cp-app-drive-element-files cp-app-drive-element-list'
}).text(Messages.fm_numberOfFiles);
}
var $filler = $('<span>', {
'class': 'cp-app-drive-element-filler cp-app-drive-element-list'
});
$fohElement.append($fhIcon).append($name).append($state)
.append($subfolders).append($files).append($filler);
if (clickable) { addFolderSortIcon($fohElement); }
return $fohElement;
};
var addFileSortIcon = function ($list) {
var $icon = $sortAscIcon.clone();
if (getSortFileDesc()) {
$icon = $sortDescIcon.clone();
}
var classSorted;
if (APP.store[SORT_FILE_BY] === '') { classSorted = 'cp-app-drive-sort-filename'; }
else if (APP.store[SORT_FILE_BY]) { classSorted = 'cp-app-drive-element-' + APP.store[SORT_FILE_BY]; }
if (classSorted) {
$list.find('.' + classSorted).addClass('cp-app-drive-sort-active').prepend($icon).find('i').hide();
}
};
var getFileListHeader = function (clickable) {
var $fihElement = $('<li>', {
'class': 'cp-app-drive-element-header cp-app-drive-element-list'
});
var clickCls = clickable ? 'cp-app-drive-sort-clickable ' : '';
var onClick = clickable ? onSortByClick : function () {};
//var $fihElement = $('<span>', {'class': 'element'}).appendTo($fileHeader);
var $fhIcon = $('<span>', {'class': 'cp-app-drive-content-icon'});
var $fhName = $('<span>', {
'class': 'cp-app-drive-element-name cp-app-drive-sort-filename ' + clickCls
}).text(Messages.fm_fileName).click(onClick);
var $fhSort = clickable ? getSortDropdown() : undefined;
var $fhState = $('<span>', {'class': 'cp-app-drive-element-state'});
var $fhType = $('<span>', {
'class': 'cp-app-drive-element-type cp-app-drive-element-list ' + clickCls
}).text(Messages.fm_type).click(onClick);
var $fhAdate = $('<span>', {
'class': 'cp-app-drive-element-atime cp-app-drive-element-list ' + clickCls
}).text(Messages.fm_lastAccess).click(onClick);
var $fhCdate = $('<span>', {
'class': 'cp-app-drive-element-ctime cp-app-drive-element-list ' + clickCls
}).text(Messages.fm_creation).click(onClick);
// If displayTitle is false, it means the "name" is the title, so do not display the "name" header
$fihElement.append($fhIcon).append($fhName).append($fhSort).append($fhState).append($fhType);
$fihElement.append($fhAdate).append($fhCdate);
if (clickable) { addFileSortIcon($fihElement); }
return $fihElement;
};
var sortElements = function (folder, path, oldkeys, prop, asc, useId) {
var root = path && manager.find(path);
if (path[0] === SHARED_FOLDER) {
path = path.slice(1);
root = Util.find(folders[APP.newSharedFolder], path);
}
var test = folder ? manager.isFolder : manager.isFile;
var keys = oldkeys.filter(function (e) {
return useId ? test(e) : (path && test(root[e]));
});
if (keys.length < 2) { return keys; }
var mult = asc ? 1 : -1;
var getProp = function (_el) {
var el = useId ? _el : root[_el];
var sfId = (el && el.root && el.key) ? el.root[el.key] : el;
if (folder && el && manager.isSharedFolder(sfId)) {
var sfData = manager.getSharedFolderData(sfId);
var title = sfData.title || sfData.lastTitle || el;
return String(title).toLowerCase();
} else if (folder) {
return String((el && el.key) || _el).toLowerCase();
}
var data = manager.getFileData(el);
if (!data) { return ''; }
if (prop === 'type') {
var hrefData = Hash.parsePadUrl(data.href || data.roHref);
return hrefData.type;
}
if (prop === 'atime' || prop === 'ctime') {
return typeof(data[prop]) === "number" ? data[prop] : new Date(data[prop]);
}
return (manager.getTitle(el) || "").toLowerCase();
};
var props = {};
keys.forEach(function (k) {
var uid = k;
if (typeof(k) === "object") {
uid = k.uid = Util.uid();
}
props[uid] = getProp(k);
});
keys.sort(function(a, b) {
var _a = props[(a && a.uid) || a];
var _b = props[(b && b.uid) || b];
if (_a < _b) { return mult * -1; }
if (_b < _a) { return mult; }
return 0;
});
return keys;
};
var sortTrashElements = function (folder, oldkeys, prop, asc) {
var test = folder ? manager.isFolder : manager.isFile;
var keys = oldkeys.filter(function (e) {
return test(e.element);
});
if (keys.length < 2) { return keys; }
var mult = asc ? 1 : -1;
var getProp = function (el, prop) {
if (prop && !folder) {
var element = el.element;
var e = manager.getFileData(element);
if (!e) {
e = {
href : el,
title : Messages.fm_noname,
atime : 0,
ctime : 0
};
}
if (prop === 'type') {
var hrefData = Hash.parsePadUrl(e.href || e.roHref);
return hrefData.type;
}
if (prop === 'atime' || prop === 'ctime') {
return new Date(e[prop]);
}
}
return (el.name || "").toLowerCase();
};
keys.sort(function(a, b) {
if (getProp(a, prop) < getProp(b, prop)) { return mult * -1; }
if (getProp(a, prop) > getProp(b, prop)) { return mult * 1; }
return 0;
});
return keys;
};
// Create the ghost icon to add pads/folders
var createNewPadIcons = function ($block, isInRoot) {
var $container = $('<div>');
if (isInRoot) {
// Folder
var $element1 = $('<li>', {
'class': 'cp-app-drive-new-folder cp-app-drive-element-row ' +
'cp-app-drive-element-grid'
}).prepend($folderIcon.clone()).appendTo($container);
$element1.append($('<span>', { 'class': 'cp-app-drive-new-name' })
.text(Messages.fm_folder));
// Shared Folder
if (!APP.disableSF && !manager.isInSharedFolder(currentPath)) {
var $element3 = $('<li>', {
'class': 'cp-app-drive-new-shared-folder cp-app-drive-element-row ' +
'cp-app-drive-element-grid'
}).prepend($sharedFolderIcon.clone()).appendTo($container);
$element3.append($('<span>', { 'class': 'cp-app-drive-new-name' })
.text(Messages.fm_sharedFolder));
}
// Upload file
var $elementFileUpload = $('<li>', {
'class': 'cp-app-drive-new-fileupload cp-app-drive-element-row ' +
'cp-app-drive-element-grid'
}).prepend(getIcon('fileupload')).appendTo($container);
$elementFileUpload.append($('<span>', {'class': 'cp-app-drive-new-name'})
.text(Messages.uploadButton));
// Upload folder
if (APP.allowFolderUpload) {
var $elementFolderUpload = $('<li>', {
'class': 'cp-app-drive-new-folderupload cp-app-drive-element-row ' +
'cp-app-drive-element-grid'
}).prepend(getIcon('folderupload')).appendTo($container);
$elementFolderUpload.append($('<span>', {'class': 'cp-app-drive-new-name'})
.text(Messages.uploadFolderButton));
}
}
// Pads
getNewPadTypes().forEach(function (type) {
var $element = $('<li>', {
'class': 'cp-app-drive-new-doc cp-app-drive-element-row ' +
'cp-app-drive-element-grid'
}).prepend(getIcon(type)).appendTo($container);
$element.append($('<span>', {'class': 'cp-app-drive-new-name'})
.text(Messages.type[type]));
$element.attr('data-type', type);
});
$container.find('.cp-app-drive-element-row').click(function () {
$block.hide();
});
return $container;
};
var createGhostIcon = function ($list) {
if (APP.$content.data('readOnlyFolder') || !APP.editable) { return; }
var isInRoot = currentPath[0] === ROOT;
var $element = $('<li>', {
'class': 'cp-app-drive-element-row cp-app-drive-new-ghost'
}).prepend($addIcon.clone()).appendTo($list);
$element.append($('<span>', {'class': 'cp-app-drive-element-name'})
.text(Messages.fm_newFile));
$element.click(function () {
var modal = UI.createModal({
id: 'cp-app-drive-new-ghost-dialog',
$body: $('body')
});
var $modal = modal.$modal;
var $title = $('<h3>').text(Messages.fm_newFile);
var $description = $('<p>').text(Messages.fm_newButtonTitle);
$modal.find('.cp-modal').append($title);
$modal.find('.cp-modal').append($description);
var $content = createNewPadIcons($modal, isInRoot);
$modal.find('.cp-modal').append($content);
window.setTimeout(function () { modal.show(); });
addNewPadHandlers($modal, isInRoot);
});
};
// Drive content toolbar
var checkCollapseButton = function () {
APP.$collapseButton.removeClass('cp-toolbar-button-active');
if (APP.$tree.is(':visible')) {
APP.$collapseButton.addClass('cp-toolbar-button-active');
}
};
var collapseTreeButton = function () {
APP.$collapseButton = APP.$collapseButton || common.createButton('', true, {
text: Messages.drive_treeButton,
name: 'files',
icon: 'fa-hdd-o',
drawer: false,
});
checkCollapseButton();
APP.toolbar.$bottomL.append(APP.$collapseButton);
APP.$collapseButton.off('click').on('click', function () {
APP.$tree.toggle();
checkCollapseButton();
});
};
var createToolbar = function () {
var $toolbar = APP.toolbar.$bottom;
APP.toolbar.$bottomL.html('');
APP.toolbar.$bottomR.html('');
if (APP.histConfig && (APP.loggedIn || !APP.newSharedFolder)) {
// ANON_SHARED_FOLDER
var $hist = common.createButton('history', true, {histConfig: APP.histConfig});
APP.toolbar.$bottomR.append($hist);
}
if (APP.$burnThisDrive) {
APP.toolbar.$bottomR.append(APP.$burnThisDrive);
}
collapseTreeButton();
return $toolbar;
};
// Unsorted element are represented by "href" in an array: they don't have a filename
// and they don't hav a hierarchical structure (folder/subfolders)
var displayHrefArray = function ($container, rootName, draggable) {
var unsorted = files[rootName];
if (unsorted.length) {
var $fileHeader = getFileListHeader(true);
$container.append($fileHeader);
}
var keys = unsorted;
var sortBy = APP.store[SORT_FILE_BY];
sortBy = sortBy === "" ? sortBy = 'name' : sortBy;
var sortedFiles = sortElements(false, [rootName], keys, sortBy, !getSortFileDesc(), true);
sortedFiles.forEach(function (id) {
var file = manager.getFileData(id);
if (!file) {
//debug("Unsorted or template returns an element not present in filesData: ", href);
file = { title: Messages.fm_noname };
//return;
}
var idx = files[rootName].indexOf(id);
var $icon = getFileIcon(id);
var ro = manager.isReadOnlyFile(id);
// ro undefined mens it's an old hash which doesn't support read-only
var roClass = typeof(ro) === 'undefined' ? ' cp-app-drive-element-noreadonly' :
ro ? ' cp-app-drive-element-readonly' : '';
var $element = $('<li>', {
'class': 'cp-app-drive-element cp-app-drive-element-file cp-app-drive-element-row' + roClass,
draggable: draggable
});
var path = [rootName, idx];
$element.data('path', path);
if (isElementSelected($element)) {
selectElement($element);
}
$element.prepend($icon).dblclick(function () {
openFile(id);
});
addFileData(id, $element);
$element.click(function(e) {
e.stopPropagation();
onElementClick(e, $element);
});
$element.contextmenu(openContextMenu('default'));
$element.data('context', 'default');
if (draggable) {
addDragAndDropHandlers($element, path, false, false);
}
$container.append($element);
});
createGhostIcon($container);
};
var displayAllFiles = function ($container) {
if (AppConfig.disableAnonymousStore && !APP.loggedIn) {
$container.append(Messages.anonymousStoreDisabled);
return;
}
var allfiles = files[FILES_DATA];
if (Object.keys(allfiles || {}).length === 0) {
createGhostIcon($container);
return;
}
var $fileHeader = getFileListHeader(true);
$container.append($fileHeader);
var keys = manager.getFiles([FILES_DATA]);
var sortedFiles = sortElements(false, [FILES_DATA], keys, APP.store[SORT_FILE_BY], !getSortFileDesc(), true);
sortedFiles.forEach(function (id) {
var $icon = getFileIcon(id);
var ro = manager.isReadOnlyFile(id);
// ro undefined maens it's an old hash which doesn't support read-only
var roClass = typeof(ro) === 'undefined' ? ' cp-app-drive-element-noreadonly' :
ro ? ' cp-app-drive-element-readonly' : '';
var $element = $('<li>', {
'class': 'cp-app-drive-element cp-app-drive-element-row' + roClass
});
$element.prepend($icon).dblclick(function () {
openFile(id);
});
addFileData(id, $element);
$element.data('path', [FILES_DATA, id]);
$element.data('element', id);
$element.click(function(e) {
e.stopPropagation();
onElementClick(e, $element);
});
$element.contextmenu(openContextMenu('default'));
$element.data('context', 'default');
$container.append($element);
});
createGhostIcon($container);
};
var displayTrashRoot = function ($list, $folderHeader, $fileHeader) {
var filesList = [];
var root = files[TRASH];
var isEmpty = true;
// Elements in the trash are JS arrays (several elements can have the same name)
Object.keys(root).forEach(function (key) {
if (!Array.isArray(root[key])) {
logError("Trash element has a wrong type", root[key]);
return;
}
root[key].forEach(function (el, idx) {
if (!manager.isFile(el.element) && !manager.isFolder(el.element)) { return; }
var spath = [key, idx, 'element'];
filesList.push({
element: el.element,
spath: spath,
name: key
});
});
isEmpty = false;
});
if (!isEmpty) {
var $empty = createEmptyTrashButton();
$content.append($empty);
}
var sortedFolders = sortTrashElements(true, filesList, null, !getSortFolderDesc());
var sortedFiles = sortTrashElements(false, filesList, APP.store[SORT_FILE_BY], !getSortFileDesc());
if (manager.hasSubfolder(root, true)) { $list.append($folderHeader); }
sortedFolders.forEach(function (f) {
var $element = createElement([TRASH], f.spath, root, true);
$list.append($element);
});
if (manager.hasFile(root, true)) { $list.append($fileHeader); }
sortedFiles.forEach(function (f) {
var $element = createElement([TRASH], f.spath, root, false);
$list.append($element);
});
};
APP.Search = {};
var displaySearch = function ($list, value) {
var search = APP.Search;
var $div = $('<div>', {'id': 'cp-app-drive-search', 'class': 'cp-unselectable'});
$searchIcon.clone().appendTo($div);
var $spinnerContainer = $(h('div.cp-app-drive-search-spinner'));
var spinner = UI.makeSpinner($spinnerContainer);
var $input = APP.Search.$input = $('<input>', {
id: 'cp-app-drive-search-input',
placeholder: Messages.fm_searchName,
type: 'text',
draggable: false,
tabindex: 1,
}).keyup(function (e) {
var lastValue = search.value;
search.value = $input.val().trim();
if (lastValue === search.value) { return; }
if (search.to) { window.clearTimeout(search.to); }
if (search.value === "") {
search.cursor = 0;
APP.displayDirectory([SEARCH]);
return;
}
spinner.spin();
if (e.which === 13) {
var newLocation = [SEARCH, $input.val()];
search.cursor = $input[0].selectionStart;
if (!manager.comparePath(newLocation, currentPath.slice())) {
APP.displayDirectory(newLocation);
}
return;
}
if (e.which === 27) {
$input.val('');
search.cursor = 0;
APP.displayDirectory([SEARCH]);
return;
}
if ($input.val()) {
if (!$input.hasClass('cp-app-drive-search-active')) {
$input.addClass('cp-app-drive-search-active');
}
} else {
$input.removeClass('cp-app-drive-search-active');
}
search.to = window.setTimeout(function () {
var newLocation = [SEARCH, $input.val()];
search.cursor = $input[0].selectionStart;
if (!manager.comparePath(newLocation, currentPath.slice())) {
APP.displayDirectory(newLocation);
}
}, 500);
}).on('click mousedown mouseup', function (e) {
e.stopPropagation();
}).val(value || '').appendTo($div);
if (value) { $input.addClass('cp-app-drive-search-active'); }
$input[0].selectionStart = search.cursor || 0;
$input[0].selectionEnd = search.cursor || 0;
var cancel = h('span.fa.fa-times.cp-app-drive-search-cancel', {title:Messages.cancel});
cancel.addEventListener('click', function () {
$input.val('');
search.cursor = 0;
APP.displayDirectory([SEARCH]);
});
$div.append(cancel);
$list.append($div);
$spinnerContainer.appendTo($list);
setTimeout(function () {
$input.focus();
});
if (typeof(value) === "string" && value.trim()) {
spinner.spin();
} else {
return;
}
setTimeout(function () {
//$list.closest('#cp-app-drive-content-folder').addClass('cp-app-drive-content-list');
var filesList = manager.search(value);
if (!filesList.length) {
$list.append(h('div.cp-app-drive-search-noresult', Messages.fm_noResult));
spinner.hide();
return;
}
var sortable = {};
var sortableFolders = [];
filesList.forEach(function (r) {
// if r.id === null, then it's a folder, not a file
r.paths.forEach(function (path) {
if (!r.inSharedFolder &&
APP.hideDuplicateOwned && manager.isDuplicateOwned(path)) { return; }
var _path = path.slice();
var key = path.pop();
var root = manager.find(path);
var obj = {
path: path,
_path: _path,
key: key,
root: root,
data: r.data
};
if (manager.isFolder(root[key])) {
sortableFolders.push(obj);
return;
}
sortable[root[key]] = obj;
});
});
var _folders = sortElements(true, [ROOT], sortableFolders, null, !getSortFolderDesc(), true);
var sortableKeys = Object.keys(sortable).map(Number);
var _files = sortElements(false, [ROOT], sortableKeys, APP.store[SORT_FILE_BY], !getSortFileDesc(), true);
var addEl = function (obj, folder) {
var $element = createElement(obj.path, obj.key, obj.root, folder);
$element.addClass('cp-app-drive-element-notrash cp-app-drive-search-result');
$element.off('contextmenu');
$element.contextmenu(openContextMenu('default'));
$element.data('context', 'default');
if (folder) {
$element.find('.cp-app-drive-element-list').css({
visibility: 'hidden'
}).text('');
}
if (manager.isPathIn(obj._path, ['hrefArray'])) {
obj._path.pop();
obj._path.push(obj.data.title);
}
var $path = $('<span>', {
'class': 'cp-app-drive-search-path'
}).appendTo($element.find('.cp-app-drive-element-name'));
createTitle($path, obj._path);
$list.append($element);
};
if (_folders.length) { getFolderListHeader(true, true).appendTo($list); }
_folders.forEach(function (el) {
var obj = el;
addEl(obj, true);
});
if (_files.length) { getFileListHeader(true).appendTo($list); }
_files.forEach(function (el) {
var obj = sortable[el];
addEl(obj, false);
});
setTimeout(collapseDrivePath);
spinner.hide();
});
};
var displayRecent = function ($list) {
var filesList = manager.getRecentPads();
var limit = 20;
var now = new Date();
var last1 = new Date(now);
last1.setDate(last1.getDate()-1);
var last7 = new Date(now);
last7.setDate(last7.getDate()-7);
var last28 = new Date(now);
last28.setDate(last28.getDate()-28);
var header7, header28, headerOld;
var i = 0;
var channels = [];
$list.append(h('li.cp-app-drive-element-separator', h('span', Messages.drive_active1Day)));
filesList.some(function (arr) {
var id = arr[0];
var file = arr[1];
if (!file || !file.atime) { return; }
if (file.atime <= last28 && i >= limit) {
return true;
}
var paths = manager.findFile(id);
if (!paths.length) { return; }
var path = paths[0];
if (manager.isPathIn(path, [TRASH])) { return; }
if (channels.indexOf(file.channel) !== -1) { return; }
channels.push(file.channel);
if (!header7 && file.atime < last1) {
$list.append(h('li.cp-app-drive-element-separator', h('span', Messages.drive_active7Days)));
header7 = true;
}
if (!header28 && file.atime < last7) {
$list.append(h('li.cp-app-drive-element-separator', h('span', Messages.drive_active28Days)));
header28 = true;
}
if (!headerOld && file.atime < last28) {
$list.append(h('li.cp-app-drive-element-separator', h('span', Messages.drive_activeOld)));
headerOld = true;
}
// Display the pad
/*
var $icon = getFileIcon(id);
var ro = manager.isReadOnlyFile(id);
// ro undefined means it's an old hash which doesn't support read-only
var roClass = typeof(ro) === 'undefined' ? ' cp-app-drive-element-noreadonly' :
ro ? ' cp-app-drive-element-readonly' : '';
var $element = $('<li>', {
'class': 'cp-app-drive-element cp-app-drive-element-notrash cp-app-drive-element-file cp-app-drive-element-row' + roClass,
});*/
var parentPath = path.slice();
var key = parentPath.pop();
var root = manager.find(parentPath);
var $element = createElement(parentPath, key, root);
$element.off('contextmenu').contextmenu(openContextMenu('default'));
$element.data('context', 'default');
$list.append($element);
i++;
});
};
// Owned pads category
var displayOwned = function ($container) {
var list = manager.getOwnedPads();
if (list.length === 0) { return; }
var $fileHeader = getFileListHeader(true);
$container.append($fileHeader);
var sortedFiles = sortElements(false, false, list, APP.store[SORT_FILE_BY], !getSortFileDesc(), true);
sortedFiles.forEach(function (id) {
var paths = manager.findFile(id);
if (!paths.length) { return; }
var path = paths[0];
var $icon = getFileIcon(id);
var ro = manager.isReadOnlyFile(id);
// ro undefined maens it's an old hash which doesn't support read-only
var roClass = typeof(ro) === 'undefined' ? ' cp-app-drive-element-noreadonly' :
ro ? ' cp-app-drive-element-readonly' : '';
var $element = $('<li>', {
'class': 'cp-app-drive-element cp-app-drive-element-notrash ' +
'cp-app-drive-element-file cp-app-drive-element-row' + roClass
});
$element.prepend($icon).dblclick(function () {
openFile(id);
});
addFileData(id, $element);
$element.data('path', path);
$element.data('element', id);
$element.click(function(e) {
e.stopPropagation();
onElementClick(e, $element);
});
$element.contextmenu(openContextMenu('default'));
$element.data('context', 'default');
$container.append($element);
});
};
// Tags category
var displayTags = function ($container) {
var list = manager.getTagsList();
if (Object.keys(list).length === 0) { return; }
var sortedTags = Object.keys(list);
sortedTags.sort(function (a, b) {
return list[b] - list[a];
});
var lines = [
h('tr', [
h('th', Messages.fm_tags_name),
h('th', Messages.fm_tags_used)
])
];
sortedTags.forEach(function (tag) {
var tagLink = h('a', { href: '#' }, '#' + tag);
$(tagLink).click(function () {
if (displayedCategories.indexOf(SEARCH) !== -1) {
APP.displayDirectory([SEARCH, '#' + tag]);
}
});
lines.push(h('tr', [
h('td', tagLink),
h('td.cp-app-drive-tags-used', list[tag])
]));
});
$(h('li.cp-app-drive-tags-list', h('table', lines))).appendTo($container);
};
// ANON_SHARED_FOLDER
// Display a shared folder for anon users (read-only)
var displaySharedFolder = function ($list) {
if (currentPath.length === 1) {
currentPath.push(ROOT);
}
var fId = APP.newSharedFolder;
var data = folders[fId];
var $folderHeader = getFolderListHeader(true);
var $fileHeader = getFileListHeader(true);
var path = currentPath.slice(1);
var root = Util.find(data, path);
var realPath = [ROOT, SHARED_FOLDER].concat(path);
if (manager.hasSubfolder(root)) { $list.append($folderHeader); }
// display sub directories
var keys = Object.keys(root);
var sortedFolders = sortElements(true, realPath, keys, null, !getSortFolderDesc());
var sortedFiles = sortElements(false, realPath, keys, APP.store[SORT_FILE_BY], !getSortFileDesc());
sortedFolders.forEach(function (key) {
if (manager.isFile(root[key])) { return; }
var $element = createElement(realPath, key, root, true);
$element.appendTo($list);
});
if (manager.hasFile(root)) { $list.append($fileHeader); }
// display files
sortedFiles.forEach(function (key) {
if (manager.isFolder(root[key])) { return; }
var $element = createElement(realPath, key, root, false);
if (!$element) { return; }
$element.appendTo($list);
});
};
// Display the selected directory into the content part (rightside)
// NOTE: Elements in the trash are not using the same storage structure as the others
var _displayDirectory = function (path, force) {
if (APP.closed || (APP.$content && !$.contains(document.documentElement, APP.$content[0]))) { return; }
APP.hideMenu();
if (!APP.editable) { debug("Read-only mode"); }
if (!appStatus.isReady && !force) { return; }
// Fix path obvious issues
if (!path || path.length === 0) {
// Only Trash and Root are available in not-owned files manager
if (!path || displayedCategories.indexOf(path[0]) === -1) {
log(Messages.fm_categoryError);
}
if (!APP.loggedIn && APP.newSharedFolder) {
// ANON_SHARED_FOLDER
path = [SHARED_FOLDER, ROOT];
} else {
path = [ROOT];
}
}
if (APP.loggedIn && path[0] === FILES_DATA) {
path = [ROOT];
}
// Get path data
appStatus.ready(false);
currentPath = path;
var s = $content.scrollTop() || 0;
$content.html("");
sel.$selectBox = $('<div>', {'class': 'cp-app-drive-content-select-box'})
.appendTo($content);
var isInRoot = manager.isPathIn(path, [ROOT]);
var inTrash = manager.isPathIn(path, [TRASH]);
var isTrashRoot = manager.comparePath(path, [TRASH]);
var isTemplate = manager.comparePath(path, [TEMPLATE]);
var isAllFiles = manager.comparePath(path, [FILES_DATA]);
var isVirtual = virtualCategories.indexOf(path[0]) !== -1;
var isSearch = path[0] === SEARCH;
var isTags = path[0] === TAGS;
// ANON_SHARED_FOLDER
var isSharedFolder = path[0] === SHARED_FOLDER && APP.newSharedFolder;
if (isSharedFolder && path.length < 2) {
path = [SHARED_FOLDER, 'root'];
currentPath = path;
}
// Make sure the path is valid
var root = isVirtual ? undefined : manager.find(path);
if (manager.isSharedFolder(root)) {
// ANON_SHARED_FOLDER
path.push(manager.user.userObject.ROOT);
root = manager.find(path);
if (!root) { return; }
}
if (!isVirtual && typeof(root) === "undefined") {
log(Messages.fm_unknownFolderError);
debug("Unable to locate the selected directory: ", path);
if (path.length === 1 && path[0] === ROOT) {
// Somehow we can't display ROOT. We should abort now because we'll
// end up in an infinite loop
return void UI.warn(Messages.fm_error_cantPin); // Internal server error, please reload...
}
var parentPath = path.slice();
parentPath.pop();
_displayDirectory(parentPath, true);
return;
}
if (!isSearch) { delete APP.Search.oldLocation; }
// Display the tree and build the content
APP.resetTree();
LS.setLastOpenedFolder(path);
createToolbar(path);
if (!isSearch) { createTitle($content, path); }
var $info = createInfoBox(path);
var $dirContent = $('<div>', {id: FOLDER_CONTENT_ID});
$dirContent.data('path', path);
if (!isTags) {
$dirContent.addClass(getViewModeClass(isSearch));
if (!isSearch) {
createViewModeButton(APP.toolbar.$bottomR);
}
}
var $list = $('<ul>').appendTo($dirContent);
var sfId = manager.isInSharedFolder(currentPath);
// Restricted folder? display ROOT instead
if (sfId && files.restrictedFolders[sfId]) {
_displayDirectory([ROOT], true);
return;
}
var readOnlyFolder = false;
// If the shared folder is offline, add the "DISCONNECTED" banner, otherwise
// use the normal "editable" behavior (based on drive offline or history mode)
if (sfId && manager.folders[sfId].offline) {
setEditable(false, false, true);
} else {
setEditable(true, false, true);
}
if (APP.readOnly) {
// Read-only drive (team?)
$content.prepend($readOnly.clone());
} else if (sfId && folders[sfId] && folders[sfId].readOnly) {
// If readonly shared folder...
$content.prepend($readOnly.clone());
readOnlyFolder = true;
}
$content.data('readOnlyFolder', readOnlyFolder);
if (!readOnlyFolder) {
createNewButton(isInRoot, APP.toolbar.$bottomL);
}
if (APP.mobile()) {
var $context = $('<button>', {
id: 'cp-app-drive-toolbar-context-mobile'
});
$context.append($('<span>', {'class': 'fa fa-caret-down'}));
$context.appendTo(APP.toolbar.$bottomR);
$context.click(function (e) {
e.preventDefault();
e.stopPropagation();
var $li = findSelectedElements();
if ($li.length !== 1) {
$li = findDataHolder($tree.find('.cp-toolbar-button-active'));
}
// Close if already opened
if ($('.cp-contextmenu:visible').length) {
APP.hideMenu();
return;
}
if (!$li.length) {
return void $dirContent.contextmenu();
}
// Open the menu
$li.contextmenu();
});
} else {
var $contextButtons = $('<span>', {'id' : 'cp-app-drive-toolbar-contextbuttons'});
$contextButtons.appendTo(APP.toolbar.$bottomR);
}
updateContextButton();
var $folderHeader = getFolderListHeader(true);
var $fileHeader = getFileListHeader(true);
if (isTemplate) {
displayHrefArray($list, path[0], true);
} else if (isAllFiles) {
displayAllFiles($list);
} else if (isTrashRoot) {
displayTrashRoot($list, $folderHeader, $fileHeader);
} else if (isSearch) {
displaySearch($list, path[1]);
} else if (path[0] === RECENT) {
displayRecent($list);
} else if (path[0] === OWNED) {
displayOwned($list);
} else if (isTags) {
displayTags($list);
} else if (isSharedFolder) {
// ANON_SHARED_FOLDER
displaySharedFolder($list);
} else {
if (!inTrash) { $dirContent.contextmenu(openContextMenu('content')); }
if (manager.hasSubfolder(root)) { $list.append($folderHeader); }
// display sub directories
var keys = Object.keys(root);
var sortedFolders = sortElements(true, path, keys, null, !getSortFolderDesc());
var sortedFiles = sortElements(false, path, keys, APP.store[SORT_FILE_BY], !getSortFileDesc());
sortedFolders.forEach(function (key) {
if (manager.isFile(root[key])) { return; }
var $element = createElement(path, key, root, true);
$element.appendTo($list);
});
if (manager.hasFile(root)) { $list.append($fileHeader); }
// display files
sortedFiles.forEach(function (key) {
if (manager.isFolder(root[key])) { return; }
var p = path.slice();
p.push(key);
if (APP.hideDuplicateOwned && manager.isDuplicateOwned(p)) { return; }
var $element = createElement(path, key, root, false);
if (!$element) { return; }
$element.appendTo($list);
});
if (!inTrash) { createGhostIcon($list); }
}
$content.append($info).append($dirContent);
/*var $truncated = $('<span>', {'class': 'cp-app-drive-element-truncated'}).text('...');
$content.find('.cp-app-drive-element').each(function (idx, el) {
var $name = $(el).find('.cp-app-drive-element-name');
if ($name.length === 0) { return; }
if ($name[0].scrollHeight > $name[0].clientHeight) {
var $tr = $truncated.clone();
$tr.attr('title', $name.text());
$(el).append($tr);
}
});*/
// If the selected element is not visible, scroll to make it visible, otherwise scroll to
// the previous scroll position
var $sel = findSelectedElements();
if ($sel.length) {
var _top = $sel[0].getBoundingClientRect().top;
var _topContent = $content[0].getBoundingClientRect().top;
if ((_topContent + s + $content.height() - 20) < _top) {
$sel[0].scrollIntoView();
} else {
$content.scrollTop(s);
}
} else {
$content.scrollTop(s);
}
delete APP.convertedFolder;
appStatus.ready(true);
};
var displayDirectory = APP.displayDirectory = function (path, force) {
if (APP.closed || (APP.$content && !$.contains(document.documentElement, APP.$content[0]))) { return; }
if (history.isHistoryMode) {
return void _displayDirectory(path, force);
}
if (!manager.comparePath(currentPath, path)) {
removeSelected();
}
updateObject(sframeChan, proxy, function () {
copyObjectValue(files, proxy.drive);
updateSharedFolders(sframeChan, manager, files, folders, function () {
_displayDirectory(path, force);
});
});
};
var createTreeElement = function (name, $icon, path, draggable, droppable, collapsable, active, isSharedFolder) {
var $name = $('<span>', { 'class': 'cp-app-drive-element' }).text(name);
$icon.css("color", isSharedFolder ? getFolderColor(path.slice(0, -1)) : getFolderColor(path));
var $collapse;
if (collapsable) {
$collapse = $expandIcon.clone();
}
var $elementRow = $('<span>', {'class': 'cp-app-drive-element-row'}).append($collapse).append($icon).append($name).click(function (e) {
e.stopPropagation();
if (isSharedFolder && !manager.folders[isSharedFolder]) {
UI.warn(Messages.fm_deletedFolder);
return;
}
if (files.restrictedFolders[isSharedFolder]) {
UI.warn(Messages.fm_restricted);
return;
}
APP.displayDirectory(path);
});
if (files.restrictedFolders[isSharedFolder]) {
$elementRow.addClass('cp-app-drive-element-restricted');
}
if (isSharedFolder) {
var sfData = manager.getSharedFolderData(isSharedFolder);
_addOwnership($elementRow, $(), sfData);
}
var $element = $('<li>').append($elementRow);
if (draggable) { $elementRow.attr('draggable', true); }
if (collapsable) {
$element.addClass('cp-app-drive-element-collapsed');
$collapse.click(function(e) {
e.stopPropagation();
if ($element.hasClass('cp-app-drive-element-collapsed')) {
// It is closed, open it
$element.removeClass('cp-app-drive-element-collapsed');
LS.setFolderOpened(path, true);
$collapse.removeClass('fa-plus-square-o');
$collapse.addClass('fa-minus-square-o');
} else {
// Collapse the folder
$element.addClass('cp-app-drive-element-collapsed');
LS.setFolderOpened(path, false);
$collapse.removeClass('fa-minus-square-o');
$collapse.addClass('fa-plus-square-o');
// Change the current opened folder if it was collapsed
if (manager.isSubpath(currentPath, path)) {
displayDirectory(path);
}
}
});
if (LS.wasFolderOpened(path) ||
(manager.isSubpath(currentPath, path) && path.length < currentPath.length)) {
$collapse.click();
}
}
var dataPath = isSharedFolder ? path.slice(0, -1) : path;
$elementRow.data('path', dataPath);
addDragAndDropHandlers($elementRow, dataPath, true, droppable);
if (active) {
$elementRow.addClass('cp-app-drive-element-active cp-leftside-active');
}
return $element;
};
var createTree = function ($container, path) {
var root = manager.find(path);
// don't try to display what doesn't exist
if (!root) { return; }
// Display the root element in the tree
var displayingRoot = manager.comparePath([ROOT], path);
if (displayingRoot) {
var isRootOpened = manager.comparePath([ROOT], currentPath);
var $rootIcon = manager.isFolderEmpty(files[ROOT]) ?
(isRootOpened ? $folderOpenedEmptyIcon : $folderEmptyIcon) :
(isRootOpened ? $folderOpenedIcon : $folderIcon);
var $rootElement = createTreeElement(ROOT_NAME, $rootIcon.clone(), [ROOT], false, true, true, isRootOpened);
if (!manager.hasSubfolder(root)) {
$rootElement.find('.cp-app-drive-icon-expcol').css('visibility', 'hidden');
}
$rootElement.addClass('cp-app-drive-tree-root');
$rootElement.find('>.cp-app-drive-element-row')
.contextmenu(openContextMenu('tree'));
$('<ul>', {'class': 'cp-app-drive-tree-docs'})
.append($rootElement).appendTo($container);
$container = $rootElement;
} else if (manager.isFolderEmpty(root)) { return; }
// Display root content
var $list = $('<ul>').appendTo($container);
var keys = Object.keys(root).sort(function (a, b) {
var newA = manager.isSharedFolder(root[a]) ?
manager.getSharedFolderData(root[a]).title : a;
var newB = manager.isSharedFolder(root[b]) ?
manager.getSharedFolderData(root[b]).title : b;
return newA < newB ? -1 :
(newA === newB ? 0 : 1);
});
keys.forEach(function (key) {
// Do not display files in the menu
if (!manager.isFolder(root[key])) { return; }
var newPath = path.slice();
newPath.push(key);
var isSharedFolder = manager.isSharedFolder(root[key]) && root[key];
var sfId = manager.isInSharedFolder(newPath) || (isSharedFolder && root[key]);
var $icon, isCurrentFolder, subfolder;
if (isSharedFolder) {
// Fix path
newPath.push(manager.user.userObject.ROOT);
isCurrentFolder = manager.comparePath(newPath, currentPath);
// Subfolders?
var newRoot = Util.find(manager, ['folders', sfId, 'proxy', manager.user.userObject.ROOT]) || {};
subfolder = manager.hasSubfolder(newRoot);
// Fix name
key = manager.getSharedFolderData(sfId).title || Messages.fm_deletedFolder;
// Fix icon
$icon = isCurrentFolder ? $sharedFolderOpenedIcon : $sharedFolderIcon;
isSharedFolder = sfId;
} else {
var isEmpty = manager.isFolderEmpty(root[key]);
subfolder = manager.hasSubfolder(root[key]);
isCurrentFolder = manager.comparePath(newPath, currentPath);
$icon = isEmpty ?
(isCurrentFolder ? $folderOpenedEmptyIcon : $folderEmptyIcon) :
(isCurrentFolder ? $folderOpenedIcon : $folderIcon);
}
var f = folders[sfId];
var editable = !(f && f.readOnly);
var $element = createTreeElement(key, $icon.clone(), newPath, true, editable,
subfolder, isCurrentFolder, isSharedFolder);
$element.appendTo($list);
$element.find('>.cp-app-drive-element-row').contextmenu(openContextMenu('tree'));
if (isSharedFolder) {
$element.find('>.cp-app-drive-element-row')
.addClass('cp-app-drive-element-sharedf');
}
if (sfId && !editable) {
$element.attr('data-ro', true);
}
if (!subfolder) { return; }
createTree($element, newPath);
});
};
var createTrash = function ($container, path) {
var $icon = manager.isFolderEmpty(files[TRASH]) ? $trashEmptyIcon.clone() : $trashIcon.clone();
var isOpened = manager.comparePath(path, currentPath);
var $trashElement = createTreeElement(TRASH_NAME, $icon, [TRASH], false, true, false, isOpened);
$trashElement.addClass('cp-app-drive-tree-root');
$trashElement.find('>.cp-app-drive-element-row')
.contextmenu(openContextMenu('trashtree'));
var $trashList = $('<ul>', { 'class': 'cp-app-drive-tree-category' })
.append($trashElement);
$container.append($trashList);
};
var categories = {};
categories[FILES_DATA] = {
name: FILES_DATA_NAME,
$icon: $unsortedIcon
};
categories[TEMPLATE] = {
name: TEMPLATE_NAME,
droppable: true,
$icon: $templateIcon
};
categories[RECENT] = {
name: RECENT_NAME,
$icon: $recentIcon
};
categories[OWNED] = {
name: OWNED_NAME,
$icon: $ownedIcon
};
categories[SEARCH] = {
name: Messages.fm_searchPlaceholder,
$icon: $searchIcon
};
categories[TAGS] = {
name: TAGS_NAME,
$icon: $tagsIcon
};
var createCategory = function ($container, cat) {
var options = categories[cat];
var $icon = options.$icon.clone();
var isOpened = manager.comparePath([cat], currentPath);
var $element = createTreeElement(options.name, $icon, [cat], options.draggable, options.droppable, false, isOpened);
$element.addClass('cp-app-drive-tree-root');
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
$container.append($list);
};
APP.resetTree = function () {
var $categories = $tree.find('.cp-app-drive-tree-categories-container');
var s = $categories.scrollTop() || 0;
$tree.html('');
/*
$(h('button.fa.fa-times.cp-close-button', {
title: Messages.filePicker_close
})).click(function (e) {
e.stopPropagation();
$tree.hide();
checkCollapseButton();
}).appendTo($tree);
*/
var $div = $('<div>', {'class': 'cp-app-drive-tree-categories-container'})
.appendTo($tree);
if (displayedCategories.indexOf(SEARCH) !== -1) { createCategory($div, SEARCH); }
if (displayedCategories.indexOf(TAGS) !== -1) { createCategory($div, TAGS); }
if (displayedCategories.indexOf(RECENT) !== -1) { createCategory($div, RECENT); }
if (displayedCategories.indexOf(OWNED) !== -1) { createCategory($div, OWNED); }
if (displayedCategories.indexOf(ROOT) !== -1) { createTree($div, [ROOT]); }
if (displayedCategories.indexOf(TEMPLATE) !== -1) { createCategory($div, TEMPLATE); }
if (displayedCategories.indexOf(FILES_DATA) !== -1) { createCategory($div, FILES_DATA); }
if (displayedCategories.indexOf(TRASH) !== -1) { createTrash($div, [TRASH]); }
$tree.append(APP.$limit);
$categories = $tree.find('.cp-app-drive-tree-categories-container');
$categories.scrollTop(s);
};
APP.hideMenu = function (e) {
$contextMenu.hide();
$trashTreeContextMenu.hide();
$trashContextMenu.hide();
$contentContextMenu.hide();
$defaultContextMenu.hide();
if (!e || !$(e.target).parents('.cp-dropdown')) {
$('.cp-dropdown-content').hide();
}
};
var stringifyPath = function (path) {
if (!Array.isArray(path)) { return; }
var $div = $('<div>');
var i = 0;
var space = 10;
path.forEach(function (s) {
if (i === 0) { s = getPrettyName(s); }
$div.append($('<span>', {'style': 'margin: 0 0 0 ' + i * space + 'px;'}).text(s));
$div.append($('<br>'));
i++;
});
return $div.html();
};
// Disable middle click in the context menu to avoid opening /drive/inner.html# in new tabs
var onWindowClick = function (e) {
if (!e.target || !$(e.target).parents('.cp-dropdown-content').length) { return; }
if (e.which !== 1) {
e.stopPropagation();
return false;
}
};
APP.getProperties = function (el, cb) {
if (!manager.isFile(el) && !manager.isSharedFolder(el)) {
return void cb('NOT_FILE');
}
//var ro = manager.isReadOnlyFile(el);
var data;
if (manager.isSharedFolder(el)) {
data = JSON.parse(JSON.stringify(manager.getSharedFolderData(el)));
} else {
data = JSON.parse(JSON.stringify(manager.getFileData(el)));
}
if (!data || !(data.href || data.roHref)) { return void cb('INVALID_FILE'); }
var opts = {};
opts.href = Hash.getRelativeHref(data.href || data.roHref);
opts.channel = data.channel;
if (manager.isSharedFolder(el)) {
var ro = folders[el] && folders[el].version >= 2;
if (!ro) { opts.noReadOnly = true; }
}
Properties.getPropertiesModal(common, opts, cb);
};
APP.getAccess = function (el, cb) {
if (!manager.isFile(el) && !manager.isSharedFolder(el)) {
return void cb('NOT_FILE');
}
var data;
if (manager.isSharedFolder(el)) {
data = JSON.parse(JSON.stringify(manager.getSharedFolderData(el)));
} else {
data = JSON.parse(JSON.stringify(manager.getFileData(el)));
}
if (!data || !(data.href || data.roHref)) { return void cb('INVALID_FILE'); }
var opts = {};
opts.href = Hash.getRelativeHref(data.href || data.roHref);
opts.channel = data.channel;
// Transfer ownership: templates are stored as templates for other users/teams
if (currentPath[0] === TEMPLATE) {
opts.isTemplate = true;
}
// Shared folders: no expiration date
if (manager.isSharedFolder(el)) {
opts.noExpiration = true;
}
Access.getAccessModal(common, opts, cb);
};
if (!APP.loggedIn) {
$contextMenu.find('.cp-app-drive-context-delete').attr('data-icon', faDelete)
.html($contextMenu.find('.cp-app-drive-context-remove').html());
}
var deleteOwnedPaths = function (paths, pathsList) {
pathsList = pathsList || [];
if (paths) {
paths.forEach(function (p) { pathsList.push(p.path); });
}
var msgD = pathsList.length === 1 ? Messages.fm_deleteOwnedPad :
Messages.fm_deleteOwnedPads;
UI.confirm(msgD, function(res) {
$(window).focus();
if (!res) { return; }
manager.deleteOwned(pathsList, function () {
pathsList.forEach(LS.removeFoldersOpened);
removeSelected();
refresh();
});
});
};
var downloadFolder = function (folderElement, folderName, sfId) {
var todo = function (data) {
data.folder = folderElement;
data.sharedFolderId = sfId;
data.name = Util.fixFileName(folderName);
data.folderName = Util.fixFileName(folderName) + '.zip';
var uo = manager.user.userObject;
if (sfId && manager.folders[sfId]) {
uo = manager.folders[sfId].userObject;
}
if (uo.getFilesRecursively) {
data.list = uo.getFilesRecursively(folderElement).map(function (el) {
var d = uo.getFileData(el);
return d.channel;
});
}
APP.FM.downloadFolder(data, function (err, obj) {
console.log(err, obj);
console.log('DONE');
});
};
todo({
uo: proxy,
sf: folders,
});
};
$contextMenu.on("click", "a", function(e) {
e.stopPropagation();
var paths = $contextMenu.data('paths');
var pathsList = [];
var type = $contextMenu.attr('data-menu-type');
var $this = $(this);
var el, data;
if (paths.length === 0) {
log(Messages.fm_forbidden);
debug("Context menu on a forbidden or unexisting element. ", paths);
return;
}
if ($this.hasClass("cp-app-drive-context-rename")) {
if (paths.length !== 1) { return; }
displayRenameInput(paths[0].element, paths[0].path);
}
else if ($this.hasClass("cp-app-drive-context-color")) {
var currentColor = getFolderColor(paths[0].path);
pickFolderColor(paths[0].element, currentColor, function (color) {
paths.forEach(function (p) {
setFolderColor(p.element, p.path, color);
});
refresh();
});
}
else if($this.hasClass("cp-app-drive-context-delete")) {
if (!APP.loggedIn) {
return void deletePaths(paths);
}
paths.forEach(function (p) { pathsList.push(p.path); });
moveElements(pathsList, [TRASH], false, refresh);
}
else if ($this.hasClass('cp-app-drive-context-deleteowned')) {
deleteOwnedPaths(paths);
}
else if ($this.hasClass('cp-app-drive-context-preview')) {
if (paths.length !== 1) { return; }
el = manager.find(paths[0].path);
openFile(el, null, false);
}
else if ($this.hasClass('cp-app-drive-context-open')) {
paths.forEach(function (p) {
var el = manager.find(p.path);
if (files.restrictedFolders[el]) {
UI.warn(Messages.fm_restricted);
return;
}
openFile(el, false, true);
});
}
else if ($this.hasClass('cp-app-drive-context-openro')) {
paths.forEach(function (p) {
var el = manager.find(p.path);
if (paths[0].path[0] === SHARED_FOLDER && APP.newSharedFolder) {
// ANON_SHARED_FOLDER
el = manager.find(paths[0].path.slice(1), APP.newSharedFolder);
}
if (manager.isPathIn(p.path, [FILES_DATA])) {
el = p.path[1];
} else {
if (!el || manager.isFolder(el)) { return; }
}
openFile(el, true, true);
});
}
else if ($this.hasClass('cp-app-drive-context-makeacopy')) {
if (paths.length !== 1) { return; }
el = manager.find(paths[0].path);
(function () {
var path = currentPath;
if (path[0] !== ROOT) { path = [ROOT]; }
var _metadata = manager.getFileData(el);
var _simpleData = {
title: _metadata.filename || _metadata.title,
href: _metadata.href || _metadata.roHref,
password: _metadata.password,
channel: _metadata.channel,
};
var parsed = Hash.parsePadUrl(_metadata.href || _metadata.roHref);
openIn(parsed.type, path, APP.team, _simpleData);
})();
}
else if ($this.hasClass('cp-app-drive-context-openincode')) {
if (paths.length !== 1) { return; }
var p = paths[0];
el = manager.find(p.path);
(function () {
var path = currentPath;
if (path[0] !== ROOT) { path = [ROOT]; }
var _metadata = manager.getFileData(el);
var _simpleData = {
title: _metadata.filename || _metadata.title,
href: _metadata.href || _metadata.roHref,
password: _metadata.password,
channel: _metadata.channel,
};
openIn('code', path, APP.team, _simpleData);
})();
}
else if ($this.hasClass('cp-app-drive-context-expandall') ||
$this.hasClass('cp-app-drive-context-collapseall')) {
if (paths.length !== 1) { return; }
var opened = $this.hasClass('cp-app-drive-context-expandall');
var openRecursive = function (path) {
LS.setFolderOpened(path, opened);
var folderContent = manager.find(path);
var subfolders = [];
for (var k in folderContent) {
if (manager.isFolder(folderContent[k])) {
if (manager.isSharedFolder(folderContent[k])) {
subfolders.push([k].concat(manager.user.userObject.ROOT));
}
else {
subfolders.push(k);
}
}
}
subfolders.forEach(function (p) {
var subPath = path.concat(p);
openRecursive(subPath);
});
};
openRecursive(paths[0].path);
refresh();
}
else if ($this.hasClass('cp-app-drive-context-download')) {
if (paths.length !== 1) { return; }
var path = paths[0];
el = manager.find(path.path);
// folder
if (manager.isFolder(el)) {
// folder
var name, folderEl;
if (!manager.isSharedFolder(el)) {
name = path.path[path.path.length - 1];
folderEl = el;
var sfId = manager.isInSharedFolder(path.path);
downloadFolder(folderEl, name, sfId);
}
// shared folder
else {
data = manager.getSharedFolderData(el);
name = data.title;
folderEl = manager.find(path.path.concat("root"));
downloadFolder(folderEl, name, el);
}
}
// file
else if (manager.isFile(el)) {
// imported file
if (path.element.is(".cp-border-color-file")) {
data = manager.getFileData(el);
APP.FM.downloadFile(data, function (err, obj) {
console.log(err, obj);
console.log('DONE');
});
}
// pad
else {
data = manager.getFileData(el);
APP.FM.downloadPad(data, function (err, obj) {
console.log(err, obj);
console.log('DONE');
});
}
}
}
else if ($this.hasClass('cp-app-drive-context-share')) {
if (paths.length !== 1) { return; }
el = manager.find(paths[0].path);
var parsed;
var friends = common.getFriends();
var anonDrive = manager.isPathIn(currentPath, [FILES_DATA]) && !APP.loggedIn;
if (manager.isFolder(el) && !manager.isSharedFolder(el) && !anonDrive) { // Folder
// if folder is inside SF
if (manager.isInSharedFolder(paths[0].path)) {
return void UI.alert(Messages.convertFolderToSF_SFParent);
}
// if folder already contains SF
else if (manager.hasSubSharedFolder(el)) {
return void UI.alert(Messages.convertFolderToSF_SFChildren);
}
// if root
else if (paths[0].path.length <= 1) {
return void UI.warn(Messages.error);
}
// if folder does not contains SF
// XXX rewrite "creation_owned1": "An <b>owned</b> pad can be deleted from the server whenever the owner wants. Deleting an owned pad removes it from other users' CryptDrives."
// tipy used in dialog to create a shared folder
else {
var convertContent = h('div', [
h('p', Messages.convertFolderToSF_confirm),
h('label', {for: 'cp-upload-password'}, Messages.fm_shareFolderPassword),
UI.passwordInput({
id: 'cp-upload-password',
placeholder: Messages.creation_passwordValue
}),
h('span', {
style: 'display:flex;align-items:center;justify-content:space-between'
}, [
UI.createCheckbox('cp-upload-owned', Messages.sharedFolders_create_owned, true),
UI.createHelper('https://docs.cryptpad.fr/en/user_guide/share_and_access.html#owners', Messages.creation_owned1)
]),
]);
return void UI.confirm(convertContent, function(res) {
if (!res) { return; }
var password = $(convertContent).find('#cp-upload-password').val() || undefined;
var owned = Util.isChecked($(convertContent).find('#cp-upload-owned'));
manager.convertFolderToSharedFolder(paths[0].path, owned, password, function (err, obj) {
if (err || obj && obj.error) { return void console.error(err || obj.error); }
if (obj && obj.fId) { APP.convertedFolder = obj.fId; }
refresh();
});
});
}
} else { // File or shared folder
var sf = !anonDrive && manager.isSharedFolder(el);
if (anonDrive) {
data = el;
} else {
data = sf ? manager.getSharedFolderData(el) : manager.getFileData(el);
}
parsed = (data.href && data.href.indexOf('#') !== -1) ? Hash.parsePadUrl(data.href) : {};
var roParsed = Hash.parsePadUrl(data.roHref);
var padType = parsed.type || roParsed.type;
var ro = !sf || (folders[el] && folders[el].version >= 2);
var padData = {
teamId: APP.team,
origin: APP.origin,
pathname: "/" + padType + "/",
friends: friends,
password: data.password,
hashes: {
editHash: parsed.hash,
viewHash: ro && roParsed.hash,
fileHash: parsed.hash
},
fileData: {
hash: parsed.hash,
password: data.password
},
isTemplate: paths[0].path[0] === 'template',
title: data.title,
sharedFolder: sf,
common: common
};
if (padType === 'file') {
return void Share.getFileShareModal(common, padData);
}
Share.getShareModal(common, padData);
}
}
else if ($this.hasClass('cp-app-drive-context-savelocal')) {
if (paths.length !== 1) { return; }
el = manager.find(paths[0].path);
if (manager.isFile(el)) {
data = manager.getFileData(el);
} else if (manager.isSharedFolder(el)) {
data = manager.getSharedFolderData(el);
}
if (!data) { return; }
sframeChan.query('Q_STORE_IN_TEAM', {
href: data.href || data.rohref,
password: data.password,
path: paths[0].path[0] === 'template' ? ['template'] : undefined,
title: data.title || '',
teamId: -1
}, function (err) {
if (err) { return void console.error(err); }
});
}
else if ($this.hasClass('cp-app-drive-context-newfolder')) {
if (paths.length !== 1) { return; }
var onFolderCreated = function (err, info) {
if (err) { return void logError(err); }
APP.newFolder = info.newPath;
APP.displayDirectory(paths[0].path);
};
el = manager.find(paths[0].path);
if (manager.isSharedFolder(el)) {
paths[0].path.push(ROOT);
}
manager.addFolder(paths[0].path, null, onFolderCreated);
}
else if ($this.hasClass('cp-app-drive-context-newsharedfolder')) {
if (paths.length !== 1) { return; }
addSharedFolderModal(function (obj) {
if (!obj) { return; }
manager.addSharedFolder(paths[0].path, obj, refresh);
});
}
else if ($this.hasClass("cp-app-drive-context-uploadfiles")) {
showUploadFilesModal();
}
else if ($this.hasClass("cp-app-drive-context-uploadfolder")) {
showUploadFolderModal();
}
else if ($this.hasClass("cp-app-drive-context-newdoc")) {
var ntype = $this.data('type') || 'pad';
var path2 = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
openIn(ntype, path2, APP.team);
}
else if ($this.hasClass("cp-app-drive-context-properties")) {
if (type === 'trash') {
var pPath = paths[0].path;
if (paths.length !== 1 || pPath.length !== 4) { return; }
var element = manager.find(pPath.slice(0,3)); // element containing the oldpath
var sPath = stringifyPath(element.path);
UI.alert('<strong>' + Messages.fm_originalPath + "</strong>:<br>" + sPath, undefined, true);
return;
}
if (paths.length !== 1) { return; }
el = manager.find(paths[0].path);
if (paths[0].path[0] === SHARED_FOLDER && APP.newSharedFolder) {
// ANON_SHARED_FOLDER
el = manager.find(paths[0].path.slice(1), APP.newSharedFolder);
}
APP.getProperties(el, function (e) {
if (e) { return void logError(e); }
});
}
else if ($this.hasClass("cp-app-drive-context-access")) {
if (paths.length !== 1) { return; }
el = manager.find(paths[0].path);
if (paths[0].path[0] === SHARED_FOLDER && APP.newSharedFolder) {
// ANON_SHARED_FOLDER
el = manager.find(paths[0].path.slice(1), APP.newSharedFolder);
}
APP.getAccess(el, function (e) {
if (e) { return void logError(e); }
});
}
else if ($this.hasClass("cp-app-drive-context-hashtag")) {
var hrefs = paths.map(function (p) {
var el = manager.find(p.path);
var data = manager.getFileData(el);
return data.href || data.roHref;
}).filter(Boolean);
common.updateTags(hrefs);
}
else if ($this.hasClass("cp-app-drive-context-empty")) {
if (paths.length !== 1 || !paths[0].element
|| !manager.comparePath(paths[0].path, [TRASH])) {
log(Messages.fm_forbidden);
return;
}
emptyTrashModal();
}
else if ($this.hasClass("cp-app-drive-context-remove")) {
return void deletePaths(paths);
}
else if ($this.hasClass("cp-app-drive-context-removesf")) {
return void deletePaths(paths);
}
else if ($this.hasClass("cp-app-drive-context-restore")) {
if (paths.length !== 1) { return; }
var restorePath = paths[0].path;
var restoreName = paths[0].path[paths[0].path.length - 1];
if (restorePath.length === 4) {
var rEl = manager.find(restorePath);
if (manager.isFile(rEl)) {
restoreName = manager.getTitle(rEl);
} else if (manager.isSharedFolder(rEl)) {
var sfData = manager.getSharedFolderData(rEl);
restoreName = sfData.title || sfData.lastTitle || Messages.fm_deletedFolder;
} else {
restoreName = restorePath[1];
}
}
UI.confirm(Messages._getKey("fm_restoreDialog", [restoreName]), function(res) {
if (!res) { return; }
manager.restore(restorePath, refresh);
});
}
else if ($this.hasClass("cp-app-drive-context-openparent")) {
if (paths.length !== 1) { return; }
var parentPath = paths[0].path.slice();
if (manager.isInTrashRoot(parentPath)) { parentPath = [TRASH]; }
else { parentPath.pop(); }
APP.displayDirectory(parentPath);
APP.selectedFiles = paths[0].path.slice(-1);
}
APP.hideMenu();
});
// Chrome considers the double-click means "select all" in the window
$content.on('mousedown', function (e) {
$content.focus();
e.preventDefault();
});
$appContainer.on('mouseup', function (e) {
if (e.which !== 1) { return ; }
if ($(e.target).is(".dropdown-submenu a, .dropdown-submenu a span")) { return; } // if we click on dropdown-submenu, don't close menu
APP.hideMenu(e);
});
$appContainer.on('click', function (e) {
if (e.which !== 1) { return ; }
removeInput();
});
$appContainer.on('drag drop', function (e) {
removeInput();
APP.hideMenu(e);
});
$appContainer.on('mouseup drop', function () {
$('.cp-app-drive-element-droppable').removeClass('cp-app-drive-element-droppable');
});
$appContainer.on('keydown', function (e) {
// "Del"
if (e.which === 46) {
if (manager.isPathIn(currentPath, [FILES_DATA]) && APP.loggedIn) {
return; // We can't remove elements directly from filesData
}
var $selected = findSelectedElements();
if (!$selected.length) { return; }
var paths = [];
var isTrash = manager.isPathIn(currentPath, [TRASH]);
$selected.each(function (idx, elmt) {
if (!$(elmt).data('path')) { return; }
paths.push($(elmt).data('path'));
});
if (!paths.length) { return; }
// If we are in the trash or anon USER or if we are holding the "shift" key,
// delete permanently
// Or if we are in a shared folder
// Or if the selection is only shared folders
if (!APP.loggedIn || isTrash || manager.isInSharedFolder(currentPath)
|| e.shiftKey) {
deletePaths(null, paths);
return;
}
// else move to trash
moveElements(paths, [TRASH], false, refresh);
return;
}
});
$appContainer.contextmenu(function () {
APP.hideMenu();
return false;
});
var onRefresh = {
refresh: function() {
if (onRefresh.to) {
window.clearTimeout(onRefresh.to);
}
onRefresh.to = window.setTimeout(refresh, 500);
}
};
var onEvDriveChange = sframeChan.on('EV_DRIVE_CHANGE', function (data) {
if (history.isHistoryMode) { return; }
var path = data.path.slice();
var originalPath = data.path.slice();
if (!APP.loggedIn && APP.newSharedFolder && data.id === APP.newSharedFolder) {
// ANON_SHARED_FOLDER
return void onRefresh.refresh();
}
// Fix the path if this is about a shared folder
if (data.id && manager.folders[data.id]) {
var uoPath = manager.getUserObjectPath(manager.folders[data.id].userObject);
if (uoPath) {
Array.prototype.unshift.apply(path, uoPath);
path.unshift('drive');
}
}
if (path[0] !== 'drive') { return false; }
path = path.slice(1);
if (originalPath[0] === 'drive') { originalPath = originalPath.slice(1); }
var cPath = currentPath.slice();
if (originalPath.length && originalPath[0] === FILES_DATA) {
onRefresh.refresh();
} else if ((manager.isPathIn(cPath, ['hrefArray', TRASH]) && cPath[0] === path[0]) ||
(path.length >= cPath.length && manager.isSubpath(path, cPath))) {
// Reload after a few ms to make sure all the change events have been received
onRefresh.refresh();
} else {
APP.resetTree();
}
return false;
});
var onEvDriveRemove = sframeChan.on('EV_DRIVE_REMOVE', function (data) {
if (history.isHistoryMode) { return; }
var path = data.path.slice();
if (!APP.loggedIn && APP.newSharedFolder && data.id === APP.newSharedFolder) {
// ANON_SHARED_FOLDER
return void onRefresh.refresh();
}
// Fix the path if this is about a shared folder
if (data.id && manager.folders[data.id]) {
var uoPath = manager.getUserObjectPath(manager.folders[data.id].userObject);
if (uoPath) {
Array.prototype.unshift.apply(path, uoPath);
path.unshift('drive');
}
}
if (path[0] !== 'drive') { return false; }
path = path.slice(1);
var cPath = currentPath.slice();
if ((manager.isPathIn(cPath, ['hrefArray', TRASH]) && cPath[0] === path[0]) ||
(path.length >= cPath.length && manager.isSubpath(path, cPath))) {
// Reload after a few to make sure all the change events have been received
onRefresh.refresh();
} else {
APP.resetTree();
}
return false;
});
$(window).on('mouseup', onWindowMouseUp);
$(window).on('keydown', onWindowKeydown);
$(window).on('click', onWindowClick);
var removeWindowListeners = function () {
$(window).off('mouseup', onWindowMouseUp);
$(window).off('keydown', onWindowKeydown);
$(window).off('click', onWindowClick);
try {
onEvDriveChange.stop();
onEvDriveRemove.stop();
} catch (e) {}
};
if (APP.histConfig) {
APP.histConfig.onOpen = function () {
// If we're in a shared folder history, store its id in memory
// so that we remember that this isn't the drive history if
// we browse through the drive
var sfId = manager.isInSharedFolder(currentPath);
if (!sfId) {
delete history.sfId;
delete APP.histConfig.sharedFolder;
return;
}
history.sfId = sfId;
var data = manager.getSharedFolderData(sfId);
var parsed = Hash.parsePadUrl(data.href || data.roHref);
APP.histConfig.sharedFolder = {
hash: parsed.hash,
password: data.password
};
};
history.onEnterHistory = function (obj) {
if (history.sfId) {
if (!obj || typeof(obj) !== "object" || Object.keys(obj).length === 0) {
return;
}
copyObjectValue(folders[history.sfId], obj);
refresh();
return;
}
history.sfId = false;
var ok = manager.isValidDrive(obj.drive);
if (!ok) { return; }
var restricted = files.restrictedFolders;
copyObjectValue(files, obj.drive);
files.restrictedFolders = restricted;
appStatus.isReady = true;
refresh();
};
history.onLeaveHistory = function () {
copyObjectValue(files, proxy.drive);
refresh();
};
}
var fmConfig = {
teamId: APP.team,
noHandlers: true,
onUploaded: function () {
refresh();
},
body: $('body')
};
APP.FM = common.createFileManager(fmConfig);
refresh();
UI.removeLoadingScreen();
/*
if (!APP.team) {
sframeChan.query('Q_DRIVE_GETDELETED', null, function (err, data) {
var ids = manager.findChannels(data);
var titles = [];
ids.forEach(function (id) {
var title = manager.getTitle(id);
titles.push(title);
var paths = manager.findFile(id);
manager.delete(paths, refresh);
});
if (!titles.length) { return; }
UI.log(Messages._getKey('fm_deletedPads', [titles.join(', ')]));
});
}
*/
var nt = nThen;
var passwordModal = function (fId, data, cb) {
var content = [];
var folderName = '<b>'+ (data.lastTitle || Messages.fm_newFolder) +'</b>';
content.push(UI.setHTML(h('p'), Messages._getKey('drive_sfPassword', [folderName])));
var newPassword = UI.passwordInput({
id: 'cp-app-prop-change-password',
placeholder: Messages.settings_changePasswordNew,
style: 'flex: 1;'
});
var passwordOk = h('button.btn.btn-secondary', Messages.properties_changePasswordButton);
var changePass = h('span.cp-password-container', [
newPassword,
passwordOk
]);
content.push(changePass);
var div = h('div', content);
var locked = false;
$(passwordOk).click(function () {
if (locked) { return; }
var pw = $(newPassword).find('.cp-password-input').val();
locked = true;
$(div).find('.alert').remove();
$(passwordOk).html('').append(h('span.fa.fa-spinner.fa-spin', {style: 'margin-left: 0'}));
manager.restoreSharedFolder(fId, pw, function (err, obj) {
if (obj && obj.error) {
var wrong = h('div.alert.alert-danger', Messages.drive_sfPasswordError);
$(div).prepend(wrong);
$(passwordOk).text(Messages.properties_changePasswordButton);
locked = false;
return;
}
UI.findCancelButton($(div).closest('.alertify')).click();
cb();
});
});
var buttons = [{
className: 'primary',
name: Messages.forgetButton,
onClick: function () {
manager.delete([['sharedFoldersTemp', fId]], function () { });
},
keys: []
}, {
className: 'cancel',
name: Messages.later,
onClick: function () {},
keys: [27]
}];
return UI.dialog.customModal(div, {
buttons: buttons,
onClose: cb
});
};
var deprecated = files.sharedFoldersTemp;
if (typeof (deprecated) === "object" && APP.editable && Object.keys(deprecated).length) {
Object.keys(deprecated).forEach(function (fId) {
var data = deprecated[fId];
var sfId = manager.user.userObject.getSFIdFromHref(data.href);
if (folders[fId] || sfId) { // This shared folder is already stored in the drive...
return void manager.delete([['sharedFoldersTemp', fId]], function () { });
}
nt = nt(function (waitFor) {
UI.openCustomModal(passwordModal(fId, data, waitFor()));
}).nThen;
});
nt(function () {
refresh();
});
}
return {
refresh: refresh,
close: function () {
APP.closed = true;
removeWindowListeners();
}
};
};
return {
create: create,
setEditable: setEditable,
logError: logError
};
});