require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); define([ '/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad-crypto/crypto.js', '/bower_components/textpatcher/TextPatcher.amd.js', 'json.sortify', '/common/cryptpad-common.js', '/common/userObject.js', '/common/toolbar.js', '/customize/application_config.js', '/common/cryptget.js', '/common/mergeDrive.js' ], function (Listmap, Crypto, TextPatcher, JSONSortify, Cryptpad, FO, Toolbar, AppConfig, Get, Merge) { var module = window.MODULE = {}; var Messages = Cryptpad.Messages; var $ = window.jQuery; var saveAs = window.saveAs; // Use `$(function () {});` to make sure the html is loaded before doing anything else $(function () { var $iframe = $('#pad-iframe').contents(); var ifrw = $('#pad-iframe')[0].contentWindow; Cryptpad.addLoadingScreen(); var onConnectError = function (info) { Cryptpad.errorLoadingScreen(Messages.websocketError); }; var APP = window.APP = { editable: false, Cryptpad: Cryptpad, loggedIn: Cryptpad.isLoggedIn(), mobile: function () { return $('body').width() <= 600; } // Menu and content area are not inline-block anymore for mobiles }; var stringify = APP.stringify = function (obj) { return JSONSortify(obj); }; var SEARCH = "search"; var SEARCH_NAME = Messages.fm_searchName; var ROOT = "root"; var ROOT_NAME = Messages.fm_rootName; var UNSORTED = "unsorted"; var UNSORTED_NAME = Messages.fm_unsortedName; 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 LOCALSTORAGE_LAST = "cryptpad-file-lastOpened"; var LOCALSTORAGE_OPENED = "cryptpad-file-openedFolders"; var LOCALSTORAGE_VIEWMODE = "cryptpad-file-viewMode"; var FOLDER_CONTENT_ID = "folderContent"; 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 getLastOpenedFolder = function () { var path; try { path = localStorage[LOCALSTORAGE_LAST] ? JSON.parse(localStorage[LOCALSTORAGE_LAST]) : [UNSORTED]; } catch (e) { path = [UNSORTED]; } return path; }; var setLastOpenedFolder = function (path) { if (path[0] === SEARCH) { return; } localStorage[LOCALSTORAGE_LAST] = JSON.stringify(path); }; var initLocalStorage = function () { try { var store = JSON.parse(localStorage[LOCALSTORAGE_OPENED]); if (!$.isArray(store)) { localStorage[LOCALSTORAGE_OPENED] = '[]'; } } catch (e) { localStorage[LOCALSTORAGE_OPENED] = '[]'; } }; var wasFolderOpened = function (path) { var store = JSON.parse(localStorage[LOCALSTORAGE_OPENED]); return store.indexOf(JSON.stringify(path)) !== -1; }; var setFolderOpened = function (path, opened) { var s = JSON.stringify(path); var store = JSON.parse(localStorage[LOCALSTORAGE_OPENED]); if (opened && store.indexOf(s) === -1) { store.push(s); } if (!opened) { var idx = store.indexOf(s); if (idx !== -1) { store.splice(idx, 1); } } localStorage[LOCALSTORAGE_OPENED] = JSON.stringify(store); }; var getViewModeClass = function () { var mode = localStorage[LOCALSTORAGE_VIEWMODE]; if (mode === 'list') { return 'list'; } return 'grid'; }; var getViewMode = function () { return localStorage[LOCALSTORAGE_VIEWMODE] || 'grid'; }; var setViewMode = function (mode) { if (typeof(mode) !== "string") { logError("Incorrect view mode: ", mode); return; } localStorage[LOCALSTORAGE_VIEWMODE] = mode; }; var setSearchCursor = function () { var $input = $iframe.find('#searchInput'); localStorage.searchCursor = $input[0].selectionStart; }; var getSearchCursor = function () { return localStorage.searchCursor || 0; }; var now = function () { return new Date().getTime(); }; var setEditable = function (state) { APP.editable = state; if (!state) { $iframe.find('#content').addClass('readonly'); $iframe.find('[draggable="true"]').attr('draggable', false); } else { $iframe.find('#content').removeClass('readonly'); $iframe.find('[draggable="false"]').attr('draggable', true); } }; // Icons var $folderIcon = $('', {"class": "fa fa-folder folder icon"}); var $folderEmptyIcon = $folderIcon.clone(); var $folderOpenedIcon = $('', {"class": "fa fa-folder-open folder"}); var $folderOpenedEmptyIcon = $folderOpenedIcon.clone(); var $fileIcon = $('', {"class": "fa fa-file-text-o file icon"}); var $padIcon = $('', {"class": "fa fa-file-word-o file icon"}); var $codeIcon = $('', {"class": "fa fa-file-code-o file icon"}); var $slideIcon = $('', {"class": "fa fa-file-powerpoint-o file icon"}); var $pollIcon = $('', {"class": "fa fa-calendar file icon"}); var $whiteboardIcon = $('', {"class": "fa fa-paint-brush"}); var $upIcon = $('', {"class": "fa fa-arrow-circle-up"}); var $unsortedIcon = $('', {"class": "fa fa-files-o"}); var $templateIcon = $('', {"class": "fa fa-cubes"}); var $trashIcon = $('', {"class": "fa fa-trash"}); var $trashEmptyIcon = $('', {"class": "fa fa-trash-o"}); var $collapseIcon = $('', {"class": "fa fa-minus-square-o expcol"}); var $expandIcon = $('', {"class": "fa fa-plus-square-o expcol"}); var $listIcon = $('', {"class": "fa fa-list"}); var $gridIcon = $('', {"class": "fa fa-th"}); var $sortAscIcon = $('', {"class": "fa fa-angle-up sortasc"}); var $sortDescIcon = $('', {"class": "fa fa-angle-down sortdesc"}); var $closeIcon = $('', {"class": "fa fa-window-close"}); var $backupIcon = $('', {"class": "fa fa-life-ring"}); var init = function (proxy) { var files = proxy.drive; var isOwnDrive = function () { return Cryptpad.getUserHash() === APP.hash || localStorage.FS_hash === APP.hash; }; var isWorkgroup = function () { return files.workgroup === 1; }; config.workgroup = isWorkgroup(); config.Cryptpad = Cryptpad; var filesOp = FO.init(files, config); filesOp.fixFiles(); var error = filesOp.error; var $tree = $iframe.find("#tree"); var $content = $iframe.find("#content"); var $appContainer = $iframe.find(".app-container"); var $driveToolbar = $iframe.find("#driveToolbar"); var $contextMenu = $iframe.find("#treeContextMenu"); var $contentContextMenu = $iframe.find("#contentContextMenu"); var $defaultContextMenu = $iframe.find("#defaultContextMenu"); var $trashTreeContextMenu = $iframe.find("#trashTreeContextMenu"); var $trashContextMenu = $iframe.find("#trashContextMenu"); // TOOLBAR /* add a "change username" button */ if (!APP.readOnly) { Cryptpad.getLastName(function (err, lastName) { APP.$displayName.text(lastName || Messages.anonymous); }); } else { APP.$displayName.html('' + Messages.readonly + ''); } // FILE MANAGER // _WORKGROUP_ and other people drive : display Documents as main page var currentPath = module.currentPath = isOwnDrive() ? getLastOpenedFolder() : [ROOT]; // Categories dislayed in the menu // _WORKGROUP_ : do not display unsorted var displayedCategories = [ROOT, UNSORTED, TRASH, SEARCH]; if (AppConfig.enableTemplates) { displayedCategories.push(TEMPLATE); } if (isWorkgroup()) { displayedCategories = [ROOT, TRASH, SEARCH]; } var lastSelectTime; var selectedElement; 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('.element-row') ? $el : $el.closest('.element-row'); }; var removeSelected = function () { $iframe.find('.selected').removeClass("selected"); var $container = $driveToolbar.find('#contextButtonsContainer'); if (!$container.length) { return; } $container.html(''); }; var removeInput = function (cancel) { if (!cancel && $iframe.find('.element-row > input').length === 1) { var $input = $iframe.find('.element-row > input'); filesOp.rename($input.data('path'), $input.val(), APP.refresh); } $iframe.find('.element-row > input').remove(); $iframe.find('.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) { 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 (fileEl, name) { if (name) { sessionStorage[Cryptpad.newPadNameKey] = name; } window.open(fileEl); delete sessionStorage[Cryptpad.newPadNameKey]; }; var refresh = APP.refresh = function () { module.displayDirectory(currentPath); }; // 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('.name'); if (!$name.length) { $name = $element.find('> .element'); } $name.hide(); var name = path[path.length - 1]; var $input = $('', { placeholder: name, value: name }).data('path', path); $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(); $input.select(); // 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('.element-row').attr("draggable", false); }); $input.on('mouseup', function (e) { e.stopPropagation(); $input.parents('.element-row').attr("draggable", true); }); },0); }; var filterContextMenu = function ($menu, paths) { //var path = $element.data('path'); if (!paths || paths.length === 0) { console.error('no paths'); } var hide = []; var hasFolder = false; paths.forEach(function (p, i) { var path = p.path; var $element = p.element; if (path.length === 1) { hide.push($menu.find('a.rename')); hide.push($menu.find('a.delete')); } if (!APP.editable) { hide.push($menu.find('a.editable')); } if (!isOwnDrive()) { hide.push($menu.find('a.own')); } if ($element.is('.file-element')) { // No folder in files hide.push($menu.find('a.newfolder')); if ($element.is('.readonly')) { // Keep only open readonly hide.push($menu.find('a.open')); } else if ($element.is('.noreadonly')) { // Keep only open readonly hide.push($menu.find('a.open_ro')); } } else { if (hasFolder) { // More than 1 folder selected: cannot create a new subfolder hide.push($menu.find('a.newfolder')); } hasFolder = true; hide.push($menu.find('a.open_ro')); // TODO: folder properties in the future? hide.push($menu.find('a.properties')); } // If we're in the trash, hide restore and properties for non-root elements if ($menu.find('a.restore').length && path && path.length > 4) { hide.push($menu.find('a.restore')); hide.push($menu.find('a.properties')); } }); if (paths.length > 1) { hide.push($menu.find('a.restore')); hide.push($menu.find('a.properties')); hide.push($menu.find('a.rename')); } if (hasFolder && paths.length > 1) { // Cannot open multiple folders hide.push($menu.find('a.open')); } return hide; }; var updatePathSize = function () { var $context = $iframe.find('#contextButtonsContainer'); var l = 50; if ($context.length) { l += $context.width() || 0; } $driveToolbar.find('.path').css('max-width', 'calc(100vw - '+$tree.width()+'px - '+l+'px)'); }; var getSelectedPaths = function ($element) { var paths = []; if ($iframe.find('.selected').length > 1) { var $selected = $iframe.find('.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 () { var $li = $content.find('.selected'); if ($li.length === 0) { $li = findDataHolder($tree.find('.active')); } var $button = $driveToolbar.find('#contextButton'); 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('#contextButtonsContainer'); 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 = $('