Translate the context menus, Shift+Del shortcut and realtime object

This commit is contained in:
yflory 2016-11-16 17:11:48 +01:00
parent 7a035fa4a0
commit da19608269
7 changed files with 355 additions and 127 deletions

View file

@ -42,17 +42,21 @@ define(['/customize/languageSelector.js',
}); });
}; };
var translateText = function (i, e) {
var $el = $(e);
var key = $el.data('localization');
$el.html(messages[key]);
};
var translateTitle = function (i, e) {
var $el = $(this);
var key = $el.data('localization-title');
$el.attr('title', messages[key]);
};
messages._applyTranslation = function () { messages._applyTranslation = function () {
$('[data-localization]').each(function (i, e) { $('[data-localization]').each(translateText);
var $el = $(this); $('#pad-iframe').contents().find('[data-localization]').each(translateText);
var key = $el.data('localization'); $('[data-localization-title]').each(translateTitle);
$el.html(messages[key]); $('#pad-iframe').contents().find('[data-localization-title]').each(translateTitle);
});
$('[data-localization-title]').each(function (i, e) {
var $el = $(this);
var key = $el.data('localization-title');
$el.attr('title', messages[key]);
});
}; };
// Non translatable keys // Non translatable keys

View file

@ -181,7 +181,15 @@ define(function () {
out.fm_unknownFolderError = "Le dossier sélectionné ou le dernier dossier visité n'existe plus. Ouverture du dossier parent..."; out.fm_unknownFolderError = "Le dossier sélectionné ou le dernier dossier visité n'existe plus. Ouverture du dossier parent...";
out.fm_contextMenuError = "Impossible d'ouvrir le menu contextuel pour cet élément. Si le problème persiste, essayez de rechercher la page."; out.fm_contextMenuError = "Impossible d'ouvrir le menu contextuel pour cet élément. Si le problème persiste, essayez de rechercher la page.";
out.fm_selectError = "Impossible de sélectionner l'élément ciblé. Si le problème persiste, essayez de recharger la page."; out.fm_selectError = "Impossible de sélectionner l'élément ciblé. Si le problème persiste, essayez de recharger la page.";
// File - Context menu
out.fc_newfolder = "Nouveau dossier";
out.fc_rename = "Renommer";
out.fc_open = "Ouvrir";
out.fc_delete = "Supprimer";
out.fc_restore = "Restaurer";
out.fc_remove = "Supprimer définitivement";
out.fc_empty = "Vider la corbeille";
// fileObject.js (logs)
out.fo_moveUnsortedError = "La liste des éléments non triés ne peut pas contenir de dossiers."; out.fo_moveUnsortedError = "La liste des éléments non triés ne peut pas contenir de dossiers.";
out.fo_existingNameError = "Ce nom est déjà utilisé dans ce répertoire. Veuillez en choisir un autre."; out.fo_existingNameError = "Ce nom est déjà utilisé dans ce répertoire. Veuillez en choisir un autre.";
out.fo_moveFolderToChildError = "Vous ne pouvez pas déplacer un dossier dans un de ses descendants"; out.fo_moveFolderToChildError = "Vous ne pouvez pas déplacer un dossier dans un de ses descendants";

View file

@ -181,7 +181,15 @@ define(function () {
out.fm_unknownFolderError = "The selected or last visited directory no longer exist. Opening the parent folder..."; out.fm_unknownFolderError = "The selected or last visited directory no longer exist. Opening the parent folder...";
out.fm_contextMenuError = "Unable to open the context menu for that element. If the problem persist, try to reload the page."; out.fm_contextMenuError = "Unable to open the context menu for that element. If the problem persist, try to reload the page.";
out.fm_selectError = "Unable to select the targetted element. If the problem persist, try to reload the page."; out.fm_selectError = "Unable to select the targetted element. If the problem persist, try to reload the page.";
// File - Context menu
out.fc_newfolder = "New folder";
out.fc_rename = "Rename";
out.fc_open = "Open";
out.fc_delete = "Delete";
out.fc_restore = "Restore";
out.fc_remove = "Delete permanently";
out.fc_empty = "Empty the trash";
// fileObject.js (logs)
out.fo_moveUnsortedError = "You can't move a folder to the list of unsorted pads"; out.fo_moveUnsortedError = "You can't move a folder to the list of unsorted pads";
out.fo_existingNameError = "Name already used in that directory. Please choose another one."; out.fo_existingNameError = "Name already used in that directory. Please choose another one.";
out.fo_moveFolderToChildError = "You can't move a folder into one of its descendants"; out.fo_moveFolderToChildError = "You can't move a folder into one of its descendants";

View file

@ -11,6 +11,11 @@ html, body {
overflow: auto; overflow: auto;
} }
body {
display: flex;
flex-flow: row;
}
.fa { .fa {
/*min-width: 17px;*/ /*min-width: 17px;*/
margin-right: 3px; margin-right: 3px;
@ -54,15 +59,13 @@ li {
/* TREE */ /* TREE */
#tree { #tree {
position:absolute;
top: 0;
left: 0;
bottom: 0;
right: 70%;
border: 2px solid blue; border: 2px solid blue;
box-sizing: border-box; box-sizing: border-box;
background: white; background: white;
overflow: auto; overflow: auto;
resize: horizontal;
width: 30%;
white-space: nowrap;
} }
#tree li { #tree li {
@ -162,15 +165,13 @@ li {
/* CONTENT */ /* CONTENT */
#content { #content {
position: absolute;
top: 0;
left: 30%;
bottom: 0;
right: 0;
border: 2px solid green; border: 2px solid green;
box-sizing: border-box; box-sizing: border-box;
background: #eee; background: #eee;
overflow: auto; overflow: auto;
flex: 1;
display: flex;
flex-flow: column;
} }
.topButtonContainer { .topButtonContainer {
@ -190,6 +191,7 @@ li {
#folderContent { #folderContent {
padding-right: 10px; padding-right: 10px;
flex: 1;
} }
#content li:hover:not(.header) .name { #content li:hover:not(.header) .name {
@ -270,12 +272,12 @@ li {
#content div.grid .listElement { #content div.grid .listElement {
display: none; display: none;
} }
@media screen and (max-width: 1000px) { @media screen and (max-width: 1200px) {
#content .list .element span.title { #content .list .element span.title {
display: none; display: none;
} }
} }
@media screen and (min-width: 1001px) { @media screen and (min-width: 1201px) {
#content .list .element span.title { #content .list .element span.title {
display: inline; display: inline;
} }

View file

@ -1,8 +1,7 @@
define([ define([
'/customize/messages.js', '/customize/messages.js',
'/common/cryptpad-common.js',
'/bower_components/jquery/dist/jquery.min.js', '/bower_components/jquery/dist/jquery.min.js',
], function (Messages, Cryptpad) { ], function (Messages) {
var $ = window.jQuery; var $ = window.jQuery;
var module = {}; var module = {};
@ -38,19 +37,6 @@ define([
return result; return result;
}; };
var deleteFromObject = function (path) {
var parentPath = path.slice();
var key = parentPath.pop();
var parentEl = exp.findElement(files, parentPath);
if (path.length === 4 && path[0] === TRASH) {
files[TRASH][path[1]].splice(path[2], 1);
} else if (path[0] === UNSORTED) {
parentEl.splice(key, 1);
} else {
delete parentEl[key];
}
};
var isPathInRoot = exp.isPathInRoot = function (path) { var isPathInRoot = exp.isPathInRoot = function (path) {
return path[0] && path[0] === ROOT; return path[0] && path[0] === ROOT;
}; };
@ -206,10 +192,55 @@ define([
return ret; return ret;
}; };
var removeFileFromRoot = function (root, href) {
if (isFile(root)) { return; }
for (var e in root) {
if (isFile(root[e])) {
if (compareFiles(href, root[e])) {
delete root[e];
}
} else {
removeFileFromRoot(root[e], href);
}
}
};
var isInTrashRoot = exp.isInTrashRoot = function (path) { var isInTrashRoot = exp.isInTrashRoot = function (path) {
return path[0] === TRASH && path.length === 4; return path[0] === TRASH && path.length === 4;
}; };
var checkDeletedFiles = function () {
var rootFiles = getRootFiles().slice();
var unsortedFiles = getUnsortedFiles().slice();
var trashFiles = getTrashFiles().slice();
var toRemove = [];
Object.keys(files[FILES_DATA]).forEach(function (f) {
if (rootFiles.indexOf(f) === -1
&& unsortedFiles.indexOf(f) === -1
&& trashFiles.indexOf(f) === -1) {
toRemove.push(f);
}
});
toRemove.forEach(function (f) {
debug("Removing", f, "from filesData");
delete files[FILES_DATA][f];
});
};
var deleteFromObject = exp.deletePathPermanently = function (path) {
var parentPath = path.slice();
var key = parentPath.pop();
var parentEl = exp.findElement(files, parentPath);
if (path.length === 4 && path[0] === TRASH) {
files[TRASH][path[1]].splice(path[2], 1);
} else if (path[0] === UNSORTED) {
parentEl.splice(key, 1);
} else {
delete parentEl[key];
}
checkDeletedFiles();
};
// Find an element in a object following a path, resursively // Find an element in a object following a path, resursively
var findElement = exp.findElement = function (root, pathInput) { var findElement = exp.findElement = function (root, pathInput) {
if (!pathInput) { if (!pathInput) {
@ -237,7 +268,7 @@ define([
}; };
// Data from filesData // Data from filesData
var getTitle = function (href) { var getTitle = exp.getTitle = function (href) {
if (!href || !files[FILES_DATA][href]) { if (!href || !files[FILES_DATA][href]) {
error("getTitle called with a non-existing href: ", href); error("getTitle called with a non-existing href: ", href);
return; return;
@ -272,7 +303,7 @@ define([
parentPath.pop(); parentPath.pop();
pushToTrash(name, element, parentPath); pushToTrash(name, element, parentPath);
if (!keepOld) { deleteFromObject(path); } if (!keepOld) { deleteFromObject(path); }
if(cb) { cb(); } if (cb) { cb(); }
}; };
var moveElement = exp.moveElement = function (elementPath, newParentPath, cb, keepOld) { var moveElement = exp.moveElement = function (elementPath, newParentPath, cb, keepOld) {
@ -377,24 +408,6 @@ define([
}; };
var checkDeletedFiles = function () {
var rootFiles = getRootFiles().slice();
var unsortedFiles = getUnsortedFiles().slice();
var trashFiles = getTrashFiles().slice();
var toRemove = [];
Object.keys(files[FILES_DATA]).forEach(function (f) {
if (rootFiles.indexOf(f) === -1
&& unsortedFiles.indexOf(f) === -1
&& trashFiles.indexOf(f) === -1) {
toRemove.push(f);
}
});
toRemove.forEach(function (f) {
debug("Removing", f, "from filesData");
delete files[FILES_DATA][f];
});
};
// Remove an element from the trash root // Remove an element from the trash root
var removeFromTrashArray = function (element, name) { var removeFromTrashArray = function (element, name) {
var array = files[TRASH][name]; var array = files[TRASH][name];
@ -490,15 +503,41 @@ define([
cb(); cb();
}; };
var forgetPad = exp.forgetPad = function (href) {
var rootFiles = getRootFiles().slice();
if (rootFiles.indexOf(href) !== -1) {
removeFileFromRoot(files[ROOT], href);
}
var unsortedIdx = getUnsortedFiles().indexOf(href);
if (unsortedIdx !== -1) {
files[UNSORTED].splice(unsortedIdx, 1);
}
var key = getTitle(href);
pushToTrash(key, href, [UNSORTED]);
};
var addPad = exp.addPad = function (href, data) {
if (Object.keys(files[FILES_DATA]).indexOf(href) === -1) {
files[FILES_DATA][href] = data;
}
var unsortedFiles = getUnsortedFiles().slice();
var rootFiles = getRootFiles().slice();
//var trashFiles = getTrashFiles().slice();
if (unsortedFiles.indexOf(href) === -1 && rootFiles.indexOf(href) === -1) {
files[UNSORTED].push(href);
}
};
var fixFiles = exp.fixFiles = function () { var fixFiles = exp.fixFiles = function () {
// Explore the tree and check that everything is correct: // Explore the tree and check that everything is correct:
// * 'root', 'trash' and 'filesData' exist and are objects // * 'root', 'trash' and 'filesData' exist and are objects
// * Folders are objects // * ROOT: Folders are objects, files are href
// * Files are href // * TRASH: Trash root contains only arrays, each element of the array is an object {element:.., path:..}
// * Trash root contains only arrays, each element of the array is an object {element:.., path:..} // * FILES_DATA: - Data (title, cdate, adte) are stored in filesData. filesData contains only href keys linking to object with title, cdate, adate.
// * Data (title, cdate, adte) are stored in filesData. filesData contains only href keys linking to object with title, cdate, adate. // - Dates (adate, cdate) can be parsed/formatted
// * Dates (adate, cdate) can be parsed/formatted // - All files in filesData should be either in 'root', 'trash' or 'unsorted'. If that's not the case, copy the fily to 'unsorted'
// * All files in filesData should be either in 'root', 'trash' or 'unsorted'. If that's not the case, copy the fily to 'unsorted' // * UNSORTED: Contains only files (href), and does not contains files that are in ROOT
debug("Cleaning file system..."); debug("Cleaning file system...");
// Create a backup // Create a backup
@ -511,6 +550,7 @@ define([
if (typeof(files[TRASH]) !== "object") { debug("TRASH was not an object"); files[TRASH] = {}; } if (typeof(files[TRASH]) !== "object") { debug("TRASH was not an object"); files[TRASH] = {}; }
if (typeof(files[FILES_DATA]) !== "object") { debug("FILES_DATA was not an object"); files[FILES_DATA] = {}; } if (typeof(files[FILES_DATA]) !== "object") { debug("FILES_DATA was not an object"); files[FILES_DATA] = {}; }
if (!$.isArray(files[UNSORTED])) { debug("UNSORTED was not an array"); files[UNSORTED] = []; } if (!$.isArray(files[UNSORTED])) { debug("UNSORTED was not an array"); files[UNSORTED] = []; }
var fixRoot = function (element) { var fixRoot = function (element) {
for (var el in element) { for (var el in element) {
if (!isFile(element[el]) && !isFolder(element[el])) { if (!isFile(element[el]) && !isFolder(element[el])) {
@ -522,6 +562,7 @@ define([
} }
}; };
fixRoot(files[ROOT]); fixRoot(files[ROOT]);
var fixTrashRoot = function (tr) { var fixTrashRoot = function (tr) {
var toClean; var toClean;
var addToClean = function (obj, idx) { var addToClean = function (obj, idx) {
@ -545,19 +586,20 @@ define([
fixTrashRoot(files[TRASH]); fixTrashRoot(files[TRASH]);
var fixUnsorted = function (us) { var fixUnsorted = function (us) {
var rootFiles = getRootFiles().slice();
var toClean = []; var toClean = [];
us.forEach(function (el, idx) { us.forEach(function (el, idx) {
if (!isFile(el)) { if (!isFile(el) || rootFiles.indexOf(el) !== -1) {
toClean.push(idx); toClean.push(idx);
} }
}); });
}; };
fixUnsorted(files[UNSORTED]); fixUnsorted(files[UNSORTED]);
var rootFiles = getRootFiles().slice();
var unsortedFiles = getUnsortedFiles().slice();
var trashFiles = getTrashFiles().slice();
var fixFilesData = function (fd) { var fixFilesData = function (fd) {
var rootFiles = getRootFiles().slice();
var unsortedFiles = getUnsortedFiles().slice();
var trashFiles = getTrashFiles().slice();
for (var el in fd) { for (var el in fd) {
if (typeof(fd[el]) !== "object") { if (typeof(fd[el]) !== "object") {
debug("An element in filesData was not an object.", fd[el]); debug("An element in filesData was not an object.", fd[el]);
@ -578,12 +620,11 @@ define([
var backup = JSON.parse(localStorage.oldFileSystem); var backup = JSON.parse(localStorage.oldFileSystem);
backup.push(files); backup.push(files);
localStorage.oldFileSystem = JSON.stringify(backup); localStorage.oldFileSystem = JSON.stringify(backup);
debug("Your file system was corrupted. It has been cleaned so that the file manager application can be used"); debug("Your file system was corrupted. It has been cleaned so that the pads you visit can be stored safely");
return; return;
} }
debug("File system was clean"); debug("File system was clean");
}; };
fixFiles();
return exp; return exp;
}; };

View file

@ -3,9 +3,9 @@
<head> <head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css"> <link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="file.css" /> <link rel="stylesheet" href="file.css" />
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
</head> </head>
<body> <body>
<div id="tree"> <div id="tree">
@ -14,20 +14,26 @@
</div> </div>
<div id="contextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;"> <div id="contextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;"> <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="#" class="open">Open</a></li> <li><a tabindex="-1" href="#" class="open" data-localization="fc_open">Open</a></li>
<li><a tabindex="-1" href="#" class="rename">Rename</a></li> <li><a tabindex="-1" href="#" class="rename" data-localization="fc_rename">Rename</a></li>
<li><a tabindex="-1" href="#" class="delete">Delete</a></li> <li><a tabindex="-1" href="#" class="delete" data-localization="fc_delete">Delete</a></li>
<li><a tabindex="-1" href="#" class="newfolder" data-localization="fc_newfolder">New folder</a></li>
</ul>
</div>
<div id="contentContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="#" class="newfolder" data-localization="fc_newfolder">New folder</a></li>
</ul> </ul>
</div> </div>
<div id="trashTreeContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;"> <div id="trashTreeContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;"> <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="#" class="empty">Empty the trash</a></li> <li><a tabindex="-1" href="#" class="empty" data-localization="fc_empty">Empty the trash</a></li>
</ul> </ul>
</div> </div>
<div id="trashContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;"> <div id="trashContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;"> <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="#" class="remove">Delete permanently</a></li> <li><a tabindex="-1" href="#" class="remove" data-localization="fc_remove">Delete permanently</a></li>
<li><a tabindex="-1" href="#" class="restore">Restore</a></li> <li><a tabindex="-1" href="#" class="restore" data-localization="fc_restore">Restore</a></li>
</ul> </ul>
</div> </div>
</body> </body>

View file

@ -1,13 +1,17 @@
require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
define([ define([
'/customize/messages.js?app=pad', '/api/config?cb=' + Math.random().toString(16).substring(2),
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.amd.js',
'/customize/messages.js?app=file',
'json.sortify', 'json.sortify',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/file/fileObject.js', '/file/fileObject.js',
'/bower_components/jquery/dist/jquery.min.js', '/bower_components/jquery/dist/jquery.min.js',
'/bower_components/bootstrap/dist/js/bootstrap.min.js', '/bower_components/bootstrap/dist/js/bootstrap.min.js',
'/customize/pad.js' '/customize/pad.js'
], function (Messages, JSONSortify, Cryptpad, FO) { ], function (Config, Listmap, Crypto, TextPatcher, Messages, JSONSortify, Cryptpad, FO) {
var module = window.MODULE = {}; var module = window.MODULE = {};
var $ = window.jQuery; var $ = window.jQuery;
@ -15,6 +19,14 @@ define([
var $iframe = $('#pad-iframe').contents(); var $iframe = $('#pad-iframe').contents();
var ifrw = $('#pad-iframe')[0].contentWindow; var ifrw = $('#pad-iframe')[0].contentWindow;
//var hash = Cryptpad.getAttribute('FS_hash', cb);
var hash = localStorage.FS_hash;
if (hash) {
window.location.hash = hash;
}
var secret = Cryptpad.getSecrets();
var ROOT = "root"; var ROOT = "root";
var ROOT_NAME = Messages.fm_rootName; var ROOT_NAME = Messages.fm_rootName;
var UNSORTED = "unsorted"; var UNSORTED = "unsorted";
@ -50,130 +62,130 @@ define([
"Dir D": { "Dir D": {
"Dir E": {}, "Dir E": {},
}, },
"File a": "#hash_a", "File a": "https://cryptpad.fr/slide/#hash_a",
"File b": "#hash_b", "File b": "https://cryptpad.fr/pad/#hash_b",
"File c": "#hash_c", "File c": "https://cryptpad.fr/pad/#hash_c",
"File d": "#hash_d", "File d": "https://cryptpad.fr/pad/#hash_d",
"File e": "#hash_e", "File e": "https://cryptpad.fr/pad/#hash_e",
"File f": "#hash_f", "File f": "https://cryptpad.fr/pad/#hash_f",
"File g": "#hash_g", "File g": "https://cryptpad.fr/pad/#hash_g",
"File h": "#hash_h", "File h": "https://cryptpad.fr/pad/#hash_h",
"File i": "#hash_i", "File i": "https://cryptpad.fr/pad/#hash_i",
"File j": "#hash_j", "File j": "https://cryptpad.fr/pad/#hash_j",
"File k": "#hash_k" "File k": "https://cryptpad.fr/pad/#hash_k"
}, },
"Dir C": {}, "Dir C": {},
"Dir B": {}, "Dir B": {},
"File A": "#hash_A" "File A": "https://cryptpad.fr/pad/#hash_A"
}, },
"Directory 2": { "Directory 2": {
"File B": "#hash_B", "File B": "https://cryptpad.fr/pad/#hash_B",
"File C": "#hash_C" "File C": "https://cryptpad.fr/pad/#hash_C"
} }
}, },
unsorted: ["#href1", "#href2", "#href3"], unsorted: ["https://cryptpad.fr/pad/#href1", "https://cryptpad.fr/pad/#href2", "https://cryptpad.fr/pad/#href3"],
filesData: { filesData: {
"#hash_a": { "https://cryptpad.fr/slide/#hash_a": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Pad A" title: "Pad A"
}, },
"#hash_b": { "https://cryptpad.fr/pad/#hash_b": {
ctime: "Mon Nov 07 2016 16:38:21 GMT+0100 (CET)", ctime: "Mon Nov 07 2016 16:38:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:38:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:38:21 GMT+0100 (CET)",
title: "Pad B" title: "Pad B"
}, },
"#hash_c": { "https://cryptpad.fr/pad/#hash_c": {
ctime: "Tue Nov 08 2016 16:34:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:34:21 GMT+0100 (CET)",
atime: "Sun Nov 06 2016 12:34:21 GMT+0100 (CET)", atime: "Sun Nov 06 2016 12:34:21 GMT+0100 (CET)",
title: "Pad C With A Very Very Very Long Title" title: "Pad C With A Very Very Very Long Title"
}, },
"#hash_e": { "https://cryptpad.fr/pad/#hash_e": {
ctime: "Tue Nov 08 2016 16:26:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:26:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:26:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:26:21 GMT+0100 (CET)",
title: "Pad E" title: "Pad E"
}, },
"#hash_f": { "https://cryptpad.fr/pad/#hash_f": {
ctime: "Tue Nov 08 2016 16:22:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:22:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:22:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:22:21 GMT+0100 (CET)",
title: "Pad F" title: "Pad F"
}, },
"#hash_g": { "https://cryptpad.fr/pad/#hash_g": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Pad A" title: "Pad A"
}, },
"#hash_h": { "https://cryptpad.fr/pad/#hash_h": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Pad A" title: "Pad A"
}, },
"#hash_i": { "https://cryptpad.fr/pad/#hash_i": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Pad A" title: "Pad A"
}, },
"#hash_j": { "https://cryptpad.fr/pad/#hash_j": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Pad A" title: "Pad A"
}, },
"#hash_k": { "https://cryptpad.fr/pad/#hash_k": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Pad A" title: "Pad A"
}, },
"#hash_Z": { "https://cryptpad.fr/pad/#hash_Z": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Code Z" title: "Code Z"
}, },
"#hash_A": { "https://cryptpad.fr/pad/#hash_A": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Code A" title: "Code A"
}, },
"#hash_B": { "https://cryptpad.fr/pad/#hash_B": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Code B" title: "Code B"
}, },
"#hash_C": { "https://cryptpad.fr/pad/#hash_C": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Code C" title: "Code C"
}, },
"#hash_1": { "https://cryptpad.fr/pad/#hash_1": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Code C" title: "Code C"
}, },
"#hash_2": { "https://cryptpad.fr/pad/#hash_2": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Code C" title: "Code C"
}, },
"#hash_3": { "https://cryptpad.fr/pad/#hash_3": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Code C" title: "Code C"
}, },
"#hash_4": { "https://cryptpad.fr/pad/#hash_4": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Code C" title: "Code C"
}, },
"#href1": { "https://cryptpad.fr/pad/#href1": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Pad unsorted 1" title: "Pad unsorted 1"
}, },
"#href2": { "https://cryptpad.fr/pad/#href2": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Pad unsorted 2" title: "Pad unsorted 2"
}, },
"#href3": { "https://cryptpad.fr/pad/#href3": {
ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)",
atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)",
title: "Pad unsorted 3" title: "Pad unsorted 3"
@ -181,7 +193,7 @@ define([
}, },
trash: { trash: {
"File Z": [{ "File Z": [{
element: "#hash_Z", element: "https://cryptpad.fr/pad/#hash_Z",
path: [ROOT] path: [ROOT]
}] }]
} }
@ -267,6 +279,7 @@ define([
var init = function (files) { var init = function (files) {
var filesOp = FO.init(files, config); var filesOp = FO.init(files, config);
filesOp.fixFiles();
var error = filesOp.error; var error = filesOp.error;
@ -277,6 +290,7 @@ define([
var $tree = $iframe.find("#tree"); var $tree = $iframe.find("#tree");
var $content = $iframe.find("#content"); var $content = $iframe.find("#content");
var $contextMenu = $iframe.find("#contextMenu"); var $contextMenu = $iframe.find("#contextMenu");
var $contentContextMenu = $iframe.find("#contentContextMenu");
var $trashTreeContextMenu = $iframe.find("#trashTreeContextMenu"); var $trashTreeContextMenu = $iframe.find("#trashTreeContextMenu");
var $trashContextMenu = $iframe.find("#trashContextMenu"); var $trashContextMenu = $iframe.find("#trashContextMenu");
var $folderIcon = $('<span>', {"class": "fa fa-folder folder", style:"color:#FEDE8B;text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;"}); var $folderIcon = $('<span>', {"class": "fa fa-folder folder", style:"color:#FEDE8B;text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;"});
@ -295,6 +309,29 @@ define([
var $listIcon = $('<span>', {"class": "fa fa-list"}); var $listIcon = $('<span>', {"class": "fa fa-list"});
var $gridIcon = $('<span>', {"class": "fa fa-th"}); var $gridIcon = $('<span>', {"class": "fa fa-th"});
var appStatus = {
isReady: true,
_onReady: [],
onReady: function (handler) {
if (isReady) {
handler();
return;
}
appStatus._onReady.push(handler);
},
ready: function (state) {
appStatus.isReady = state;
if (state) {
appStatus._onReady.forEach(function (h) {
h();
});
}
}
};
var isReady = false;
var removeSelected = function () { var removeSelected = function () {
$iframe.find('.selected').removeClass("selected"); $iframe.find('.selected').removeClass("selected");
}; };
@ -356,7 +393,8 @@ define([
removeInput(); removeInput();
} }
}); });
$input.insertAfter($element); $element.parent().append($input);
//$input.insertAfter($element);
$input.focus(); $input.focus();
$input.select(); $input.select();
// We don't want to open the file/folder when clicking on the input // We don't want to open the file/folder when clicking on the input
@ -427,6 +465,11 @@ define([
}; };
var openDirectoryContextMenu = function (e) { var openDirectoryContextMenu = function (e) {
var $element = $(e.target).closest('li');
$contextMenu.find('li').show();
if ($element.hasClass('file-element')) {
$contextMenu.find('a.newfolder').parent('li').hide();
}
openContextMenu(e, $contextMenu); openContextMenu(e, $contextMenu);
return false; return false;
}; };
@ -447,6 +490,22 @@ define([
return false; return false;
}; };
var openContentContextMenu = function (e) {
module.hideMenu();
e.stopPropagation();
var path = $(e.target).closest('#' + FOLDER_CONTENT_ID).data('path');
if (!path) { return; }
var $menu = $contentContextMenu;
removeSelected();
$menu.css({
display: "block",
left: e.pageX,
top: e.pageY
});
$menu.find('a').data('path', path);
return false;
};
// filesOp.moveElements is able to move several paths to a new location, including // filesOp.moveElements is able to move several paths to a new location, including
// the Trash or the "Unsorted files" folder // the Trash or the "Unsorted files" folder
var moveElements = function (paths, newPath, force, cb) { var moveElements = function (paths, newPath, force, cb) {
@ -555,14 +614,17 @@ define([
if (typeof(files[FILES_DATA][element]) === "undefined") { if (typeof(files[FILES_DATA][element]) === "undefined") {
return; return;
} }
var hrefData = Cryptpad.parsePadUrl(element);
var data = files[FILES_DATA][element]; var data = files[FILES_DATA][element];
var type = Messages.type[hrefData.type] || hrefData.type;
var $title = $('<span>', {'class': 'title listElement', title: data.title}).text(data.title); var $title = $('<span>', {'class': 'title listElement', title: data.title}).text(data.title);
var $type = $('<span>', {'class': 'date listElement', title: type}).text(type);
var $adate = $('<span>', {'class': 'date listElement', title: getDate(data.atime)}).text(getDate(data.atime)); var $adate = $('<span>', {'class': 'date listElement', title: getDate(data.atime)}).text(getDate(data.atime));
var $cdate = $('<span>', {'class': 'date listElement', title: getDate(data.ctime)}).text(getDate(data.ctime)); var $cdate = $('<span>', {'class': 'date listElement', title: getDate(data.ctime)}).text(getDate(data.ctime));
if (displayTitle) { if (displayTitle) {
$span.append($title); $span.append($title);
} }
$span.append($adate).append($cdate); $span.append($type).append($adate).append($cdate);
}; };
var addFolderData = function (element, key, $span) { var addFolderData = function (element, key, $span) {
@ -594,8 +656,10 @@ define([
var element = filesOp.findElement(files, newPath); var element = filesOp.findElement(files, newPath);
var $icon = $fileIcon.clone(); var $icon = $fileIcon.clone();
var spanClass = 'file-element element'; var spanClass = 'file-element element';
var liClass = 'file-item';
if (isFolder) { if (isFolder) {
spanClass = 'folder-element element'; spanClass = 'folder-element element';
liClass = 'folder-item';
$icon = filesOp.isFolderEmpty(root[key]) ? $folderEmptyIcon.clone() : $folderIcon.clone(); $icon = filesOp.isFolderEmpty(root[key]) ? $folderEmptyIcon.clone() : $folderIcon.clone();
} }
var $name = $('<span>', { 'class': spanClass }).text(key); var $name = $('<span>', { 'class': spanClass }).text(key);
@ -614,6 +678,7 @@ define([
if (isTrash) { return; } if (isTrash) { return; }
openFile(root[key]); openFile(root[key]);
}); });
$element.addClass(liClass);
$element.data('path', newPath); $element.data('path', newPath);
addDragAndDropHandlers($element, newPath, isFolder, !isTrash); addDragAndDropHandlers($element, newPath, isFolder, !isTrash);
$element.click(function(e) { $element.click(function(e) {
@ -627,9 +692,9 @@ define([
} }
var isNewFolder = module.newFolder && filesOp.comparePath(newPath, module.newFolder); var isNewFolder = module.newFolder && filesOp.comparePath(newPath, module.newFolder);
if (isNewFolder) { if (isNewFolder) {
window.setTimeout(function() { appStatus.onReady(function () {
displayRenameInput($name, newPath); window.setTimeout(function () { displayRenameInput($name, newPath); }, 0);
}, 500); });
delete module.newFolder; delete module.newFolder;
} }
return $element; return $element;
@ -752,9 +817,10 @@ define([
var $fihElement = $('<span>', {'class': 'element'}).appendTo($fileHeader); var $fihElement = $('<span>', {'class': 'element'}).appendTo($fileHeader);
var $fhName = $('<span>', {'class': 'name'}).text(Messages.fm_fileName); var $fhName = $('<span>', {'class': 'name'}).text(Messages.fm_fileName);
var $fhTitle = displayTitle ? $('<span>', {'class': 'title '}).text(Messages.fm_title) : ''; var $fhTitle = displayTitle ? $('<span>', {'class': 'title '}).text(Messages.fm_title) : '';
var $fhType = $('<span>', {'class': 'date'}).text(Messages.table_type);
var $fhAdate = $('<span>', {'class': 'date'}).text(Messages.fm_lastAccess); var $fhAdate = $('<span>', {'class': 'date'}).text(Messages.fm_lastAccess);
var $fhCdate = $('<span>', {'class': 'date'}).text(Messages.fm_creation); var $fhCdate = $('<span>', {'class': 'date'}).text(Messages.fm_creation);
$fihElement.append($fhName).append($fhTitle).append($fhAdate).append($fhCdate); $fihElement.append($fhName).append($fhTitle).append($fhType).append($fhAdate).append($fhCdate);
return $fileHeader; return $fileHeader;
}; };
@ -827,6 +893,7 @@ define([
// Display the selected directory into the content part (rightside) // Display the selected directory into the content part (rightside)
// NOTE: Elements in the trash are not using the same storage structure as the others // NOTE: Elements in the trash are not using the same storage structure as the others
var displayDirectory = module.displayDirectory = function (path) { var displayDirectory = module.displayDirectory = function (path) {
appStatus.ready(false);
currentPath = path; currentPath = path;
$content.html(""); $content.html("");
if (!path || path.length === 0) { if (!path || path.length === 0) {
@ -852,6 +919,8 @@ define([
var $title = createTitle(path); var $title = createTitle(path);
var $dirContent = $('<div>', {id: FOLDER_CONTENT_ID}); var $dirContent = $('<div>', {id: FOLDER_CONTENT_ID});
$dirContent.data('path', path);
$dirContent.contextmenu(openContentContextMenu);
var mode = getViewMode(); var mode = getViewMode();
if (mode) { if (mode) {
$dirContent.addClass(getViewModeClass()); $dirContent.addClass(getViewModeClass());
@ -891,6 +960,7 @@ define([
}); });
} }
$content.append($title).append($dirContent); $content.append($title).append($dirContent);
appStatus.ready(true);
}; };
var createTreeElement = function (name, $icon, path, draggable, collapsable, active) { var createTreeElement = function (name, $icon, path, draggable, collapsable, active) {
@ -1040,6 +1110,7 @@ define([
$contextMenu.hide(); $contextMenu.hide();
$trashTreeContextMenu.hide(); $trashTreeContextMenu.hide();
$trashContextMenu.hide(); $trashContextMenu.hide();
$contentContextMenu.hide();
}; };
$contextMenu.on("click", "a", function(e) { $contextMenu.on("click", "a", function(e) {
@ -1060,6 +1131,26 @@ define([
else if ($(this).hasClass('open')) { else if ($(this).hasClass('open')) {
$element.dblclick(); $element.dblclick();
} }
else if ($(this).hasClass('newfolder')) {
var onCreated = function (info) {
module.newFolder = info.newPath;
module.displayDirectory(path);;
};
filesOp.createNewFolder(path, null, onCreated);
}
module.hideMenu();
});
$contentContextMenu.on('click', 'a', function (e) {
e.stopPropagation();
var path = $(this).data('path');
if ($(this).hasClass("newfolder")) {
var onCreated = function (info) {
module.newFolder = info.newPath;
refresh();
};
filesOp.createNewFolder(path, null, onCreated);
}
module.hideMenu(); module.hideMenu();
}); });
@ -1127,8 +1218,8 @@ define([
$(ifrw).on('keyup', function (e) { $(ifrw).on('keyup', function (e) {
pressKey(e.which, false); pressKey(e.which, false);
}); });
$(ifrw).on('keypress', function (e) { $(ifrw).on('keydown', function (e) {
if (e.which === 0) { if (e.which === 46) {
var $selected = $iframe.find('.selected'); var $selected = $iframe.find('.selected');
if (!$selected.length) { return; } if (!$selected.length) { return; }
var paths = []; var paths = [];
@ -1136,7 +1227,14 @@ define([
if (!$(elmt).data('path')) { return; } if (!$(elmt).data('path')) { return; }
paths.push($(elmt).data('path')); paths.push($(elmt).data('path'));
}); });
if (filesOp.isPathInTrash(currentPath)) { // If we are in the trash or if we are holding the "shift" key, delete permanently,
// else move to trash
if (filesOp.isPathInTrash(currentPath) || e.shiftKey) {
var todo = filesOp.removeFromTrash;
if (!filesOp.isPathInTrash(currentPath)) {
// If we are not in the trash, we just have to remove the key from root/unsorted
todo = filesOp.deletePathPermanently;
}
// If we are already in the trash, delete the elements permanently // If we are already in the trash, delete the elements permanently
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]); var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]);
if (paths.length === 1) { if (paths.length === 1) {
@ -1146,7 +1244,7 @@ define([
} }
Cryptpad.confirm(msg, function(res) { Cryptpad.confirm(msg, function(res) {
paths.forEach(function(p) { paths.forEach(function(p) {
filesOp.removeFromTrash(p); todo(p);
}); });
refresh(); refresh();
}); });
@ -1156,6 +1254,67 @@ define([
} }
}); });
}; };
/*
initLSOpened(); initLSOpened();
init(filesObject); init(filesObject);
*/
var listmapConfig = module.config = {
data: {},
websocketURL: Cryptpad.getWebsocketURL(),
channel: secret.channel,
readOnly: false,
validateKey: secret.keys.validateKey || undefined,
crypto: Crypto.createEncryptor(secret.keys),
};
// don't initialize until the store is ready.
Cryptpad.ready(function () {
var rt = window.rt = module.rt = Listmap.create(listmapConfig);
rt.proxy.on('create', function (info) {
var realtime = module.realtime = info.realtime;
var editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
window.location.hash = editHash;
//Cryptpad.setAttribute("FS_hash", editHash, cb, store);
localStorage.FS_hash = editHash;
module.patchText = TextPatcher.create({
realtime: realtime,
logging: true,
});
/*Cryptpad.getPadTitle(function (err, title) {
title = document.title = title || info.channel.slice(0, 8);
Cryptpad.setPadTitle(title, function (err, data) {
if (err) {
console.log("unable to remember pad");
console.log(err);
return;
}
});
});*/
}).on('ready', function () {
if (JSON.stringify(rt.proxy) === '{}') {
var store = Cryptpad.getStore();
store.get(Cryptpad.storageKey, function (err, s) {
rt.proxy.filesData = s;
initLSOpened();
init(rt.proxy);
});
return;
}
initLSOpened();
init(rt.proxy);
})
.on('disconnect', function () {
//setEditable(false);
Cryptpad.alert(Messages.common_connectionLost);
});
});
}); });