cryptpad/www/drive/inner.js

3027 lines
127 KiB
JavaScript
Raw Normal View History

define([
2017-09-27 08:31:35 +00:00
'jquery',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.js',
'/common/toolbar3.js',
'json.sortify',
'/common/cryptpad-common.js',
'/common/common-util.js',
'/common/cryptget.js',
'/bower_components/nthen/index.js',
'/common/sframe-common.js',
'/common/common-realtime.js',
'/common/userObject.js',
'/customize/application_config.js',
'/common/mergeDrive.js',
'/common/sframe-chainpad-listmap.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
2017-09-27 08:31:35 +00:00
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,
Crypto,
TextPatcher,
Toolbar,
JSONSortify,
Cryptpad,
Util,
Cryptget,
nThen,
SFCommon,
CommonRealtime,
FO,
AppConfig,
Merge,
Listmap)
{
var Messages = Cryptpad.Messages;
var APP = window.APP = {
editable: false,
Cryptpad: Cryptpad,
mobile: function () { return $('body').width() <= 600; } // Menu and content area are not inline-block anymore for mobiles
};
var stringify = function (obj) {
return JSONSortify(obj);
};
var onConnectError = function () {
Cryptpad.errorLoadingScreen(Messages.websocketError);
};
var E_OVER_LIMIT = 'E_OVER_LIMIT';
var SEARCH = "search";
var SEARCH_NAME = Messages.fm_searchName;
var ROOT = "root";
var ROOT_NAME = Messages.fm_rootName;
var FILES_DATA = Cryptpad.storageKey;
var FILES_DATA_NAME = Messages.fm_filesDataName;
var TEMPLATE = "template";
var TEMPLATE_NAME = Messages.fm_templateName;
var TRASH = "trash";
var TRASH_NAME = Messages.fm_trashName;
var RECENT = "recent";
var RECENT_NAME = Messages.fm_recentPadsName;
var LS_LAST = "app-drive-lastOpened";
var LS_OPENED = "app-drive-openedFolders";
var LS_VIEWMODE = "app-drive-viewMode";
var LS_SEARCHCURSOR = "app-drive-searchCursor";
var FOLDER_CONTENT_ID = "cp-app-drive-content-folder";
var config = {};
var DEBUG = config.DEBUG = true;
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 = Cryptpad.log;
var localStore = window.cryptpadStore;
APP.store = {};
var getLastOpenedFolder = function () {
var path;
try {
path = APP.store[LS_LAST] ? JSON.parse(APP.store[LS_LAST]) : [ROOT];
} catch (e) {
path = [ROOT];
}
return path;
};
var setLastOpenedFolder = function (path) {
if (path[0] === SEARCH) { return; }
APP.store[LS_LAST] = JSON.stringify(path);
localStore.put(LS_LAST, JSON.stringify(path));
};
var wasFolderOpened = function (path) {
var stored = JSON.parse(APP.store[LS_OPENED] || '[]');
return stored.indexOf(JSON.stringify(path)) !== -1;
};
var 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));
};
var getViewModeClass = function () {
var mode = APP.store[LS_VIEWMODE];
if (mode === 'list') { 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);
};
var setSearchCursor = function () {
var $input = $('#cp-app-drive-tree-search-input');
APP.store[LS_SEARCHCURSOR] = $input[0].selectionStart;
localStore.put(LS_SEARCHCURSOR, $input[0].selectionStart);
};
var getSearchCursor = function () {
return APP.store[LS_SEARCHCURSOR] || 0;
};
var setEditable = function (state) {
APP.editable = state;
if (!state) {
APP.$content.addClass('cp-app-drive-readonly');
$('[draggable="true"]').attr('draggable', false);
}
else {
APP.$content.removeClass('cp-app-drive-readonly');
$('[draggable="false"]').attr('draggable', true);
}
};
// Icons
var $folderIcon = $('<span>', {
"class": "fa fa-folder 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": "fa fa-folder-open cp-app-drive-icon-folder"});
//var $folderOpenedIcon = $('<img>', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"});
var $folderOpenedEmptyIcon = $folderOpenedIcon.clone();
//var $upIcon = $('<span>', {"class": "fa fa-arrow-circle-up"});
var $unsortedIcon = $('<span>', {"class": "fa fa-files-o"});
var $templateIcon = $('<span>', {"class": "fa fa-cubes"});
var $recentIcon = $('<span>', {"class": "fa fa-clock-o"});
var $trashIcon = $('<span>', {"class": "fa fa-trash"});
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 $emptyTrashIcon = $('<button>', {"class": "fa fa-ban"});
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"});
2017-09-27 08:36:16 +00:00
//var $backupIcon = $('<span>', {"class": "fa fa-life-ring"});
2017-09-27 08:31:35 +00:00
var $searchIcon = $('<span>', {"class": "fa fa-search cp-app-drive-tree-search-con"});
var $addIcon = $('<span>', {"class": "fa fa-plus"});
var $renamedIcon = $('<span>', {"class": "fa fa-flag"});
var $readonlyIcon = $('<span>', {"class": "fa fa-eye"});
var history = {
isHistoryMode: false,
};
var andThen = function (common, proxy) {
var files = proxy.drive;
var metadataMgr = common.getMetadataMgr();
var priv = metadataMgr.getPrivateData();
var user = metadataMgr.getUserData();
var isOwnDrive = function () {
return true; // TODO
//return Cryptpad.getUserHash() === APP.hash || localStorage.FS_hash === APP.hash;
};
var isWorkgroup = function () {
return files.workgroup === 1;
};
config.workgroup = isWorkgroup();
config.Cryptpad = Cryptpad;
config.loggedIn = APP.loggedIn;
APP.origin = priv.origin;
var filesOp = FO.init(files, config);
filesOp.fixFiles();
var error = filesOp.error;
var $tree = APP.$tree = $("#cp-app-drive-tree");
var $content = APP.$content = $("#cp-app-drive-content");
var $appContainer = $(".cp-app-drive-container");
var $driveToolbar = $("#cp-app-drive-toolbar");
var $contextMenu = $("#cp-app-drive-context-tree");
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");
$tree.on('drop dragover', function (e) {
e.preventDefault();
e.stopPropagation();
});
$driveToolbar.on('drop dragover', function (e) {
e.preventDefault();
e.stopPropagation();
});
// TOOLBAR
/* add a "change username" button */
if (!APP.readOnly) {
APP.$displayName.text(user.name || Messages.anonymous);
} else {
APP.$displayName.html('<span class="' + Toolbar.constants.readonly + '">' + Messages.readonly + '</span>');
}
// FILE MANAGER
// _WORKGROUP_ and other people drive : display Documents as main page
var currentPath = APP.currentPath = isOwnDrive() ? getLastOpenedFolder() : [ROOT];
// Categories dislayed in the menu
// _WORKGROUP_ : do not display unsorted
var displayedCategories = [ROOT, TRASH, SEARCH, RECENT];
if (AppConfig.enableTemplates) { displayedCategories.push(TEMPLATE); }
if (isWorkgroup()) { displayedCategories = [ROOT, TRASH, SEARCH]; }
var virtualCategories = [SEARCH, RECENT];
if (!APP.loggedIn) {
displayedCategories = [FILES_DATA];
currentPath = [FILES_DATA];
$tree.hide();
if (Object.keys(files.root).length && !proxy.anonymousAlert) {
Cryptpad.alert(Messages.fm_alert_anonymous, null, true);
proxy.anonymousAlert = true;
}
}
if (!APP.readOnly) {
setEditable(true);
}
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) {
$('.cp-app-drive-element-selected').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 = 200;
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 (currentPath[0] === SEARCH) { return; }
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);
});
$(window).on('mouseup', 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')
.addClass('cp-app-drive-element-selected');
e.stopPropagation();
});
// Arrow keys to modify the selection
$(window).keydown(function (e) {
var $searchBar = $tree.find('#cp-app-drive-tree-search-input');
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; }
var click = function (el) {
if (!el) { return; }
APP.onElementClick(ev, $(el));
};
// 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;
}
// [Left, Up, Right, Down]
if ([37, 38, 39, 40].indexOf(e.which) === -1) { return; }
e.preventDefault();
var $selection = $content.find('.cp-app-drive-element.cp-app-drive-element-selected');
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 removeInput = function (cancel) {
if (!cancel && $('.cp-app-drive-element-row > input').length === 1) {
var $input = $('.cp-app-drive-element-row > input');
filesOp.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 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) {
error("Unable to format that string to a date with .toLocaleString", sDate, e);
}
return ret;
};
var openFile = function (el, href) {
if (!href) {
var data = filesOp.getFileData(el);
if (!data || !data.href) {
return void logError("Missing data for the file", el, data);
}
href = data.href;
}
window.open(APP.origin + href);
};
var refresh = APP.refresh = function () {
APP.displayDirectory(currentPath);
};
var getFileNameExtension = function (name) {
var matched = /\.\S+$/.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();
removeSelected();
var $name = $element.find('.cp-app-drive-element-name');
if (!$name.length) {
$name = $element.find('> .cp-app-drive-element');
}
$name.hide();
var el = filesOp.find(path);
var name = filesOp.isFile(el) ? filesOp.getTitle(el) : path[path.length - 1];
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) {
if (e.which === 13) {
removeInput(true);
filesOp.rename(path, $input.val(), refresh);
return;
}
if (e.which === 27) {
removeInput(true);
}
});
//$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) {
removeSelected();
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);
};
var filterContextMenu = function ($menu, paths) {
//var path = $element.data('path');
if (!paths || paths.length === 0) { logError('no paths'); }
var hide = [];
var hasFolder = false;
paths.forEach(function (p) {
var path = p.path;
var $element = p.element;
if (path.length === 1) {
// Can't rename or delete root elements
hide.push($menu.find('a.cp-app-drive-context-rename'));
hide.push($menu.find('a.cp-app-drive-context-delete'));
}
if (!APP.editable) {
hide.push($menu.find('a.cp-app-drive-context-editable'));
}
if (!isOwnDrive()) {
hide.push($menu.find('a.cp-app-drive-context-own'));
}
if ($element.is('.cp-app-drive-element-file')) {
// No folder in files
hide.push($menu.find('a.cp-app-drive-context-newfolder'));
if ($element.is('.cp-app-drive-readonly')) {
// Keep only open readonly
hide.push($menu.find('a.cp-app-drive-context-open'));
} else if ($element.is('.cp-app-drive-noreadonly')) {
// Keep only open readonly
hide.push($menu.find('a.cp-app-drive-context-openro'));
}
} else {
if (hasFolder) {
// More than 1 folder selected: cannot create a new subfolder
hide.push($menu.find('a.cp-app-drive-context-newfolder'));
}
hasFolder = true;
hide.push($menu.find('a.cp-app-drive-context-openro'));
hide.push($menu.find('a.cp-app-drive-context-properties'));
}
// If we're in the trash, hide restore and properties for non-root elements
if ($menu.find('a.cp-app-drive-context-restore').length && path && path.length > 4) {
hide.push($menu.find('a.cp-app-drive-context-restore'));
hide.push($menu.find('a.cp-app-drive-context-properties'));
}
});
if (paths.length > 1) {
hide.push($menu.find('a.cp-app-drive-context-restore'));
hide.push($menu.find('a.cp-app-drive-context-properties'));
hide.push($menu.find('a.cp-app-drive-context-rename'));
}
if (hasFolder && paths.length > 1) {
// Cannot open multiple folders
hide.push($menu.find('a.cp-app-drive-context-open'));
}
return hide;
};
var getSelectedPaths = function ($element) {
var paths = [];
if ($('.cp-app-drive-element-selected').length > 1) {
var $selected = $('.cp-app-drive-element-selected');
$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 updateContextButton = function () {
if (filesOp.isPathIn(currentPath, [TRASH])) {
$driveToolbar.find('cp-app-drive-toolbar-emptytrash').show();
} else {
$driveToolbar.find('cp-app-drive-toolbar-emptytrash').hide();
}
var $li = $content.find('.cp-app-drive-element-selected');
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: '#000'
});
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 $menu = $element.data('context');
if (!$menu) { return; }
//var actions = [];
var $actions = $menu.find('a');
var toHide = filterContextMenu($menu, paths);
$actions = $actions.filter(function (i, el) {
for (var j = 0; j < toHide.length; j++) {
if ($(el).is(toHide[j])) { return false; }
}
return true;
});
$actions.each(function (i, el) {
var $a = $('<button>', {'class': 'cp-app-drive-element'});
if ($(el).attr('data-icon')) {
$a.addClass('fa').addClass($(el).attr('data-icon'));
$a.attr('title', $(el).text());
} else {
$a.text($(el).text());
}
$(el).data('paths', paths);
//$(el).data('path', path);
//:$(el).data('element', $element);
$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 (!$(el).hasClass("cp-app-drive-element-selected")) {
$(el).addClass("cp-app-drive-element-selected");
}
});
for (var i = Math.min(sel.startSelected, sel.endSelected);
i <= Math.max(sel.startSelected, sel.endSelected);
i++) {
$el = $($elements.get(i));
if (!$el.hasClass("cp-app-drive-element-selected")) {
$el.addClass("cp-app-drive-element-selected");
}
}
} else {
if (!$element.hasClass("cp-app-drive-element-selected")) {
$element.addClass("cp-app-drive-element-selected");
} else {
$element.removeClass("cp-app-drive-element-selected");
}
}
updateContextButton();
};
var displayMenu = function (e, $menu) {
$menu.css({ display: "block" });
if (APP.mobile()) { 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 (e, $menu) {
APP.hideMenu();
e.stopPropagation();
var $element = findDataHolder($(e.target));
if (!$element.length) {
logError("Unable to locate the .element tag", e.target);
$menu.hide();
log(Messages.fm_contextMenuError);
return false;
}
if (!$element.hasClass('cp-app-drive-element-selected')) { //paths.length === 1) {
onElementClick(undefined, $element);
}
var paths = getSelectedPaths($element);
var toHide = filterContextMenu($menu, paths);
toHide.forEach(function ($a) {
$a.parent('li').hide();
});
displayMenu(e, $menu);
if ($menu.find('li:visible').length === 0) {
debug("No visible element in the context menu. Abort.");
$menu.hide();
return true;
}
$menu.find('a').data('paths', paths);
//$menu.find('a').data('path', path);
//$menu.find('a').data('element', $element);
return false;
};
var openDirectoryContextMenu = function (e) {
$contextMenu.find('li').show();
openContextMenu(e, $contextMenu);
return false;
};
var openDefaultContextMenu = function (e) {
$defaultContextMenu.find('li').show();
openContextMenu(e, $defaultContextMenu);
return false;
};
var openTrashTreeContextMenu = function (e) {
removeSelected();
$trashTreeContextMenu.find('li').show();
openContextMenu(e, $trashTreeContextMenu);
return false;
};
var openTrashContextMenu = function (e) {
var path = findDataHolder($(e.target)).data('path');
if (!path) { return; }
$trashContextMenu.find('li').show();
openContextMenu(e, $trashContextMenu);
return false;
};
var openContentContextMenu = function (e) {
APP.hideMenu();
e.stopPropagation();
var path = $(e.target).closest('#' + FOLDER_CONTENT_ID).data('path');
if (!path) { return; }
var $menu = $contentContextMenu;
removeSelected();
if (!APP.editable) {
$menu.find('a.cp-app-drive-context-editable').parent('li').hide();
}
if (!isOwnDrive()) {
$menu.find('a.cp-app-drive-context-own').parent('li').hide();
}
$menu.find('[data-type]').each(function (idx, el) {
if (AppConfig.availablePadTypes.indexOf($(el).attr('data-type')) === -1) {
$(el).hide();
}
});
displayMenu(e, $menu);
if ($menu.find('li:visible').length === 0) {
debug("No visible element in the context menu. Abort.");
$menu.hide();
return true;
}
$menu.find('a').data('path', path);
return false;
};
var getElementName = function (path) {
var file = filesOp.find(path);
if (!file || !filesOp.isFile(file)) { return '???'; }
return filesOp.getTitle(file);
};
// filesOp.moveElements is able to move several paths to a new location, including
// the Trash or the "Unsorted files" folder
var moveElements = function (paths, newPath, force, cb) {
if (!APP.editable) { return; }
var andThenMove = function () {
filesOp.move(paths, newPath, cb);
};
// Cancel drag&drop from TRASH to TRASH
if (filesOp.isPathIn(newPath, [TRASH]) && paths.length && paths[0][0] === TRASH) {
return;
}
andThenMove();
};
// 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 = $('.cp-app-drive-element-selected');
$selected.each(function (idx, elmt) {
var ePath = $(elmt).data('path');
if (ePath) {
var val = filesOp.find(ePath);
if (!val) { return; } // Error? A ".selected" element in not in the object
paths.push({
path: ePath,
value: {
name: getElementName(ePath),
el: val
}
});
}
});
} else {
removeSelected();
$element.addClass('cp-app-drive-element-selected');
var val = filesOp.find(path);
if (!val) { return; } // The element in not in the object
paths = [{
path: path,
value: {
name: getElementName(path),
el: val
}
}];
}
var data = {
'path': paths
};
ev.dataTransfer.setData("text", stringify(data));
};
var onFileDrop = APP.onFileDrop = function (file, e) {
APP.FM.onFileDrop(file, e);
};
var findDropPath = function (target) {
var $target = $(target);
var $el = findDataHolder($target);
var newPath = $el.data('path');
if ((!newPath || filesOp.isFile(filesOp.find(newPath)))
&& $target.parents('#cp-app-drive-content')) {
newPath = currentPath;
}
return newPath;
};
var onDrop = function (ev) {
ev.preventDefault();
$('.cp-app-drive-element-droppable').removeClass('cp-app-drive-element-droppable');
var data = ev.dataTransfer.getData("text");
// Don't the 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; }
// Dropped elements can be moved from the same file manager or imported from another one.
// A moved element should be removed from its previous location
var movedPaths = [];
var importedElements = [];
oldPaths.forEach(function (p) {
var el = filesOp.find(p.path);
if (el && (stringify(el) === stringify(p.value.el) || !p.value || !p.value.el)) {
movedPaths.push(p.path);
} else {
importedElements.push(p.value);
}
});
var newPath = findDropPath(ev.target);
if (!newPath) { return; }
if (movedPaths && movedPaths.length) {
moveElements(movedPaths, newPath, null, refresh);
}
if (importedElements && importedElements.length) {
// TODO workgroup
//filesOp.importElements(importedElements, newPath, 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('droppable');
});
$element.on('dragleave', function (e) {
e.preventDefault();
e.stopPropagation();
counter--;
if (counter <= 0) {
counter = 0;
$element.removeClass('droppable');
}
});
};
addDragAndDropHandlers($content, null, true, true);
// In list mode, display metadata from the filesData object
// _WORKGROUP_ : Do not display title, atime and ctime columns since we don't have files data
var addFileData = function (element, $span) {
if (!filesOp.isFile(element)) { return; }
var data = filesOp.getFileData(element);
if (!data) { return void logError("No data for the file", element); }
var hrefData = Cryptpad.parsePadUrl(data.href);
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('title', Messages._getKey('fm_renamedPad', [data.title]));
}
var name = filesOp.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);
$span.html('');
$span.append($name);
$span.append($state);
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));
$span.append($type);
if (!isWorkgroup()) {
$span.append($adate).append($cdate);
}
};
var addFolderData = function (element, key, $span) {
if (!element || !filesOp.isFolder(element)) { return; }
$span.html('');
// The element with the class '.name' is underlined when the 'li' is hovered
var sf = filesOp.hasSubfolder(element);
var files = filesOp.hasFile(element);
var $name = $('<span>', {'class': 'cp-app-drive-element-name'}).text(key);
var $state = $('<span>', {'class': 'cp-app-drive-element-state'});
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(files);
$span.append($name).append($state).append($subfolders).append($files);
};
// This is duplicated in cryptpad-common, it should be unified
var getFileIcon = function (id) {
var data = filesOp.getFileData(id);
return Cryptpad.getFileIcon(data);
};
var getIcon = Cryptpad.getIcon;
// 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;
if (isTrash && Array.isArray(elPath)) {
key = elPath[0];
elPath.forEach(function (k) { newPath.push(k); });
} else {
key = elPath;
newPath.push(key);
}
var element = filesOp.find(newPath);
var $icon = !isFolder ? getFileIcon(element) : undefined;
var ro = filesOp.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 cp-app-drive-element' + roClass;
if (isFolder) {
liClass = 'cp-app-drive-element-folder cp-app-drive-element';
$icon = filesOp.isFolderEmpty(root[key]) ? $folderEmptyIcon.clone() : $folderIcon.clone();
}
var $element = $('<li>', {
draggable: true,
'class': 'cp-app-drive-element-row'
});
if (!isFolder && Array.isArray(APP.selectedFiles)) {
var idx = APP.selectedFiles.indexOf(element);
if (idx !== -1) {
$element.addClass('cp-app-drive-element-selected');
APP.selectedFiles.splice(idx, 1);
}
}
if (isFolder) {
addFolderData(element, key, $element);
} else {
addFileData(element, $element);
}
$element.prepend($icon).dblclick(function () {
if (isFolder) {
APP.displayDirectory(newPath);
return;
}
if (isTrash) { return; }
openFile(root[key]);
});
$element.addClass(liClass);
$element.data('path', newPath);
addDragAndDropHandlers($element, newPath, isFolder, !isTrash);
$element.click(function(e) {
e.stopPropagation();
onElementClick(e, $element, newPath);
});
if (!isTrash) {
$element.contextmenu(openDirectoryContextMenu);
$element.data('context', $contextMenu);
} else {
$element.contextmenu(openTrashContextMenu);
$element.data('context', $trashContextMenu);
}
2017-10-05 08:21:59 +00:00
var isNewFolder = APP.newFolder && filesOp.comparePath(newPath, APP.newFolder);
2017-09-27 08:31:35 +00:00
if (isNewFolder) {
appStatus.onReady(function () {
window.setTimeout(function () { displayRenameInput($element, newPath); }, 0);
});
delete APP.newFolder;
}
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;
default: pName = name;
}
return pName;
};
// Create the title block with the "parent folder" button
var createTitle = function ($container, path, noStyle) {
if (!path || path.length === 0) { return; }
var isTrash = filesOp.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 : filesOp.find(path);
path = path[0] === SEARCH ? path.slice(0,1) : path;
path.forEach(function (p, idx) {
if (isTrash && [2,3].indexOf(idx) !== -1) { return; }
var name = p;
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 () {
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 && filesOp.isFile(el)) {
name = getElementName(path);
}
if (idx === 0) { name = getPrettyName(p); }
else {
var $span2 = $('<span>', {
'class': 'cp-app-drive-path-element cp-app-drive-path-separator'
}).text(' / ');
$container.prepend($span2);
}
$span.text(name).prependTo($container);
});
};
var createInfoBox = function (path) {
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;
default:
msg = undefined;
}
if (!APP.loggedIn) {
msg = Messages.fm_info_anonymous;
$box.html(msg);
return $box;
}
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.removeClass('cp-app-drive-toolbar-active');
$listButton.addClass('cp-app-drive-toolbar-active');
setViewMode('list');
$('#' + FOLDER_CONTENT_ID).removeClass('cp-app-drive-content-grid');
$('#' + FOLDER_CONTENT_ID).addClass('cp-app-drive-content-list');
common.feedback('DRIVE_LIST_MODE');
});
$gridButton.click(function () {
$listButton.removeClass('cp-app-drive-toolbar-active');
$gridButton.addClass('cp-app-drive-toolbar-active');
setViewMode('grid');
$('#' + FOLDER_CONTENT_ID).addClass('cp-app-drive-content-grid');
$('#' + FOLDER_CONTENT_ID).removeClass('cp-app-drive-content-list');
common.feedback('DRIVE_GRID_MODE');
});
if (getViewMode() === 'list') {
$listButton.addClass('cp-app-drive-toolbar-active');
} else {
$gridButton.addClass('cp-app-drive-toolbar-active');
}
$listButton.attr('title', Messages.fm_viewListButton);
$gridButton.attr('title', Messages.fm_viewGridButton);
$container.append($listButton).append($gridButton);
};
var createEmptyTrashButton = function ($container) {
var $button = $emptyTrashIcon.clone();
$button.addClass('cp-app-drive-toolbar-emptytrash');
$button.attr('title', Messages.fc_empty);
$button.click(function () {
Cryptpad.confirm(Messages.fm_emptyTrashDialog, function(res) {
if (!res) { return; }
filesOp.emptyTrash(refresh);
});
});
$container.append($button);
};
var getNewPadTypes = function () {
var arr = [];
AppConfig.availablePadTypes.forEach(function (type) {
if (type === 'drive') { return; }
if (type === 'contacts') { return; }
if (type === 'todo') { return; }
if (type === 'file') { return; }
if (!Cryptpad.isLoggedIn() && AppConfig.registeredOnlyTypes &&
AppConfig.registeredOnlyTypes.indexOf(type) !== -1) {
return;
}
arr.push(type);
});
return arr;
};
var addNewPadHandlers = function ($block, isInRoot) {
// Handlers
if (isInRoot) {
var onCreated = function (err, info) {
if (err) {
if (err === E_OVER_LIMIT) {
return void Cryptpad.alert(Messages.pinLimitDrive, null, true);
}
return void Cryptpad.alert(Messages.fm_error_cantPin);
}
APP.newFolder = info.newPath;
refresh();
};
2017-10-04 13:51:44 +00:00
$block.find('a.cp-app-drive-new-folder, li.cp-app-drive-new-folder')
2017-09-27 08:31:35 +00:00
.click(function () {
filesOp.addFolder(currentPath, null, onCreated);
});
2017-10-04 13:51:44 +00:00
$block.find('a.cp-app-drive-new-upload, li.cp-app-drive-new-upload')
2017-09-27 08:31:35 +00:00
.click(function () {
var $input = $('<input>', {
'type': 'file',
'style': 'display: none;'
}).on('change', function (e) {
var file = e.target.files[0];
var ev = {
target: $content[0]
};
APP.FM.handleFile(file, ev);
});
$input.click();
});
}
2017-10-04 13:51:44 +00:00
$block.find('a.cp-app-drive-new-doc, li.cp-app-drive-new-doc')
2017-09-27 08:31:35 +00:00
.click(function () {
var type = $(this).attr('data-type') || 'pad';
var path = filesOp.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
common.sessionStorage.put(Cryptpad.newPadPathKey, path, function () {
common.openURL('/' + type + '/');
});
});
};
var createNewButton = function (isInRoot, $container) {
if (!APP.editable) { return; }
if (!APP.loggedIn) { return; } // Anonymous users can use the + menu in the toolbar
if (!filesOp.isPathIn(currentPath, [ROOT, 'hrefArray'])) { return; }
// Create dropdown
var options = [];
if (isInRoot) {
options.push({
tag: 'a',
2017-10-04 13:51:44 +00:00
attributes: {'class': 'cp-app-drive-new-older'},
2017-09-27 08:31:35 +00:00
content: $('<div>').append($folderIcon.clone()).html() + Messages.fm_folder
});
options.push({tag: 'hr'});
options.push({
tag: 'a',
2017-10-04 13:51:44 +00:00
attributes: {'class': 'cp-app-drive-new-upload'},
2017-09-27 08:31:35 +00:00
content: $('<div>').append(getIcon('file')).html() + Messages.uploadButton
});
options.push({tag: 'hr'});
}
getNewPadTypes().forEach(function (type) {
var attributes = {
2017-10-04 13:51:44 +00:00
'class': 'cp-app-drive-new-doc',
2017-09-27 08:31:35 +00:00
'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',
};
var $block = Cryptpad.createDropdown(dropdownConfig);
// Custom style:
$block.find('button').addClass('cp-app-drive-toolbar-new');
$block.find('button').attr('title', Messages.fm_newButtonTitle);
addNewPadHandlers($block, isInRoot);
$container.append($block);
};
var hideNewButton = function () {
$('.cp-dropdown-content').hide();
};
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-title', 'cp-app-drive-element-type',
'cp-app-drive-element-atime', 'cp-app-drive-element-ctime'].some(function (c) {
if ($span.hasClass(c)) {
if (value === c) { descValue = descValue ? false : true; }
else {
// atime and ctime should be ordered in a desc order at the first click
value = c.replace(/cp-app-drive-element-/, '');
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 getFolderListHeader = function () {
var $fohElement = $('<li>', {
'class': 'cp-app-drive-element-header cp-app-drive-element-list'
});
//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 ' +
'cp-app-drive-sort-clickable'
}).text(Messages.fm_folderName).click(onSortByClick);
var $state = $('<span>', {'class': 'cp-app-drive-element-state'});
var $subfolders = $('<span>', {
'class': 'cp-app-drive-element-folders cp-app-drive-element-list'
}).text(Messages.fm_numberOfFolders);
var $files = $('<span>', {
'class': 'cp-app-drive-element-files cp-app-drive-element-list'
}).text(Messages.fm_numberOfFiles);
$fohElement.append($fhIcon).append($name).append($state)
.append($subfolders).append($files);
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 = APP.store[SORT_FILE_BY]; }
if (classSorted) {
$list.find('.' + classSorted).addClass('cp-app-drive-sort-active').prepend($icon);
}
};
// _WORKGROUP_ : do not display title, atime and ctime in workgroups since we don't have files data
var getFileListHeader = function () {
var $fihElement = $('<li>', {
'class': 'cp-app-drive-element-header cp-app-drive-element-list'
});
//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 ' +
'cp-app-drive-sort-clickable'
}).text(Messages.fm_fileName).click(onSortByClick);
var $fhState = $('<span>', {'class': 'cp-app-drive-element-state'});
var $fhType = $('<span>', {
'class': 'cp-app-drive-element-type cp-app-drive-sort-clickable'
}).text(Messages.fm_type).click(onSortByClick);
var $fhAdate = $('<span>', {
'class': 'cp-app-drive-element-atime cp-app-drive-sort-clickable'
}).text(Messages.fm_lastAccess).click(onSortByClick);
var $fhCdate = $('<span>', {
'class': 'cp-app-drive-element-ctime cp-app-drive-sort-clickable'
}).text(Messages.fm_creation).click(onSortByClick);
// If displayTitle is false, it means the "name" is the title, so do not display the "name" header
$fihElement.append($fhIcon).append($fhName).append($fhState).append($fhType);
if (!isWorkgroup()) {
$fihElement.append($fhAdate).append($fhCdate);
}
addFileSortIcon($fihElement);
return $fihElement;
};
var sortElements = function (folder, path, oldkeys, prop, asc, useId) {
var root = filesOp.find(path);
var test = folder ? filesOp.isFolder : filesOp.isFile;
var keys = oldkeys.filter(function (e) {
return useId ? test(e) : test(root[e]);
});
if (keys.length < 2) { return keys; }
var mult = asc ? 1 : -1;
var getProp = function (el, prop) {
if (folder) { return el.toLowerCase(); }
var id = useId ? el : root[el];
var data = filesOp.getFileData(id);
if (!data) { return ''; }
if (prop === 'type') {
var hrefData = Cryptpad.parsePadUrl(data.href);
return hrefData.type;
}
if (prop === 'atime' || prop === 'ctime') {
return new Date(data[prop]);
}
return (filesOp.getTitle(id) || "").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;
};
var sortTrashElements = function (folder, oldkeys, prop, asc) {
var test = folder ? filesOp.isFolder : filesOp.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 = filesOp.getFileData(element);
if (!e) {
e = {
href : el,
title : Messages.fm_noname,
atime : 0,
ctime : 0
};
}
if (prop === 'type') {
var hrefData = Cryptpad.parsePadUrl(e.href);
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));
// File
var $element2 = $('<li>', {
'class': 'cp-app-drive-new-upload cp-app-drive-element-row ' +
'cp-app-drive-element-grid'
}).prepend(getIcon('file')).appendTo($container);
$element2.append($('<span>', {'class': 'cp-app-drive-new-name'})
.text(Messages.uploadButton));
}
// 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) {
var isInRoot = currentPath[0] === ROOT;
var $element = $('<li>', {
'class': 'cp-app-drive-element-row cp-app-drive-element-grid cp-app-drive-new-ghost'
}).prepend($addIcon.clone()).appendTo($list);
$element.append($('<span>', {'class': 'cp-app-drive-element-name'})
.text(Messages.fm_newFile));
$element.attr('title', Messages.fm_newFile);
$element.click(function () {
var $modal = Cryptpad.createModal({
id: 'cp-app-drive-new-ghost-dialog',
$body: $('body')
});
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 createToolbar = function () {
var $toolbar = $driveToolbar;
$toolbar.html('');
$('<div>', {'class': 'cp-app-drive-toolbar-leftside'}).appendTo($toolbar);
$('<div>', {'class': 'cp-app-drive-path cp-unselectable'}).appendTo($toolbar);
var $rightside = $('<div>', {'class': 'cp-app-drive-toolbar-rightside'})
.appendTo($toolbar);
var $hist = common.createButton('history', true, {histConfig: APP.histConfig});
$rightside.append($hist);
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(false);
$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 = filesOp.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 = filesOp.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-row' + roClass,
draggable: draggable
});
if (Array.isArray(APP.selectedFiles)) {
var sidx = APP.selectedFiles.indexOf(id);
if (sidx !== -1) {
$element.addClass('cp-app-drive-element-selected');
APP.selectedFiles.splice(sidx, 1);
}
}
addFileData(id, $element);
$element.prepend($icon).dblclick(function () {
openFile(id);
});
var path = [rootName, idx];
$element.data('path', path);
$element.click(function(e) {
e.stopPropagation();
onElementClick(e, $element, path);
});
$element.contextmenu(openDefaultContextMenu);
$element.data('context', $defaultContextMenu);
if (draggable) {
addDragAndDropHandlers($element, path, false, false);
}
$container.append($element);
});
createGhostIcon($container);
};
var displayAllFiles = function ($container) {
var allfiles = files[FILES_DATA];
if (allfiles.length === 0) { return; }
var $fileHeader = getFileListHeader(false);
$container.append($fileHeader);
var keys = filesOp.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 = filesOp.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
});
addFileData(id, $element);
$element.data('path', [FILES_DATA, id]);
$element.data('element', id);
$element.prepend($icon).dblclick(function () {
openFile(id);
});
$element.click(function(e) {
e.stopPropagation();
onElementClick(e, $element);
});
$element.contextmenu(openDefaultContextMenu);
$element.data('context', $defaultContextMenu);
$container.append($element);
});
};
var displayTrashRoot = function ($list, $folderHeader, $fileHeader) {
var filesList = [];
var root = files[TRASH];
// 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 (!filesOp.isFile(el.element) && !filesOp.isFolder(el.element)) { return; }
var spath = [key, idx, 'element'];
filesList.push({
element: el.element,
spath: spath,
name: key
});
});
});
var sortedFolders = sortTrashElements(true, filesList, null, !getSortFolderDesc());
var sortedFiles = sortTrashElements(false, filesList, APP.store[SORT_FILE_BY], !getSortFileDesc());
if (filesOp.hasSubfolder(root, true)) { $list.append($folderHeader); }
sortedFolders.forEach(function (f) {
var $element = createElement([TRASH], f.spath, root, true);
$list.append($element);
});
if (filesOp.hasFile(root, true)) { $list.append($fileHeader); }
sortedFiles.forEach(function (f) {
var $element = createElement([TRASH], f.spath, root, false);
$list.append($element);
});
};
var displaySearch = function ($list, value) {
var filesList = filesOp.search(value);
filesList.forEach(function (r) {
r.paths.forEach(function (path) {
var href = r.data.href;
var parsed = Cryptpad.parsePadUrl(href);
var $table = $('<table>');
var $icon = $('<td>', {'rowspan': '3', 'class': 'cp-app-drive-search-icon'})
.append(getFileIcon(href));
var $title = $('<td>', {
'class': 'cp-app-drive-search-col1 cp-app-drive-search-title'
}).text(r.data.title)
.click(function () {
openFile(null, r.data.href);
});
var $typeName = $('<td>', {'class': 'cp-app-drive-search-label2'})
.text(Messages.fm_type);
var $type = $('<td>', {'class': 'cp-app-drive-search-col2'})
.text(Messages.type[parsed.type] || parsed.type);
var $atimeName = $('<td>', {'class': 'cp-app-drive-search-label2'})
.text(Messages.fm_lastAccess);
var $atime = $('<td>', {'class': 'cp-app-drive-search-col2'})
.text(new Date(r.data.atime).toLocaleString());
var $ctimeName = $('<td>', {'class': 'cp-app-drive-search-label2'})
.text(Messages.fm_creation);
var $ctime = $('<td>', {'class': 'cp-app-drive-search-col2'})
.text(new Date(r.data.ctime).toLocaleString());
if (filesOp.isPathIn(path, ['hrefArray'])) {
path.pop();
path.push(r.data.title);
}
var $path = $('<td>', {
'class': 'cp-app-drive-search-col1 cp-app-drive-search-path'
});
createTitle($path, path, true);
var parentPath = path.slice();
var $a;
if (parentPath) {
$a = $('<a>').text(Messages.fm_openParent).click(function (e) {
e.preventDefault();
if (filesOp.isInTrashRoot(parentPath)) { parentPath = [TRASH]; }
else { parentPath.pop(); }
APP.selectedFiles = [r.id];
APP.displayDirectory(parentPath);
});
}
var $openDir = $('<td>', {'class': 'cp-app-drive-search-opendir'}).append($a);
// rows 1-3
$('<tr>').append($icon).append($title).append($typeName).append($type).appendTo($table);
$('<tr>').append($path).append($atimeName).append($atime).appendTo($table);
$('<tr>').append($openDir).append($ctimeName).append($ctime).appendTo($table);
$('<li>', {'class':'cp-app-drive-search-result'}).append($table).appendTo($list);
});
});
};
var displayRecent = function ($list) {
var filesList = filesOp.getRecentPads();
var limit = 20;
var i = 0;
filesList.forEach(function (id) {
if (i >= limit) { return; }
// Check path (pad exists and not in trash)
var paths = filesOp.findFile(id);
if (!paths.length) { return; }
var path = paths[0];
if (filesOp.isPathIn(path, [TRASH])) { return; }
// Display the pad
var file = filesOp.getFileData(id);
if (!file) {
//debug("Unsorted or template returns an element not present in filesData: ", href);
file = { title: Messages.fm_noname };
//return;
}
var $icon = getFileIcon(id);
var ro = filesOp.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-row' + roClass,
});
addFileData(id, $element);
$element.prepend($icon).dblclick(function () {
openFile(id);
});
$element.data('path', path);
$element.click(function(e) {
e.stopPropagation();
onElementClick(e, $element, path);
});
$element.contextmenu(openDefaultContextMenu);
$element.data('context', $defaultContextMenu);
/*if (draggable) {
addDragAndDropHandlers($element, path, false, false);
}*/
$list.append($element);
i++;
});
};
// Display the selected directory into the content part (rightside)
// NOTE: Elements in the trash are not using the same storage structure as the others
// _WORKGROUP_ : do not change the lastOpenedFolder value in localStorage
var displayDirectory = APP.displayDirectory = function (path, force) {
APP.hideMenu();
if (!APP.editable) { debug("Read-only mode"); }
if (!appStatus.isReady && !force) { return; }
// Only Trash and Root are available in not-owned files manager
if (!path || displayedCategories.indexOf(path[0]) === -1) {
log(Messages.categoryError);
currentPath = [ROOT];
displayDirectory(currentPath);
return;
}
appStatus.ready(false);
currentPath = path;
var s = $content.scrollTop() || 0;
$content.html("");
sel.$selectBox = $('<div>', {'class': 'cp-app-drive-content-select-box'})
.appendTo($content);
if (!path || path.length === 0) {
path = [ROOT];
}
var isInRoot = filesOp.isPathIn(path, [ROOT]);
var inTrash = filesOp.isPathIn(path, [TRASH]);
var isTrashRoot = filesOp.comparePath(path, [TRASH]);
var isTemplate = filesOp.comparePath(path, [TEMPLATE]);
var isAllFiles = filesOp.comparePath(path, [FILES_DATA]);
var isSearch = path[0] === SEARCH;
var isRecent = path[0] === RECENT;
var isVirtual = virtualCategories.indexOf(path[0]) !== -1;
var root = isVirtual ? undefined : filesOp.find(path);
if (!isVirtual && typeof(root) === "undefined") {
log(Messages.fm_unknownFolderError);
debug("Unable to locate the selected directory: ", path);
var parentPath = path.slice();
parentPath.pop();
displayDirectory(parentPath, true);
return;
}
if (!isSearch) { delete APP.Search.oldLocation; }
APP.resetTree();
if (displayedCategories.indexOf(SEARCH) !== -1 && $tree.find('#cp-app-drive-tree-search-input').length) {
// in history mode we want to focus the version number input
if (!history.isHistoryMode && !APP.mobile()) {
var st = $tree.scrollTop() || 0;
$tree.find('#cp-app-drive-tree-search-input').focus();
$tree.scrollTop(st);
}
$tree.find('#cp-app-drive-tree-search-input')[0].selectionStart = getSearchCursor();
$tree.find('#cp-app-drive-tree-search-input')[0].selectionEnd = getSearchCursor();
}
if (!isWorkgroup()) {
setLastOpenedFolder(path);
}
var $toolbar = createToolbar(path);
var $info = createInfoBox(path);
var $dirContent = $('<div>', {id: FOLDER_CONTENT_ID});
$dirContent.data('path', path);
if (!isSearch) {
var mode = getViewMode();
if (mode) {
$dirContent.addClass(getViewModeClass());
}
createViewModeButton($toolbar.find('.cp-app-drive-toolbar-rightside'));
}
if (inTrash) {
createEmptyTrashButton($toolbar.find('.cp-app-drive-toolbar-rightside'));
}
var $list = $('<ul>').appendTo($dirContent);
// NewButton can be undefined if we're in read only mode
createNewButton(isInRoot, $toolbar.find('.cp-app-drive-toolbar-leftside'));
createTitle($toolbar.find('.cp-app-drive-path'), path);
if (APP.mobile()) {
var $context = $('<button>', {
'class': 'cp-dropdown-container',
id: 'cp-app-drive-toolbar-context-mobile'
});
$context.append($('<span>', {'class': 'fa fa-caret-down'}));
$context.appendTo($toolbar.find('.cp-app-drive-toolbar-rightside'));
$context.click(function (e) {
e.preventDefault();
e.stopPropagation();
var $li = $content.find('.cp-app-drive-element-selected');
if ($li.length !== 1) {
$li = findDataHolder($tree.find('.cp-app-drive-element-active'));
}
// Close if already opened
if ($('.cp-app-drive-context:visible').length) {
APP.hideMenu();
return;
}
// Open the menu
$('.cp-app-drive-context').css({
top: ($context.offset().top + 32) + 'px',
right: '0px',
left: ''
});
$li.contextmenu();
});
} else {
var $contextButtons = $('<span>', {'id' : 'cp-app-drive-toolbar-contextbuttons'});
$contextButtons.appendTo($toolbar.find('.cp-app-drive-toolbar-rightside'));
}
updateContextButton();
var $folderHeader = getFolderListHeader();
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 (isRecent) {
displayRecent($list);
} else {
$dirContent.contextmenu(openContentContextMenu);
if (filesOp.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 (filesOp.isFile(root[key])) { return; }
var $element = createElement(path, key, root, true);
$element.appendTo($list);
});
if (filesOp.hasFile(root)) { $list.append($fileHeader); }
// display files
sortedFiles.forEach(function (key) {
if (filesOp.isFolder(root[key])) { return; }
var $element = createElement(path, key, root, false);
$element.appendTo($list);
});
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);
}
});
$content.scrollTop(s);
appStatus.ready(true);
};
var createTreeElement = function (name, $icon, path, draggable, droppable, collapsable, active) {
var $name = $('<span>', { 'class': 'cp-app-drive-element' }).text(name);
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();
APP.displayDirectory(path);
});
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');
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');
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 (filesOp.isSubpath(currentPath, path)) {
displayDirectory(path);
}
}
});
if (wasFolderOpened(path) ||
(filesOp.isSubpath(currentPath, path) && path.length < currentPath.length)) {
$collapse.click();
}
}
$elementRow.data('path', path);
addDragAndDropHandlers($elementRow, path, true, droppable);
if (active) {
$elementRow.addClass('cp-app-drive-element-active cp-leftside-active');
}
return $element;
};
var createTree = function ($container, path) {
var root = filesOp.find(path);
// don't try to display what doesn't exist
if (!root) { return; }
// Display the root element in the tree
var displayingRoot = filesOp.comparePath([ROOT], path);
if (displayingRoot) {
var isRootOpened = filesOp.comparePath([ROOT], currentPath);
var $rootIcon = filesOp.isFolderEmpty(files[ROOT]) ?
(isRootOpened ? $folderOpenedEmptyIcon : $folderEmptyIcon) :
(isRootOpened ? $folderOpenedIcon : $folderIcon);
var $rootElement = createTreeElement(ROOT_NAME, $rootIcon.clone(), [ROOT], false, true, true, isRootOpened);
if (!filesOp.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(openDirectoryContextMenu);
$('<ul>', {'class': 'cp-app-drive-tree-docs'})
.append($rootElement).appendTo($container);
$container = $rootElement;
} else if (filesOp.isFolderEmpty(root)) { return; }
// Display root content
var $list = $('<ul>').appendTo($container);
var keys = Object.keys(root).sort();
keys.forEach(function (key) {
// Do not display files in the menu
if (!filesOp.isFolder(root[key])) { return; }
var newPath = path.slice();
newPath.push(key);
var isCurrentFolder = filesOp.comparePath(newPath, currentPath);
var isEmpty = filesOp.isFolderEmpty(root[key]);
var subfolder = filesOp.hasSubfolder(root[key]);
var $icon = isEmpty ?
(isCurrentFolder ? $folderOpenedEmptyIcon : $folderEmptyIcon) :
(isCurrentFolder ? $folderOpenedIcon : $folderIcon);
var $element = createTreeElement(key, $icon.clone(), newPath, true, true, subfolder, isCurrentFolder);
$element.appendTo($list);
$element.find('>.cp-app-drive-element-row').contextmenu(openDirectoryContextMenu);
createTree($element, newPath);
});
};
var createTemplate = function ($container, path) {
var $icon = $templateIcon.clone();
var isOpened = filesOp.comparePath(path, currentPath);
var $element = createTreeElement(TEMPLATE_NAME, $icon, [TEMPLATE], false, true, false, isOpened);
$element.addClass('cp-app-drive-tree-root');
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
$container.append($list);
};
var createAllFiles = function ($container, path) {
var $icon = $unsortedIcon.clone();
var isOpened = filesOp.comparePath(path, currentPath);
var $allfilesElement = createTreeElement(FILES_DATA_NAME, $icon, [FILES_DATA], false, false, false, isOpened);
$allfilesElement.addClass('root');
var $allfilesList = $('<ul>', { 'class': 'cp-app-drive-tree-category' })
.append($allfilesElement);
$container.append($allfilesList);
};
var createTrash = function ($container, path) {
var $icon = filesOp.isFolderEmpty(files[TRASH]) ? $trashEmptyIcon.clone() : $trashIcon.clone();
var isOpened = filesOp.comparePath(path, currentPath);
var $trashElement = createTreeElement(TRASH_NAME, $icon, [TRASH], false, true, false, isOpened);
$trashElement.addClass('root');
$trashElement.find('>.cp-app-drive-element-row').contextmenu(openTrashTreeContextMenu);
var $trashList = $('<ul>', { 'class': 'cp-app-drive-tree-category' })
.append($trashElement);
$container.append($trashList);
};
var createRecent = function ($container, path) {
var $icon = $recentIcon.clone();
var isOpened = filesOp.comparePath(path, currentPath);
var $element = createTreeElement(RECENT_NAME, $icon, [RECENT], false, false, false, isOpened);
$element.addClass('root');
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
$container.append($list);
};
var search = APP.Search = {};
var createSearch = function ($container) {
var isInSearch = currentPath[0] === SEARCH;
var $div = $('<div>', {'id': 'cp-app-drive-tree-search', 'class': 'cp-unselectable'});
var $input = $('<input>', {
id: 'cp-app-drive-tree-search-input',
type: 'text',
draggable: false,
tabindex: 1,
placeholder: Messages.fm_searchPlaceholder
}).keyup(function (e) {
if (search.to) { window.clearTimeout(search.to); }
if ([38, 39, 40, 41].indexOf(e.which) !== -1) {
if (!$input.val()) {
$input.blur();
$content.focus();
return;
} else {
e.stopPropagation();
}
}
var isInSearchTmp = currentPath[0] === SEARCH;
if ($input.val().trim() === "") {
setSearchCursor(0);
if (search.oldLocation && search.oldLocation.length) { displayDirectory(search.oldLocation); }
return;
}
if (e.which === 13) {
if (!isInSearchTmp) { search.oldLocation = currentPath.slice(); }
var newLocation = [SEARCH, $input.val()];
setSearchCursor();
if (!filesOp.comparePath(newLocation, currentPath.slice())) { displayDirectory(newLocation); }
return;
}
if (APP.mobile()) { return; }
search.to = window.setTimeout(function () {
if (!isInSearchTmp) { search.oldLocation = currentPath.slice(); }
var newLocation = [SEARCH, $input.val()];
setSearchCursor();
if (!filesOp.comparePath(newLocation, currentPath.slice())) { displayDirectory(newLocation); }
}, 500);
}).appendTo($div);
$searchIcon.clone().appendTo($div);
if (isInSearch) { $input.val(currentPath[1] || ''); }
$container.append($div);
};
APP.resetTree = function () {
var $categories = $tree.find('.cp-app-drive-tree-categories-container');
var s = $categories.scrollTop() || 0;
$tree.html('');
if (displayedCategories.indexOf(SEARCH) !== -1) { createSearch($tree); }
var $div = $('<div>', {'class': 'cp-app-drive-tree-categories-container'})
.appendTo($tree);
if (displayedCategories.indexOf(RECENT) !== -1) { createRecent($div, [RECENT]); }
if (displayedCategories.indexOf(ROOT) !== -1) { createTree($div, [ROOT]); }
if (displayedCategories.indexOf(TEMPLATE) !== -1) { createTemplate($div, [TEMPLATE]); }
if (displayedCategories.indexOf(FILES_DATA) !== -1) { createAllFiles($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 () {
$contextMenu.hide();
$trashTreeContextMenu.hide();
$trashContextMenu.hide();
$contentContextMenu.hide();
$defaultContextMenu.hide();
$('.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();
};
var getReadOnlyUrl = APP.getRO = function (id) {
if (!filesOp.isFile(id)) { return; }
var data = filesOp.getFileData(id);
if (!data) { return; }
var parsed = Cryptpad.parsePadUrl(data.href);
if (parsed.hashData.type !== "pad") { return; }
var i = data.href.indexOf('#') + 1;
var base = APP.origin + data.href.slice(0, i);
var hrefsecret = Cryptpad.getSecrets(parsed.type, parsed.hash);
if (!hrefsecret.keys) { return; }
var viewHash = Cryptpad.getViewHashFromKeys(hrefsecret.channel, hrefsecret.keys);
return base + viewHash;
};
// Disable middle click in the context menu to avoid opening /drive/inner.html# in new tabs
$(window).click(function (e) {
if (!e.target || !$(e.target).parents('.cp-dropdown-content').length) { return; }
if (e.which !== 1) {
e.stopPropagation();
return false;
}
});
var getProperties = function (el, cb) {
if (!filesOp.isFile(el)) {
return void cb('NOT_FILE');
}
var ro = filesOp.isReadOnlyFile(el);
var base = APP.origin;
var $d = $('<div>');
$('<strong>').text(Messages.fc_prop).appendTo($d);
var data = filesOp.getFileData(el);
if (!data || !data.href) { return void cb(void 0, $d); }
$('<br>').appendTo($d);
if (!ro) {
$('<label>', {'for': 'cp-app-drive-prop-link'}).text(Messages.editShare).appendTo($d);
$d.append(Cryptpad.dialog.selectable(base + data.href, {
id: 'cp-app-drive-prop-link',
}));
}
var parsed = Cryptpad.parsePadUrl(data.href);
if (parsed.hashData && parsed.hashData.type === 'pad') {
var roLink = ro ? base + data.href : getReadOnlyUrl(el);
if (roLink) {
$('<label>', {'for': 'cp-app-drive-prop-rolink'}).text(Messages.viewShare).appendTo($d);
$d.append(Cryptpad.dialog.selectable(roLink, {
id: 'cp-app-drive-prop-rolink',
}));
}
}
if (data.tags && Array.isArray(data.tags)) {
$('<label>', {'for': 'cp-app-drive-prop-tags'}).text(Messages.fm_prop_tagsList).appendTo($d);
$d.append(Cryptpad.dialog.selectable(data.tags.join(', '), {
id: 'cp-app-drive-prop-tags',
}));
}
if (APP.loggedIn && AppConfig.enablePinning) {
// check the size of this file...
common.getFileSize(data.href, function (e, bytes) {
if (e) {
// there was a problem with the RPC
logError(e);
// but we don't want to break the interface.
// continue as if there was no RPC
return void cb(void 0, $d);
}
var KB = Cryptpad.bytesToKilobytes(bytes);
var formatted = Messages._getKey('formattedKB', [KB]);
$('<br>').appendTo($d);
$('<label>', {
'for': 'cp-app-drive-prop-size'
}).text(Messages.fc_sizeInKilobytes).appendTo($d);
$d.append(Cryptpad.dialog.selectable(formatted, {
id: 'cp-app-drive-prop-size',
}));
cb(void 0, $d);
});
} else {
cb(void 0, $d);
}
};
$contextMenu.on("click", "a", function(e) {
e.stopPropagation();
var paths = $(this).data('paths');
//var path = $(this).data('path');
//var $element = $(this).data('element');
if (paths.length === 0) {
log(Messages.fm_forbidden);
debug("Directory 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-delete")) {
var pathsList = [];
paths.forEach(function (p) { pathsList.push(p.path); });
moveElements(pathsList, [TRASH], false, refresh);
}
else if ($(this).hasClass('cp-app-drive-context-open')) {
paths.forEach(function (p) {
var $element = p.element;
$element.click();
$element.dblclick();
});
}
else if ($(this).hasClass('cp-app-drive-context-openro')) {
paths.forEach(function (p) {
var el = filesOp.find(p.path);
if (filesOp.isFolder(el)) { return; }
var roUrl = getReadOnlyUrl(el);
openFile(null, roUrl);
});
}
else if ($(this).hasClass('cp-app-drive-context-newfolder')) {
if (paths.length !== 1) { return; }
var onCreated = function (err, info) {
if (err) { return void logError(err); }
APP.newFolder = info.newPath;
APP.displayDirectory(paths[0].path);
};
filesOp.addFolder(paths[0].path, null, onCreated);
}
else if ($(this).hasClass("cp-app-drive-context-properties")) {
if (paths.length !== 1) { return; }
var el = filesOp.find(paths[0].path);
getProperties(el, function (e, $prop) {
if (e) { return void logError(e); }
Cryptpad.alert($prop[0], undefined, true);
});
}
APP.hideMenu();
});
$defaultContextMenu.on("click", "a", function(e) {
e.stopPropagation();
var paths = $(this).data('paths');
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-open')) {
paths.forEach(function (p) {
var $element = p.element;
$element.dblclick();
});
}
else if ($(this).hasClass('cp-app-drive-context-openro')) {
paths.forEach(function (p) {
var el = filesOp.find(p.path);
if (filesOp.isPathIn(p.path, [FILES_DATA])) { el = el.href; }
if (!el || filesOp.isFolder(el)) { return; }
var roUrl = getReadOnlyUrl(el);
openFile(null, roUrl);
});
}
else if ($(this).hasClass('cp-app-drive-context-delete')) {
var pathsList = [];
paths.forEach(function (p) { pathsList.push(p.path); });
if (!APP.loggedIn) {
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]);
if (paths.length === 1) {
msg = Messages.fm_removePermanentlyDialog;
}
Cryptpad.confirm(msg, function(res) {
$(window).focus();
if (!res) { return; }
filesOp.delete(pathsList, refresh);
});
return;
}
moveElements(pathsList, [TRASH], false, refresh);
}
else if ($(this).hasClass("cp-app-drive-context-properties")) {
if (paths.length !== 1) { return; }
var el = filesOp.find(paths[0].path);
getProperties(el, function (e, $prop) {
if (e) { return void logError(e); }
Cryptpad.alert($prop[0], undefined, true);
});
}
APP.hideMenu();
});
$contentContextMenu.on('click', 'a', function (e) {
e.stopPropagation();
var path = $(this).data('path');
var onCreated = function (err, info) {
if (err === E_OVER_LIMIT) {
return void Cryptpad.alert(Messages.pinLimitDrive, null, true);
}
if (err) {
return void Cryptpad.alert(Messages.fm_error_cantPin);
}
APP.newFolder = info.newPath;
refresh();
};
if ($(this).hasClass("cp-app-drive-context-newfolder")) {
filesOp.addFolder(path, null, onCreated);
}
else if ($(this).hasClass("cp-app-drive-context-newdoc")) {
var type = $(this).data('type') || 'pad';
sessionStorage[Cryptpad.newPadPathKey] = filesOp.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
window.open(APP.origin + '/' + type + '/');
2017-09-27 08:31:35 +00:00
}
APP.hideMenu();
});
$trashTreeContextMenu.on('click', 'a', function (e) {
e.stopPropagation();
var paths = $(this).data('paths');
if (paths.length !== 1 || !paths[0].element || !filesOp.comparePath(paths[0].path, [TRASH])) {
log(Messages.fm_forbidden);
debug("Trash tree context menu on a forbidden or unexisting element. ", paths);
return;
}
if ($(this).hasClass("cp-app-drive-context-empty")) {
Cryptpad.confirm(Messages.fm_emptyTrashDialog, function(res) {
if (!res) { return; }
filesOp.emptyTrash(refresh);
});
}
APP.hideMenu();
});
$trashContextMenu.on('click', 'a', function (e) {
e.stopPropagation();
var paths = $(this).data('paths');
if (paths.length === 0) {
log(Messages.fm_forbidden);
debug("Trash context menu on a forbidden or unexisting element. ", paths);
return;
}
var path = paths[0].path;
var name = paths[0].path[paths[0].path.length - 1];
if ($(this).hasClass("cp-app-drive-context-remove")) {
if (paths.length === 1) {
Cryptpad.confirm(Messages.fm_removePermanentlyDialog, function(res) {
if (!res) { return; }
filesOp.delete([path], refresh);
});
return;
}
var pathsList = [];
paths.forEach(function (p) { pathsList.push(p.path); });
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]);
Cryptpad.confirm(msg, function(res) {
if (!res) { return; }
filesOp.delete(pathsList, refresh);
});
}
else if ($(this).hasClass("cp-app-drive-context-restore")) {
if (paths.length !== 1) { return; }
if (path.length === 4) {
var el = filesOp.find(path);
if (filesOp.isFile(el)) {
name = filesOp.getTitle(el);
} else {
name = path[1];
}
}
Cryptpad.confirm(Messages._getKey("fm_restoreDialog", [name]), function(res) {
if (!res) { return; }
filesOp.restore(path, refresh);
});
}
else if ($(this).hasClass("cp-app-drive-context-properties")) {
if (paths.length !== 1 || path.length !== 4) { return; }
var element = filesOp.find(path.slice(0,3)); // element containing the oldpath
var sPath = stringifyPath(element.path);
Cryptpad.alert('<strong>' + Messages.fm_originalPath + "</strong>:<br>" + sPath, undefined, true);
}
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 (sel.down) { return; }
if (e.which !== 1) { return ; }
APP.hideMenu(e);
//removeSelected(e);
});
$appContainer.on('click', function (e) {
if (e.which !== 1) { return ; }
removeInput();
hideNewButton();
});
$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 (filesOp.isPathIn(currentPath, [FILES_DATA]) && APP.loggedIn) {
return; // We can't remove elements directly from filesData
}
var $selected = $('.cp-app-drive-element-selected');
if (!$selected.length) { return; }
var paths = [];
var isTrash = filesOp.isPathIn(currentPath, [TRASH]);
$selected.each(function (idx, elmt) {
if (!$(elmt).data('path')) { return; }
paths.push($(elmt).data('path'));
});
// If we are in the trash or anon pad or if we are holding the "shift" key, delete permanently,
if (!APP.loggedIn || isTrash || e.shiftKey) {
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]);
if (paths.length === 1) {
msg = Messages.fm_removePermanentlyDialog;
}
Cryptpad.confirm(msg, function(res) {
$(window).focus();
if (!res) { return; }
filesOp.delete(paths, refresh);
});
return;
}
// else move to trash
moveElements(paths, [TRASH], false, refresh);
}
});
$appContainer.contextmenu(function () {
APP.hideMenu();
return false;
});
var onRefresh = {
refresh: function() {
if (onRefresh.to) {
window.clearTimeout(onRefresh.to);
}
onRefresh.to = window.setTimeout(refresh, 500);
}
};
proxy.on('change', [], function () {
if (history.isHistoryMode) { return; }
var path = arguments[2];
if (path[0] !== 'drive') { return false; }
path = path.slice(1);
var cPath = currentPath.slice();
if ((filesOp.isPathIn(cPath, ['hrefArray', TRASH]) && cPath[0] === path[0]) ||
(path.length >= cPath.length && filesOp.isSubpath(path, cPath))) {
// Reload after a few ms to make sure all the change events have been received
onRefresh.refresh();
} else if (path.length && path[0] === FILES_DATA) {
onRefresh.refresh();
}
APP.resetTree();
return false;
}).on('remove', [], function () {
if (history.isHistoryMode) { return; }
var path = arguments[1];
if (path[0] !== 'drive') { return false; }
path = path.slice(1);
var cPath = currentPath.slice();
if ((filesOp.isPathIn(cPath, ['hrefArray', TRASH]) && cPath[0] === path[0]) ||
(path.length >= cPath.length && filesOp.isSubpath(path, cPath))) {
// Reload after a few to make sure all the change events have been received
onRefresh.to = window.setTimeout(refresh, 500);
}
APP.resetTree();
return false;
}).on('change', ['drive', 'migrate'], function () {
var path = arguments[2];
var value = arguments[1];
if (path[1] === "migrate" && value === 1) {
if (APP.onDisconnect) { APP.onDisconnect(true); }
}
});
history.onEnterHistory = function (obj) {
var files = obj.drive;
filesOp = FO.init(files, config);
appStatus.isReady = true;
refresh();
};
history.onLeaveHistory = function () {
var files = proxy.drive;
filesOp = FO.init(files, config);
refresh();
};
var fmConfig = {
noHandlers: true,
onUploaded: function (ev, data) {
try {
// Get the folder path
console.log(ev.target);
console.log(data);
var newPath = findDropPath(ev.target);
if (!newPath) { return void refresh(); }
var href = data.url;
// Get the current file location in ROOT
var id = filesOp.getIdFromHref(href);
var paths = filesOp.findFile(id);
if (paths.length !== 1) { return; }
// Try to move and refresh
moveElements([paths[0]], newPath, true);
refresh();
} catch (e) {
console.error(e);
refresh();
}
},
body: $('body')
};
APP.FM = common.createFileManager(fmConfig);
refresh();
Cryptpad.removeLoadingScreen();
};
var setHistory = function (bool, update) {
history.isHistoryMode = bool;
setEditable(!bool);
if (!bool && update) {
history.onLeaveHistory();
}
};
var main = function () {
var common;
var proxy;
2017-09-27 08:36:16 +00:00
var readOnly;
2017-09-27 08:31:35 +00:00
nThen(function (waitFor) {
$(waitFor(function () {
Cryptpad.addLoadingScreen();
}));
window.cryptpadStore.getAll(waitFor(function (val) {
APP.store = JSON.parse(JSON.stringify(val));
}));
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (waitFor) {
var privReady = Util.once(waitFor());
var metadataMgr = common.getMetadataMgr();
if (JSON.stringify(metadataMgr.getPrivateData()) !== '{}') {
privReady();
return;
}
metadataMgr.onChange(function () {
if (typeof(metadataMgr.getPrivateData().readOnly) === 'boolean') {
readOnly = metadataMgr.getPrivateData().readOnly;
privReady();
}
});
}).nThen(function (/* waitFor */) {
APP.loggedIn = common.isLoggedIn();
APP.SFCommon = common;
if (!APP.loggedIn) { common.feedback('ANONYMOUS_DRIVE'); }
APP.$body = $('body');
APP.$bar = $('#cp-toolbar');
common.setTabTitle(Messages.type.drive);
var listmapConfig = {
data: {},
common: common,
logging: false
};
var metadataMgr;
var rt = APP.rt = Listmap.create(listmapConfig);
proxy = rt.proxy;
var onCreate = function (info) {
var realtime = APP.realtime = info.realtime;
APP.patchText = TextPatcher.create({
realtime: realtime,
logging: true,
});
metadataMgr = common.getMetadataMgr();
var configTb = {
displayed: ['useradmin', 'pageTitle', 'newpad', 'limit'],
pageTitle: Messages.type.drive,
metadataMgr: metadataMgr,
readOnly: readOnly,
realtime: info.realtime,
common: Cryptpad,
sfCommon: common,
$container: APP.$bar
};
2017-09-27 08:36:16 +00:00
var toolbar = APP.toolbar = Toolbar.create(configTb);
2017-09-27 08:31:35 +00:00
var $rightside = toolbar.$rightside;
$rightside.html(''); // Remove the drawer if we don't use it to hide the toolbar
APP.$displayName = APP.$bar.find('.' + Toolbar.constants.username);
/* add the usage */
common.createUsageBar(function (err, $limitContainer) {
if (err) { return void logError(err); }
APP.$limit = $limitContainer;
}, true);
/* add a history button */
APP.histConfig = {
onLocal: function () {
proxy.drive = history.currentObj.drive;
},
onRemote: function () {},
setHistory: setHistory,
applyVal: function (val) {
var obj = JSON.parse(val || '{}');
history.currentObj = obj;
history.onEnterHistory(obj);
},
$toolbar: APP.$bar,
};
/* TODO iframe: backup button here?
if (!readOnly && !APP.loggedIn) {
// TODO secure drive
// cryptpad-backup --> cp-toolbar-backup
var $backupButton = Cryptpad.createButton('', true).removeClass('fa').removeClass('fa-question').addClass('cryptpad-backup');
$backupButton.append($backupIcon.clone().css('marginRight', '0px'));
$backupButton.attr('title', Messages.fm_backup_title);
$backupButton.on('click', function() {
var url = window.location.origin + window.location.pathname + '#' + editHash;
var msg = Messages.fm_alert_backupUrl + '<input type="text" readonly="readonly" id="fm_backupUrl" value="'+url+'">';
Cryptpad.alert(msg, undefined, true);
$('#fm_backupUrl').val(url);
$('#fm_backupUrl').click(function () {
$(this).select();
});
});
$userBlock.append($backupButton);
}
*/
metadataMgr.onChange(function () {
var name = metadataMgr.getUserData().name || Messages.anonymous;
APP.$displayName.text(name);
});
};
var onReady = function () {
$('body').css('display', '');
APP.files = proxy;
if (!proxy.drive || typeof(proxy.drive) !== 'object') { proxy.drive = {}; }
andThen(common, proxy);
Cryptpad.removeLoadingScreen();
};
var onDisconnect = APP.onDisconnect = function (noAlert) {
setEditable(false);
if (APP.refresh) { APP.refresh(); }
APP.toolbar.failed();
if (!noAlert) { Cryptpad.alert(Messages.common_connectionLost, undefined, true); }
};
var onReconnect = function (info) {
setEditable(true);
if (APP.refresh) { APP.refresh(); }
APP.toolbar.reconnecting(info.myId);
Cryptpad.findOKButton().click();
};
proxy.on('create', function (info) {
onCreate(info);
}).on('ready', function () {
onReady();
});
proxy.on('disconnect', function () {
onDisconnect();
});
proxy.on('reconnect', function (info) {
onReconnect(info);
});
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
onConnectError();
}
});
//Cryptpad.onLogout(function () { setEditable(false); });
});
};
main();
});