From 9a4a6e9b40cdc49d8d9dde885beca093d7810565 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 12 Jun 2017 10:17:52 +0200 Subject: [PATCH 01/99] Always display the upgrade button in the drive --- www/drive/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/drive/main.js b/www/drive/main.js index b6cdfd2c9..ee241ffed 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -2726,7 +2726,7 @@ define([ if (err) { return void logError(err); } $leftside.html(''); $leftside.append($limitContainer); - }); + }, true); /* add a history button */ var histConfig = { From 6dd80c26b18ca8b0e0bfeffe622f4bdc19d24a5f Mon Sep 17 00:00:00 2001 From: Pierre Bondoerffer Date: Mon, 12 Jun 2017 12:00:39 +0200 Subject: [PATCH 02/99] Make editing more visible --- www/poll/main.js | 10 ++++++++-- www/poll/poll.css | 8 ++++++-- www/poll/poll.less | 10 +++++++++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/www/poll/main.js b/www/poll/main.js index eb0edef26..1ff7638d5 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -107,12 +107,15 @@ define([ }; var unlockElements = function () { + var $input; APP.editable.row.forEach(function (id) { - $('input[type="text"][disabled="disabled"][data-rt-id="' + id + '"]').removeAttr('disabled'); + $input = $('input[type="text"][disabled="disabled"][data-rt-id="' + id + '"]').removeAttr('disabled'); + $input.parent().parent().addClass('editing'); $('span.edit[data-rt-id="' + id + '"]').css('visibility', 'hidden'); }); APP.editable.col.forEach(function (id) { - $('input[disabled="disabled"][data-rt-id^="' + id + '"]').removeAttr('disabled'); + $input = $('input[disabled="disabled"][data-rt-id^="' + id + '"]').removeAttr('disabled'); + $input.parent().addClass('editing'); $('input[type="checkbox"][data-rt-id^="' + id + '"]').addClass('enabled'); $('.lock[data-rt-id="' + id + '"]').addClass('fa-unlock').removeClass('fa-lock').attr('title', Messages.poll_unlocked); }); @@ -296,6 +299,7 @@ define([ $('.lock[data-rt-id!="' + APP.userid + '"]').addClass('fa-lock').removeClass('fa-unlock').attr('title', Messages.poll_locked); var $cells = APP.$table.find('thead td:not(.uncommitted), tbody td'); $cells.find('[type="text"][data-rt-id!="' + APP.userid + '"]').attr('disabled', true); + $cells.removeClass('editing'); $('.edit[data-rt-id!="' + APP.userid + '"]').css('visibility', 'visible'); APP.editable.col = [APP.userid]; APP.editable.row = []; @@ -352,6 +356,8 @@ define([ e.stopPropagation(); if (!APP.ready) { return; } + if (e.which !== 1) { return; } + var target = e && e.target; if (!target) { return void debug("NO TARGET"); } diff --git a/www/poll/poll.css b/www/poll/poll.css index 3e534d5e2..a019dabe3 100644 --- a/www/poll/poll.css +++ b/www/poll/poll.css @@ -170,6 +170,10 @@ div.realtime table { border-collapse: collapse; width: calc(100% - 1px); } +form.realtime table .editing, +div.realtime table .editing { + background-color: #88b8cc; +} form.realtime table tr td:first-child, div.realtime table tr td:first-child { position: absolute; @@ -298,8 +302,8 @@ div.realtime table thead td input[type="text"][disabled] { color: #000; border: 1px solid transparent; } -form.realtime table tbody .text-cell, -div.realtime table tbody .text-cell { +form.realtime table tbody td:not(.editing) .text-cell, +div.realtime table tbody td:not(.editing) .text-cell { background: #aaa; } form.realtime table tbody .text-cell input[type="text"], diff --git a/www/poll/poll.less b/www/poll/poll.less index 699e96e03..d1979a958 100644 --- a/www/poll/poll.less +++ b/www/poll/poll.less @@ -4,6 +4,7 @@ @poll-th-bg: #aaa; @poll-th-user-bg: #999; @poll-td-bg: #aaa; +@poll-editing: #88b8cc; @poll-placeholder: #666; @poll-border-color: #555; @poll-cover-color: #000; @@ -195,6 +196,9 @@ form.realtime, div.realtime { table { border-collapse: collapse; width: ~"calc(100% - 1px)"; + .editing { + background-color: @poll-editing; + } tr { td:first-child { position:absolute; @@ -327,8 +331,12 @@ form.realtime, div.realtime { } tbody { + td:not(.editing) { + .text-cell { + background: @poll-td-bg; + } + } .text-cell { - background: @poll-td-bg; //border-radius: 20px 0 0 20px; input[type="text"] { width: ~"calc(100% - 50px)"; From 6bc191725ff232e79885d85d3e5fa35161d9f956 Mon Sep 17 00:00:00 2001 From: Pierre Bondoerffer Date: Mon, 12 Jun 2017 12:37:07 +0200 Subject: [PATCH 03/99] poll: support disconnect and fix trash button --- www/poll/main.js | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/www/poll/main.js b/www/poll/main.js index 1ff7638d5..2d848f17d 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -46,7 +46,8 @@ define([ editable: { row: [], col: [] - } + }, + locked: false }; var sortColumns = function (order, firstcol) { @@ -293,6 +294,7 @@ define([ }; var hideInputs = function (target, isKeyup) { + if (APP.locked) { return; } if (!isKeyup && $(target).is('[type="text"]')) { return; } @@ -353,10 +355,13 @@ define([ }; var handleClick = function (e, isKeyup) { + if (APP.locked) { return; } + e.stopPropagation(); if (!APP.ready) { return; } - if (e.which !== 1) { return; } + if (!isKeyup && e.which !== 1) { return; } + var target = e && e.target; @@ -533,7 +538,10 @@ var ready = function (info, userid, readOnly) { .click(handleClick) .on('keyup', function (e) { handleClick(e, true); }); - $(window).click(hideInputs); + $(window).click(function(e) { + if (e.which !== 1) { return; } + hideInputs(); + }); proxy .on('change', ['info'], function (o, n, p) { @@ -576,14 +584,33 @@ var ready = function (info, userid, readOnly) { UserList.getLastName(APP.toolbar.$userNameButton, isNew); }; +var setEditable = function (editable) { + APP.locked = !editable; + + if (editable === false) { + // disable all the things + $('.realtime input, .realtime button').attr('disabled', APP.locked); + $('span.edit, span.remove').hide(); + $('span.lock').addClass('fa-lock').removeClass('fa-unlock') + .attr('title', Messages.poll_locked) + .css({'cursor': 'default'}); + } else { + // enable + $('span.edit, span.remove').show(); + $('span.lock').css({'cursor': ''}); + $('.realtime button').attr('disabled', APP.locked); + unlockElements(); + } +}; + var disconnect = function () { - //setEditable(false); // TODO + setEditable(false); APP.toolbar.failed(); Cryptpad.alert(Messages.common_connectionLost, undefined, true); }; var reconnect = function (info) { - //setEditable(true); // TODO + setEditable(true); APP.toolbar.reconnecting(info.myId); Cryptpad.findOKButton().click(); }; @@ -638,7 +665,7 @@ var create = function (info) { /* add a forget button */ var forgetCb = function (err) { if (err) { return; } - disconnect(); + setEditable(false); }; var $forgetPad = Cryptpad.createButton('forget', true, {}, forgetCb); $rightside.append($forgetPad); @@ -709,7 +736,7 @@ var create = function (info) { } }); - //Cryptpad.onLogout(function () { setEditable(false); }); TODO + Cryptpad.onLogout(function () { setEditable(false); }); }); Cryptpad.onError(function (info) { if (info) { From 5d565660a9c5c035269ba3b1b757455c795fb458 Mon Sep 17 00:00:00 2001 From: Pierre Bondoerffer Date: Mon, 12 Jun 2017 12:41:03 +0200 Subject: [PATCH 04/99] tiny fixes --- www/poll/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/poll/main.js b/www/poll/main.js index 2d848f17d..0ff5dc0e0 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -589,7 +589,7 @@ var setEditable = function (editable) { if (editable === false) { // disable all the things - $('.realtime input, .realtime button').attr('disabled', APP.locked); + $('.realtime input, .realtime button, .upper button, .realtime textarea').attr('disabled', APP.locked); $('span.edit, span.remove').hide(); $('span.lock').addClass('fa-lock').removeClass('fa-unlock') .attr('title', Messages.poll_locked) @@ -598,7 +598,7 @@ var setEditable = function (editable) { // enable $('span.edit, span.remove').show(); $('span.lock').css({'cursor': ''}); - $('.realtime button').attr('disabled', APP.locked); + $('.realtime button, .upper button, .realtime textarea').attr('disabled', APP.locked); unlockElements(); } }; From 685b352db8b614658775de363673795207a494c9 Mon Sep 17 00:00:00 2001 From: Pierre Bondoerffer Date: Mon, 12 Jun 2017 14:25:10 +0200 Subject: [PATCH 05/99] poll: Add show help button --- customize.dist/translations/messages.es.js | 3 +++ customize.dist/translations/messages.fr.js | 3 +++ customize.dist/translations/messages.js | 3 +++ www/poll/index.html | 2 +- www/poll/main.js | 19 +++++++++++++------ 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/customize.dist/translations/messages.es.js b/customize.dist/translations/messages.es.js index 57eb41f9e..ce4bcb788 100644 --- a/customize.dist/translations/messages.es.js +++ b/customize.dist/translations/messages.es.js @@ -452,6 +452,9 @@ define(function () { out.poll_locked = "Cerrado"; out.poll_unlocked = "Abierto"; + out.poll_show_help_button = "Mostrar ayuda"; + out.poll_hide_help_button = "Esconder ayuda"; + // 1.8.0 - Idopogo out.common_connectionLost = "Connexión perdida
El documento está ahora en modo solo lectura hasta que la conexión vuelva."; diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 70d804652..0372d6081 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -202,6 +202,9 @@ define(function () { out.poll_locked = "Verrouillé"; out.poll_unlocked = "Déverrouillé"; + out.poll_show_help_button = "Afficher l'aide"; + out.poll_hide_help_button = "Cacher l'aide"; + // Canvas out.canvas_clear = "Nettoyer"; out.canvas_delete = "Supprimer la sélection"; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 6d4f07c02..dd14ad8da 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -204,6 +204,9 @@ define(function () { out.poll_locked = "Locked"; out.poll_unlocked = "Unlocked"; + out.poll_show_help_button = "Show help"; + out.poll_hide_help_button = "Hide help"; + // Canvas out.canvas_clear = "Clear"; out.canvas_delete = "Delete selection"; diff --git a/www/poll/index.html b/www/poll/index.html index 31226d989..22280e8a9 100644 --- a/www/poll/index.html +++ b/www/poll/index.html @@ -28,7 +28,7 @@
- +
diff --git a/www/poll/main.js b/www/poll/main.js index 0ff5dc0e0..05009b5b1 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -142,12 +142,10 @@ define([ var setTablePublished = function (bool) { if (bool) { if (APP.$publish) { APP.$publish.hide(); } - if (APP.$admin) { APP.$admin.show(); } $('#create-option').hide(); $('.remove[data-rt-id^="y"], .edit[data-rt-id^="y"]').hide(); } else { if (APP.$publish) { APP.$publish.show(); } - if (APP.$admin) { APP.$admin.hide(); } $('#create-option').show(); $('.remove[data-rt-id^="y"], .edit[data-rt-id^="y"]').show(); } @@ -432,6 +430,15 @@ define([ }); }; + var showHelp = function(help) { + if (typeof help === 'undefined') { help = !$('#howItWorks').is(':visible'); } + + var msg = (help ? Messages.poll_hide_help_button : Messages.poll_show_help_button); + + $('#howItWorks').toggle(help); + $('#help').text(msg).attr('title', msg); + }; + var Title; var UserList; @@ -498,9 +505,9 @@ var ready = function (info, userid, readOnly) { }); // #publish button is removed in readonly - APP.$admin = $('#admin') + APP.$help = $('#help') .click(function () { - publish(false); + showHelp(); }); // Title @@ -702,7 +709,7 @@ var create = function (info) { }; if (readOnly) { - $('#commit, #create-user, #create-option, #publish, #admin').remove(); + $('#commit, #create-user, #create-option, #publish').remove(); } var parsedHash = Cryptpad.parsePadUrl(window.location.href); @@ -732,7 +739,7 @@ var create = function (info) { if (e) { console.error(e); } }); } else if (value === "1") { - $('#howItWorks').hide(); + showHelp(false); } }); From c7023134898554b27ff78bed0541d6a25074f190 Mon Sep 17 00:00:00 2001 From: XWiki SAS Date: Mon, 12 Jun 2017 17:34:45 +0200 Subject: [PATCH 06/99] change label texte --- www/whiteboard/main.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/www/whiteboard/main.js b/www/whiteboard/main.js index dce656473..a96fdb457 100644 --- a/www/whiteboard/main.js +++ b/www/whiteboard/main.js @@ -50,7 +50,7 @@ define([ }; var $toggle = $('#toggleDraw'); - var $width = $('#width'); + var $width = $('#width'); // brush width var $widthLabel = $('label[for="width"]'); var $opacity = $('#opacity'); var $opacityLabel = $('label[for="opacity"]'); @@ -98,7 +98,9 @@ window.canvas = canvas; var val = $width.val(); canvas.freeDrawingBrush.width = Number(val); $widthLabel.text(val); + $widthLabel.text("width: " + $width.val()) createCursor(); + console.log("change size") }; updateBrushWidth(); @@ -108,8 +110,11 @@ window.canvas = canvas; var val = $opacity.val(); brush.opacity = Number(val); canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity); - $opacityLabel.text(val); + $opacityLabel.text(val) + $opacityLabel.text("opacity: " + $opacity.val()); createCursor(); + console.log("change opacity") + console.log("opacity: " + $opacity.val()) }; updateBrushOpacity(); From a58162a617d77d879aaaefb00df2992c4f066047 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 12 Jun 2017 17:49:33 +0200 Subject: [PATCH 07/99] Make the drive tests work when not logged in --- www/common/userObject.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/www/common/userObject.js b/www/common/userObject.js index f17e3838b..4c21084b0 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -520,7 +520,7 @@ define([ // ADD var add = exp.add = function (id, path) { - if (!Cryptpad.isLoggedIn()) { return; } + if (!Cryptpad.isLoggedIn() && !config.testMode) { return; } var data = files[FILES_DATA][id]; if (!data || typeof(data) !== "object") { return; } var newPath = path, parentEl; @@ -559,7 +559,7 @@ define([ exp.forget = function (href) { var id = getIdFromHref(href); if (!id) { return; } - if (!Cryptpad.isLoggedIn()) { + if (!Cryptpad.isLoggedIn() && !config.testMode) { // delete permanently exp.removePadAttribute(href); spliceFileData(id); @@ -588,7 +588,7 @@ define([ }; var checkDeletedFiles = function () { // Nothing in OLD_FILES_DATA for workgroups - if (workgroup || !Cryptpad.isLoggedIn()) { return; } + if (workgroup || (!Cryptpad.isLoggedIn() && !config.testMode)) { return; } var filesList = getFiles([ROOT, 'hrefArray', TRASH]); var fData = files[FILES_DATA]; @@ -617,7 +617,7 @@ define([ var trashPaths = paths.filter(function(x) { return isPathIn(x, [TRASH]); }); var allFilesPaths = paths.filter(function(x) { return isPathIn(x, [FILES_DATA]); }); - if (!Cryptpad.isLoggedIn()) { + if (!Cryptpad.isLoggedIn() && !config.testMode) { allFilesPaths.forEach(function (path) { var el = find(path); if (!el) { return; } @@ -967,7 +967,7 @@ define([ toClean.push(id); continue; } - if (Cryptpad.isLoggedIn() && rootFiles.indexOf(id) === -1) { + if ((Cryptpad.isLoggedIn() || config.testMode) && rootFiles.indexOf(id) === -1) { debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el); var newName = Cryptpad.createChannelId(); root[newName] = id; From b8e394db671e55ae11e6ada2063963b1fa1a04c4 Mon Sep 17 00:00:00 2001 From: Pierre Bondoerffer Date: Mon, 12 Jun 2017 18:01:51 +0200 Subject: [PATCH 08/99] poll: Implement multiple choices --- www/poll/main.js | 20 +++++++++----- www/poll/poll.css | 65 +++++++++++++++++++++++++++++++--------------- www/poll/poll.less | 34 +++++++++++++++++++----- www/poll/render.js | 24 ++++++++++------- 4 files changed, 101 insertions(+), 42 deletions(-) diff --git a/www/poll/main.js b/www/poll/main.js index 05009b5b1..a805b4c02 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -98,7 +98,7 @@ define([ // Enable the checkboxes for the user's column (committed or not) $('input[disabled="disabled"][data-rt-id^="' + id + '"]').removeAttr('disabled'); - $('input[type="checkbox"][data-rt-id^="' + id + '"]').addClass('enabled'); + $('input[type="number"][data-rt-id^="' + id + '"]').addClass('enabled'); $('.lock[data-rt-id="' + id + '"]').addClass('fa-unlock').removeClass('fa-lock').attr('title', Messages.poll_unlocked); if (isOwnColumnCommitted()) { return; } @@ -117,7 +117,7 @@ define([ APP.editable.col.forEach(function (id) { $input = $('input[disabled="disabled"][data-rt-id^="' + id + '"]').removeAttr('disabled'); $input.parent().addClass('editing'); - $('input[type="checkbox"][data-rt-id^="' + id + '"]').addClass('enabled'); + $('input[type="number"][data-rt-id^="' + id + '"]').addClass('enabled'); $('.lock[data-rt-id="' + id + '"]').addClass('fa-unlock').removeClass('fa-lock').attr('title', Messages.poll_unlocked); }); }; @@ -276,10 +276,10 @@ define([ Render.setValue(object, id, input.value); change(null, null, null, 50); break; - case 'checkbox': - debug("checkbox[tr-id='%s'] %s", id, input.checked); + case 'number': + debug("checkbox[tr-id='%s'] %s", id, input.value); if (APP.editable.col.indexOf(x) >= 0 || x === APP.userid) { - Render.setValue(object, id, input.checked); + Render.setValue(object, id, parseInt(input.value)); change(); } else { debug('checkbox locked'); @@ -378,10 +378,18 @@ define([ hideInputs(target, isKeyup); break; } + if ($(target).is('input[type="number"]')) { break; } + handleInput(target); break; + case 'LABEL': + var input = $('input[type="number"][id=' + $(target).attr('for') + ']'); + var value = parseInt(input.val()); + input.val((value + 1) % 4); + + handleInput(input[0]); + break; case 'SPAN': - //case 'LABEL': if (shouldLock) { break; } diff --git a/www/poll/poll.css b/www/poll/poll.css index a019dabe3..979f7f2b8 100644 --- a/www/poll/poll.css +++ b/www/poll/poll.css @@ -229,41 +229,64 @@ div.realtime table tr td.checkbox-cell div.checkbox-contain label { height: 100%; width: 100%; } -form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable), -div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) { +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable), +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) { display: none; } -form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover, -div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover { +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover { font-weight: bold; - background-color: #FA5858; color: #000; display: block; } -form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover:after, -div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover:after { +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover:after, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover:after { height: 100%; } -form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover:after, -div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover:after { - content: "✖"; -} -form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.yes, -div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.yes { +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover.yes, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover.yes { background-color: #46E981; } -form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.yes:after, -div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.yes:after { - content: "✔"; -} -form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.uncommitted, -div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.uncommitted { +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover.uncommitted, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover.uncommitted { background: #ddd; } -form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.mine, -div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.mine { +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover.mine, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover.mine { display: none; } +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="0"] ~ .cover, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="0"] ~ .cover { + background-color: #FA5858; +} +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="0"] ~ .cover:after, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="0"] ~ .cover:after { + content: "✖"; +} +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="1"] ~ .cover, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="1"] ~ .cover { + background-color: #46E981; +} +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="1"] ~ .cover:after, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="1"] ~ .cover:after { + content: "✔"; +} +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="2"] ~ .cover, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="2"] ~ .cover { + background-color: #ff5; +} +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="2"] ~ .cover:after, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="2"] ~ .cover:after { + content: "~"; +} +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="3"] ~ .cover, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="3"] ~ .cover { + background-color: #ccc; +} +form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="3"] ~ .cover:after, +div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="3"] ~ .cover:after { + content: "?"; +} form.realtime table input[type="text"], div.realtime table input[type="text"] { height: auto; diff --git a/www/poll/poll.less b/www/poll/poll.less index d1979a958..95b5cfa52 100644 --- a/www/poll/poll.less +++ b/www/poll/poll.less @@ -9,6 +9,8 @@ @poll-border-color: #555; @poll-cover-color: #000; @poll-fg: #000; +@poll-option-yellow: #ff5; +@poll-option-gray: #ccc; html, body { width: 100%; @@ -251,7 +253,7 @@ form.realtime, div.realtime { } input { - &[type="checkbox"] { + &[type="number"] { &:not(.editable) { display: none; @@ -259,26 +261,21 @@ form.realtime, div.realtime { display: block; font-weight: bold; - background-color: @cp-red; color: @poll-cover-color; &:after { height: 100%; } - &:after { content: "✖"; } - display: block; &.yes { background-color: @cp-green; - &:after { content: "✔"; } } &.uncommitted { background: #ddd; } - &.mine { display: none; } @@ -286,6 +283,31 @@ form.realtime, div.realtime { } } } + + input[type="number"][value="0"] { + ~ .cover { + background-color: @cp-red; + &:after { content: "✖"; } + } + } + input[type="number"][value="1"] { + ~ .cover { + background-color: @cp-green; + &:after { content: "✔"; } + } + } + input[type="number"][value="2"] { + ~ .cover { + background-color: @poll-option-yellow; + &:after { content: "~"; } + } + } + input[type="number"][value="3"] { + ~ .cover { + background-color: @poll-option-gray; + &:after { content: "?"; } + } + } } } } diff --git a/www/poll/render.js b/www/poll/render.js index 8399d5b71..08b91962a 100644 --- a/www/poll/render.js +++ b/www/poll/render.js @@ -70,7 +70,12 @@ var Renderer = function (Cryptpad) { }; var getCellValue = Render.getCellValue = function (obj, cellId) { - return Cryptpad.find(obj, ['table', 'cells'].concat([cellId])); + var value = Cryptpad.find(obj, ['table', 'cells'].concat([cellId])); + if (typeof value === 'boolean') { + return (value === true ? '1' : '0'); + } else { + return value; + } }; var setRowValue = Render.setRowValue = function (obj, rowId, value) { @@ -234,16 +239,20 @@ var Renderer = function (Cryptpad) { disabled: 'disabled' }].concat(cols.map(function (col) { var id = [col, rows[i-1]].join('_'); - var val = cells[id] || false; + var val = cells[id]; var result = { 'data-rt-id': id, - type: 'checkbox', + type: 'number', autocomplete: 'nope', + value: '3', }; if (readOnly) { result.disabled = "disabled"; } - if (val) { result.checked = true; } + if (typeof val !== 'undefined') { + if (typeof val === 'boolean') { val = (val ? '1' : '0'); } + result.value = val; + } return result; })); }); @@ -297,9 +306,6 @@ var Renderer = function (Cryptpad) { attrs.id = cell['data-rt-id']; var labelClass = 'cover'; - if (cell.checked) { - labelClass += ' yes'; - } // TODO implement Yes/No/Maybe/Undecided return ['TD', {class:"checkbox-cell"}, [ @@ -326,7 +332,7 @@ var Renderer = function (Cryptpad) { ]]; } - if (cell && cell.type === 'checkbox') { + if (cell && cell.type === 'number') { return makeCheckbox(cell); } return ['TD', cell, []]; @@ -399,7 +405,7 @@ var Renderer = function (Cryptpad) { preDiffApply: function (info) { if (!diffIsInput(info)) { return; } switch (getInputType(info)) { - case 'checkbox': + case 'number': //console.log('checkbox'); //console.log("[preDiffApply]", info); break; From 49370764c17f881362c0346c5d65333524816204 Mon Sep 17 00:00:00 2001 From: XWiki SAS Date: Mon, 12 Jun 2017 18:14:33 +0200 Subject: [PATCH 09/99] change whiteboard translation --- customize.dist/translations/messages.js | 3 +++ www/whiteboard/main.js | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index fa08fb1c7..cc279516c 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -211,6 +211,9 @@ define(function () { out.canvas_enable = "Enable draw"; out.canvas_width = "Width"; out.canvas_opacity = "Opacity"; + out.canvas_opacityLabel = "opacity: {0}"; + out.canvas_widthLabel = "Width: {0}"; + // File manager diff --git a/www/whiteboard/main.js b/www/whiteboard/main.js index a96fdb457..a19b5ba63 100644 --- a/www/whiteboard/main.js +++ b/www/whiteboard/main.js @@ -97,7 +97,8 @@ window.canvas = canvas; var updateBrushWidth = function () { var val = $width.val(); canvas.freeDrawingBrush.width = Number(val); - $widthLabel.text(val); + $widthLabel.text(val) + $widthLabel.text(Cryptpad.Messages._getKey("canvas_widthLabel", [val])) $widthLabel.text("width: " + $width.val()) createCursor(); console.log("change size") @@ -111,7 +112,7 @@ window.canvas = canvas; brush.opacity = Number(val); canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity); $opacityLabel.text(val) - $opacityLabel.text("opacity: " + $opacity.val()); + $opacityLabel.text(Cryptpad.Messages._getKey("canvas_opacityLabel", [val])); createCursor(); console.log("change opacity") console.log("opacity: " + $opacity.val()) From 0118fdbd0036c7a7c4dfc3e17e653d91580f2629 Mon Sep 17 00:00:00 2001 From: Pierre Bondoerffer Date: Mon, 12 Jun 2017 18:16:11 +0200 Subject: [PATCH 10/99] use a local variable --- www/poll/main.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/www/poll/main.js b/www/poll/main.js index a805b4c02..400811537 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -108,14 +108,13 @@ define([ }; var unlockElements = function () { - var $input; APP.editable.row.forEach(function (id) { - $input = $('input[type="text"][disabled="disabled"][data-rt-id="' + id + '"]').removeAttr('disabled'); + var $input = $('input[type="text"][disabled="disabled"][data-rt-id="' + id + '"]').removeAttr('disabled'); $input.parent().parent().addClass('editing'); $('span.edit[data-rt-id="' + id + '"]').css('visibility', 'hidden'); }); APP.editable.col.forEach(function (id) { - $input = $('input[disabled="disabled"][data-rt-id^="' + id + '"]').removeAttr('disabled'); + var $input = $('input[disabled="disabled"][data-rt-id^="' + id + '"]').removeAttr('disabled'); $input.parent().addClass('editing'); $('input[type="number"][data-rt-id^="' + id + '"]').addClass('enabled'); $('.lock[data-rt-id="' + id + '"]').addClass('fa-unlock').removeClass('fa-lock').attr('title', Messages.poll_unlocked); From cd1c95792997e1ff13af090a5944f64c643cf8e5 Mon Sep 17 00:00:00 2001 From: Pierre Bondoerffer Date: Mon, 12 Jun 2017 18:23:17 +0200 Subject: [PATCH 11/99] NaN safety --- www/poll/main.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/www/poll/main.js b/www/poll/main.js index 400811537..35bc4ffb2 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -278,7 +278,14 @@ define([ case 'number': debug("checkbox[tr-id='%s'] %s", id, input.value); if (APP.editable.col.indexOf(x) >= 0 || x === APP.userid) { - Render.setValue(object, id, parseInt(input.value)); + var value = parseInt(input.value); + + if (isNaN(value)) { + console.error("Got NaN?!"); + break; + } + + Render.setValue(object, id, value); change(); } else { debug('checkbox locked'); @@ -359,7 +366,6 @@ define([ if (!APP.ready) { return; } if (!isKeyup && e.which !== 1) { return; } - var target = e && e.target; if (!target) { return void debug("NO TARGET"); } @@ -384,6 +390,7 @@ define([ case 'LABEL': var input = $('input[type="number"][id=' + $(target).attr('for') + ']'); var value = parseInt(input.val()); + input.val((value + 1) % 4); handleInput(input[0]); From 56f2c46c755b649d55bbd473f4f0086f29176fe7 Mon Sep 17 00:00:00 2001 From: Pierre Bondoerffer Date: Mon, 12 Jun 2017 18:25:06 +0200 Subject: [PATCH 12/99] document a line --- www/poll/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/poll/main.js b/www/poll/main.js index 35bc4ffb2..59d1fb2e4 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -364,7 +364,7 @@ define([ e.stopPropagation(); if (!APP.ready) { return; } - if (!isKeyup && e.which !== 1) { return; } + if (!isKeyup && e.which !== 1) { return; } // only allow left clicks var target = e && e.target; From 848010842f7a034fa1d0fba07d78c96841471ae5 Mon Sep 17 00:00:00 2001 From: Pierre Bondoerffer Date: Mon, 12 Jun 2017 18:26:27 +0200 Subject: [PATCH 13/99] dont sanity check silently --- www/poll/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/poll/main.js b/www/poll/main.js index 59d1fb2e4..99419eb22 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -383,7 +383,7 @@ define([ hideInputs(target, isKeyup); break; } - if ($(target).is('input[type="number"]')) { break; } + if ($(target).is('input[type="number"]')) { console.error("number input focused?"); break; } handleInput(target); break; From b2eef94232a53f9f74114ccdbc12615bfb09345e Mon Sep 17 00:00:00 2001 From: Pierre Bondoerffer Date: Mon, 12 Jun 2017 18:35:13 +0200 Subject: [PATCH 14/99] use numbers, not strings --- www/poll/render.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/poll/render.js b/www/poll/render.js index 08b91962a..f84fdc964 100644 --- a/www/poll/render.js +++ b/www/poll/render.js @@ -72,7 +72,7 @@ var Renderer = function (Cryptpad) { var getCellValue = Render.getCellValue = function (obj, cellId) { var value = Cryptpad.find(obj, ['table', 'cells'].concat([cellId])); if (typeof value === 'boolean') { - return (value === true ? '1' : '0'); + return (value === true ? 1 : 0); } else { return value; } From 2b5339193c5f801f01c8977984d664d812e222a2 Mon Sep 17 00:00:00 2001 From: Pierre Bondoerffer Date: Mon, 12 Jun 2017 18:45:04 +0200 Subject: [PATCH 15/99] bring admin back --- www/poll/index.html | 1 + www/poll/main.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/www/poll/index.html b/www/poll/index.html index 22280e8a9..091086f5c 100644 --- a/www/poll/index.html +++ b/www/poll/index.html @@ -28,6 +28,7 @@
+
diff --git a/www/poll/main.js b/www/poll/main.js index 99419eb22..8043da3fc 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -141,10 +141,12 @@ define([ var setTablePublished = function (bool) { if (bool) { if (APP.$publish) { APP.$publish.hide(); } + if (APP.$admin) { APP.$admin.show(); } $('#create-option').hide(); $('.remove[data-rt-id^="y"], .edit[data-rt-id^="y"]').hide(); } else { if (APP.$publish) { APP.$publish.show(); } + if (APP.$admin) { APP.$admin.hide(); } $('#create-option').show(); $('.remove[data-rt-id^="y"], .edit[data-rt-id^="y"]').show(); } @@ -518,7 +520,11 @@ var ready = function (info, userid, readOnly) { publish(true); }); - // #publish button is removed in readonly + APP.$admin = $('#admin') + .click(function () { + publish(false); + }); + APP.$help = $('#help') .click(function () { showHelp(); @@ -723,7 +729,7 @@ var create = function (info) { }; if (readOnly) { - $('#commit, #create-user, #create-option, #publish').remove(); + $('#commit, #create-user, #create-option, #publish, #admin').remove(); } var parsedHash = Cryptpad.parsePadUrl(window.location.href); From d344f0377853cb64c2503cb2768527c887c5a8d5 Mon Sep 17 00:00:00 2001 From: Pierre Bondoerffer Date: Mon, 12 Jun 2017 18:57:07 +0200 Subject: [PATCH 16/99] proper button text --- www/poll/main.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/poll/main.js b/www/poll/main.js index 8043da3fc..4878b60a6 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -758,7 +758,8 @@ var create = function (info) { Cryptpad.setAttribute(HIDE_INTRODUCTION_TEXT, "1", function (e) { if (e) { console.error(e); } }); - } else if (value === "1") { + showHelp(true); + } else { showHelp(false); } }); From 977faee40b4bd02dd8e897b06b0b08d8c0aa6c51 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 13 Jun 2017 11:14:01 +0200 Subject: [PATCH 17/99] expose more debugging information from rpc errors --- rpc.js | 67 ++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/rpc.js b/rpc.js index cfb6d7772..9f143f45e 100644 --- a/rpc.js +++ b/rpc.js @@ -17,6 +17,15 @@ var Store = require("./storage/file"); var DEFAULT_LIMIT = 50 * 1024 * 1024; var SESSION_EXPIRATION_TIME = 60 * 1000; +var SUPPRESS_RPC_ERRORS = false; + +var WARN = function (e, output) { + if (!SUPPRESS_RPC_ERRORS) { + console.error(new Date().toISOString() + ' [' + e + ']', output); + console.error(new Error(e).stack); + console.error(); + } +}; var isValidId = function (chan) { return chan && chan.length && /^[a-fA-F0-9]/.test(chan) || @@ -237,11 +246,10 @@ var loadUserPins = function (Env, publicKey, cb) { } break; default: - console.error('invalid message read from store'); + WARN('invalid message read from store', msg); } } catch (e) { - console.log('invalid message read from store'); - console.error(e); + WARN('invalid message read from store', e); } }, function () { // no more messages @@ -328,7 +336,7 @@ var getMultipleFileSize = function (Env, channels, cb) { channels.forEach(function (channel) { getFileSize(Env, channel, function (e, size) { if (e) { - console.error(e); + WARN('getFileSize', e); counts[channel] = -1; return done(); } @@ -504,7 +512,7 @@ var pinChannel = function (Env, publicKey, channels, cb) { getFreeSpace(Env, publicKey, function (e, free) { if (e) { - console.error(e); + WARN('getFreeSpace', e); return void cb(e); } if (pinSize > free) { return void cb('E_OVER_LIMIT'); } @@ -573,7 +581,7 @@ var resetUserPins = function (Env, publicKey, channelList, cb) { getFreeSpace(Env, publicKey, function (e, free) { if (e) { - console.error(e); + WARN('getFreeSpace', e); return void cb(e); } @@ -646,6 +654,9 @@ var makeFileStream = function (root, id, cb) { stream.on('open', function () { cb(void 0, stream); }); + stream.on('error', function (e) { + WARN('stream error', e); + }); } catch (err) { cb('BAD_STREAM'); } @@ -737,14 +748,12 @@ var upload_complete = function (Env, publicKey, cb) { safeMkdir(Path.join(paths.blob, prefix), function (e) { if (e) { - console.error('[safeMkdir]'); - console.error(e); - console.log(); + WARN('safeMkdir', e); return void cb('RENAME_ERR'); } isFile(newPath, function (e, yes) { if (e) { - console.error(e); + WARN('isFile', e); return void cb(e); } if (yes) { @@ -770,7 +779,7 @@ var upload_complete = function (Env, publicKey, cb) { // lol wut handle ur errors Fs.rename(oldPath, newPath, function (e) { if (e) { - console.error(e); + WARN('rename', e); if (retries--) { return setTimeout(function () { @@ -803,7 +812,7 @@ var upload_status = function (Env, publicKey, filesize, cb) { if (filesize >= free) { return cb('NOT_ENOUGH_SPACE'); } isFile(filePath, function (e, yes) { if (e) { - console.error("uploadError: [%s]", e); + WARN('upload', e); return cb('UNNOWN_ERROR'); } cb(e, yes); @@ -834,11 +843,7 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) // load pin-store... console.log('loading rpc module...'); - var warn = function (e, output) { - if (e && !config.suppressRPCErrors) { - console.error(new Date().toISOString() + ' [' + e + ']', output); - } - }; + if (config.suppressRPCErrors) { SUPPRESS_RPC_ERRORS = true; } var keyOrDefaultString = function (key, def) { return typeof(config[key]) === 'string'? config[key]: def; @@ -937,41 +942,41 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) case 'COOKIE': return void Respond(void 0); case 'RESET': return resetUserPins(Env, safeKey, msg[1], function (e, hash) { - //warn(e, hash); + //WARN(e, hash); return void Respond(e, hash); }); case 'PIN': return pinChannel(Env, safeKey, msg[1], function (e, hash) { - warn(e, hash); + WARN(e, hash); Respond(e, hash); }); case 'UNPIN': return unpinChannel(Env, safeKey, msg[1], function (e, hash) { - warn(e, hash); + WARN(e, hash); Respond(e, hash); }); case 'GET_HASH': return void getHash(Env, safeKey, function (e, hash) { - warn(e, hash); + WARN(e, hash); Respond(e, hash); }); case 'GET_TOTAL_SIZE': // TODO cache this, since it will get called quite a bit return getTotalSize(Env, safeKey, function (e, size) { if (e) { - warn(e, safeKey); + WARN(e, safeKey); return void Respond(e); } Respond(e, size); }); case 'GET_FILE_SIZE': return void getFileSize(Env, msg[1], function (e, size) { - warn(e, msg[1]); + WARN(e, msg[1]); Respond(e, size); }); case 'UPDATE_LIMITS': return void updateLimits(config, safeKey, function (e, limit) { if (e) { - warn(e, limit); + WARN(e, limit); return void Respond(e); } Respond(void 0, limit); @@ -979,7 +984,7 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) case 'GET_LIMIT': return void getLimit(Env, safeKey, function (e, limit) { if (e) { - warn(e, limit); + WARN(e, limit); return void Respond(e); } Respond(void 0, limit); @@ -987,7 +992,7 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) case 'GET_MULTIPLE_FILE_SIZE': return void getMultipleFileSize(Env, msg[1], function (e, dict) { if (e) { - warn(e, dict); + WARN(e, dict); return void Respond(e); } Respond(void 0, dict); @@ -997,7 +1002,7 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) case 'UPLOAD': if (!privileged) { return deny(); } return void upload(Env, safeKey, msg[1], function (e, len) { - warn(e, len); + WARN(e, len); Respond(e, len); }); case 'UPLOAD_STATUS': @@ -1015,13 +1020,13 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) case 'UPLOAD_COMPLETE': if (!privileged) { return deny(); } return void upload_complete(Env, safeKey, function (e, hash) { - warn(e, hash); + WARN(e, hash); Respond(e, hash); }); case 'UPLOAD_CANCEL': if (!privileged) { return deny(); } return void upload_cancel(Env, safeKey, function (e) { - warn(e); + WARN(e); Respond(e); }); default: @@ -1054,7 +1059,9 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) var updateLimitDaily = function () { updateLimits(config, undefined, function (e) { - if (e) { console.error('Error updating the storage limits', e); } + if (e) { + WARN('limitUpdate', e); + } }); }; updateLimitDaily(); From 278125747c555859e41beaabf97a6ceb26c0da28 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 13 Jun 2017 11:18:14 +0200 Subject: [PATCH 18/99] only log errors if there is an error --- rpc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc.js b/rpc.js index 9f143f45e..6e555ae4a 100644 --- a/rpc.js +++ b/rpc.js @@ -20,7 +20,7 @@ var SESSION_EXPIRATION_TIME = 60 * 1000; var SUPPRESS_RPC_ERRORS = false; var WARN = function (e, output) { - if (!SUPPRESS_RPC_ERRORS) { + if (!SUPPRESS_RPC_ERRORS && e && output) { console.error(new Date().toISOString() + ' [' + e + ']', output); console.error(new Error(e).stack); console.error(); From 803bad1421ffd64c156029b6b598d16a653dc689 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 13 Jun 2017 11:21:26 +0200 Subject: [PATCH 19/99] Move the file upload code in a separate file --- www/common/common-file.js | 260 ++++++++++++++++++++++++++++++++++ www/common/common-title.js | 5 +- www/common/cryptpad-common.js | 17 ++- www/common/toolbar2.js | 2 +- www/file/file.css | 16 +-- www/file/file.less | 2 +- www/file/inner.html | 4 +- www/file/main.js | 53 ++----- 8 files changed, 299 insertions(+), 60 deletions(-) create mode 100644 www/common/common-file.js diff --git a/www/common/common-file.js b/www/common/common-file.js new file mode 100644 index 000000000..9781df3ef --- /dev/null +++ b/www/common/common-file.js @@ -0,0 +1,260 @@ +define([ + 'jquery', + '/file/file-crypto.js', + '/bower_components/tweetnacl/nacl-fast.min.js', +], function ($, FileCrypto) { + var Nacl = window.nacl; + var module = {}; + + module.create = function (common) { + var File = {}; + + var Messages = common.Messages; + + var queue = File.queue = { + queue: [], + inProgress: false + }; + + var uid = function () { + return 'file-' + String(Math.random()).substring(2); + }; + + var $table = File.$table = $('', { id: 'uploadStatus' }); + var $thead = $('').appendTo($table); + $('', {id: id}).appendTo($table); + + var $cancel = $('', {'class': 'cancel fa fa-times'}).click(function () { + queue.queue = queue.queue.filter(function (el) { return el.id !== id; }); + $cancel.remove(); + $tr.find('.upCancel').text('-'); + $tr.find('.progressValue').text(Messages.upload_cancelled); + }); + + var $link = $('', { + 'class': 'upLink', + 'rel': 'noopener noreferrer' + }).text(obj.metadata.name); + + $('
').text(Messages.upload_name).appendTo($thead); + $('').text(Messages.upload_size).appendTo($thead); + $('').text(Messages.upload_progress).appendTo($thead); + $('').text(Messages.cancel).appendTo($thead); + + var createTableContainer = function ($body) { + File.$container = $('
', { id: 'uploadStatusContainer' }).append($table).appendTo($body); + return File.$container; + }; + + var upload = function (blob, metadata, id) { + if (queue.inProgress) { return; } + queue.inProgress = true; + + var $row = $table.find('tr[id="'+id+'"]'); + + $row.find('.upCancel').html('-'); + var $pv = $row.find('.progressValue'); + var $pb = $row.find('.progressContainer'); + var $link = $row.find('.upLink'); + + var updateProgress = function (progressValue) { + $pv.text(Math.round(progressValue*100)/100 + '%'); + $pb.css({ + width: (progressValue/100)*188+'px' + }); + }; + + var u8 = new Uint8Array(blob); + + var key = Nacl.randomBytes(32); + var next = FileCrypto.encrypt(u8, metadata, key); + + var estimate = FileCrypto.computeEncryptedSize(blob.byteLength, metadata); + + var sendChunk = function (box, cb) { + var enc = Nacl.util.encodeBase64(box); + Cryptpad.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) { + console.log(box); + cb(e, msg); + }); + }; + + var actual = 0; + var again = function (err, box) { + if (err) { throw new Error(err); } + if (box) { + actual += box.length; + var progressValue = (actual / estimate * 100); + updateProgress(progressValue); + + return void sendChunk(box, function (e) { + if (e) { return console.error(e); } + next(again); + }); + } + + if (actual !== estimate) { + console.error('Estimated size does not match actual size'); + } + + // if not box then done + Cryptpad.uploadComplete(function (e, id) { + if (e) { return void console.error(e); } + var uri = ['', 'blob', id.slice(0,2), id].join('/'); + console.log("encrypted blob is now available as %s", uri); + + var b64Key = Nacl.util.encodeBase64(key); + + var hash = Cryptpad.getFileHashFromKeys(id, b64Key); + var href = '/file/#' + hash; + $link.attr('href', href) + .click(function (e) { + e.preventDefault(); + window.open($link.attr('href'), '_blank'); + }); + + // TODO add button to table which copies link to clipboard? + //APP.toolbar.addElement(['fileshare'], {}); + + var title = document.title = metadata.name; + myFile = blob; + myDataType = metadata.type; + + Cryptpad.renamePad(title || "", href, function (err) { + if (err) { console.error(err); } // TODO + console.log(title); + Cryptpad.log(Messages._getKey('upload_success', [title])); + queue.inProgress = false; + queue.next(); + }) + //Title.updateTitle(title || "", href); + //APP.toolbar.title.show(); + }); + }; + + Cryptpad.uploadStatus(estimate, function (e, pending) { + if (e) { + queue.inProgress = false; + queue.next(); + if (e === 'TOO_LARGE') { + // TODO update table to say too big? + return void Cryptpad.alert(Messages.upload_tooLarge); + } + if (e === 'NOT_ENOUGH_SPACE') { + // TODO update table to say not enough space? + return void Cryptpad.alert(Messages.upload_notEnoughSpace); + } + console.error(e); + return void Cryptpad.alert(Messages.upload_serverError); + } + + if (pending) { + // TODO keep this message in case of pending files in another window? + return void Cryptpad.confirm(Messages.upload_uploadPending, function (yes) { + if (!yes) { return; } + Cryptpad.uploadCancel(function (e, res) { + if (e) { + return void console.error(e); + } + console.log(res); + next(again); + }); + }); + } + next(again); + }); + }; + + var prettySize = function (bytes) { + var kB = Cryptpad.bytesToKilobytes(bytes); + if (kB < 1024) { return kB + Messages.KB; } + var mB = Cryptpad.bytesToMegabytes(bytes); + return mB + Messages.MB; + }; + + queue.next = function () { + if (queue.queue.length === 0) { return; } + if (queue.inProgress) { return; } + var file = queue.queue.shift(); + upload(file.blob, file.metadata, file.id); + }; + queue.push = function (obj) { + var id = uid(); + obj.id = id; + queue.queue.push(obj); + + $table.show(); + var estimate = FileCrypto.computeEncryptedSize(obj.blob.byteLength, obj.metadata); + + var $progressBar = $('
', {'class':'progressContainer'}); + var $progressValue = $('', {'class':'progressValue'}).text(Messages.upload_pending); + + var $tr = $('
').append($link).appendTo($tr); + $('').text(prettySize(estimate)).appendTo($tr); + $('', {'class': 'upProgress'}).append($progressBar).append($progressValue).appendTo($tr); + $('', {'class': 'upCancel'}).append($cancel).appendTo($tr); + + queue.next(); + }; + + var handleFile = File.handleFile = function (file) { + console.log(file); + var reader = new FileReader(); + reader.onloadend = function () { + queue.push({ + blob: this.result, + metadata: { + name: file.name, + type: file.type, + } + }); + }; + reader.readAsArrayBuffer(file); + }; + + var createAreaHandlers = File.createDropArea = function ($area, $hoverArea, todo) { + var counter = 0; + if (!$hoverArea) { $hoverArea = $area; } + $hoverArea + .on('dragenter', function (e) { + e.preventDefault(); + e.stopPropagation(); + counter++; + $label.addClass('hovering'); + }) + .on('dragleave', function (e) { + e.preventDefault(); + e.stopPropagation(); + counter--; + if (counter <= 0) { + $label.removeClass('hovering'); + } + }); + + $area + .on('drag dragstart dragend dragover drop dragenter dragleave', function (e) { + e.preventDefault(); + e.stopPropagation(); + }) + .on('drop', function (e) { + e.stopPropagation(); + var dropped = e.originalEvent.dataTransfer.files; + counter = 0; + $label.removeClass('hovering'); + + Array.prototype.slice.call(dropped).forEach(function (d) { + todo(d); + }); + }); + }; + + File.createUploader = function ($area, $hover, $body) { + createAreaHandlers($area, null, handleFile); + var $c = createTableContainer($body); + }; + + return File; + }; + + return module; +}); diff --git a/www/common/common-title.js b/www/common/common-title.js index 226dc1b34..f5f793f6e 100644 --- a/www/common/common-title.js +++ b/www/common/common-title.js @@ -43,11 +43,12 @@ define(function () { onLocal(); }; - exp.updateTitle = function (newTitle) { + // update title: href is optional; if not specified, we use window.location.href + exp.updateTitle = function (newTitle, href) { if (newTitle === exp.title) { return; } // Change the title now, and set it back to the old value if there is an error var oldTitle = exp.title; - Cryptpad.renamePad(newTitle, function (err, data) { + Cryptpad.renamePad(newTitle, href, function (err, data) { if (err) { console.log("Couldn't set pad title"); console.error(err); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index fcefbd0fe..af8a600ca 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -11,11 +11,13 @@ define([ '/common/common-title.js', '/common/common-metadata.js', '/common/common-codemirror.js', + '/common/common-file.js', '/common/clipboard.js', '/common/pinpad.js', '/customize/application_config.js' -], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata, CodeMirror, Clipboard, Pinpad, AppConfig) { +], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata, + CodeMirror, Files, Clipboard, Pinpad, AppConfig) { /* This file exposes functionality which is specific to Cryptpad, but not to any particular pad type. This includes functions for committing metadata @@ -114,6 +116,9 @@ define([ // CodeMirror common.createCodemirror = CodeMirror.create; + // Files + common.createFileManager = function () { return Files.create(common); }; + // History common.getHistory = function (config) { return History.create(common, config); }; @@ -520,8 +525,8 @@ define([ cb ("store.forgetPad is not a function"); }; - common.setPadTitle = function (name, cb) { - var href = window.location.href; + common.setPadTitle = function (name, padHref, cb) { + var href = padHref || window.location.href; var parsed = parsePadUrl(href); if (!parsed.hash) { return; } href = getRelativeHref(href); @@ -621,15 +626,15 @@ define([ /* * Buttons */ - common.renamePad = function (title, callback) { + common.renamePad = function (title, href, callback) { if (title === null) { return; } if (title.trim() === "") { - var parsed = parsePadUrl(window.location.href); + var parsed = parsePadUrl(href || window.location.href); title = getDefaultName(parsed); } - common.setPadTitle(title, function (err) { + common.setPadTitle(title, href, function (err) { if (err) { console.log("unable to set pad title"); console.log(err); diff --git a/www/common/toolbar2.js b/www/common/toolbar2.js index 813cef23c..39bfc49e8 100644 --- a/www/common/toolbar2.js +++ b/www/common/toolbar2.js @@ -438,7 +438,7 @@ define([ if (name === "") { name = $input.attr('placeholder'); } - Cryptpad.renamePad(name, function (err, newtitle) { + Cryptpad.renamePad(name, null, function (err, newtitle) { if (err) { return; } $text.text(newtitle); callback(null, newtitle); diff --git a/www/file/file.css b/www/file/file.css index f1e78d72a..eade31db1 100644 --- a/www/file/file.css +++ b/www/file/file.css @@ -77,7 +77,7 @@ body { z-index: 10000; display: block; } -#status { +#uploadStatus { display: none; width: 80vw; margin-top: 50px; @@ -85,24 +85,24 @@ body { border: 1px solid black; border-collapse: collapse; } -#status tr:nth-child(1) { +#uploadStatus tr:nth-child(1) { background-color: #ccc; border: 1px solid #999; } -#status tr:nth-child(1) td { +#uploadStatus tr:nth-child(1) td { text-align: center; } -#status td { +#uploadStatus td { border-left: 1px solid #BBB; border-right: 1px solid #BBB; padding: 0 10px; } -#status .upProgress { +#uploadStatus .upProgress { width: 200px; position: relative; text-align: center; } -#status .progressContainer { +#uploadStatus .progressContainer { position: absolute; width: 0px; left: 5px; @@ -110,9 +110,9 @@ body { bottom: 1px; background-color: rgba(0, 0, 255, 0.3); } -#status .upCancel { +#uploadStatus .upCancel { text-align: center; } -#status .fa.cancel { +#uploadStatus .fa.cancel { color: #ff0073; } diff --git a/www/file/file.less b/www/file/file.less index 04407f8e5..2347889f7 100644 --- a/www/file/file.less +++ b/www/file/file.less @@ -87,7 +87,7 @@ html, body { display: block; } -#status { +#uploadStatus { display: none; width: 80vw; margin-top: 50px; diff --git a/www/file/inner.html b/www/file/inner.html index f5946c099..0005241a5 100644 --- a/www/file/inner.html +++ b/www/file/inner.html @@ -21,14 +21,14 @@ data-localization="download_button"> - + diff --git a/www/file/main.js b/www/file/main.js index a07a7d27e..3e7c7faa3 100644 --- a/www/file/main.js +++ b/www/file/main.js @@ -17,7 +17,7 @@ define([ var APP = {}; $(function () { - +// TODO race condition with contents() here var ifrw = $('#pad-iframe')[0].contentWindow; var $iframe = $('#pad-iframe').contents(); var $form = $iframe.find('#upload-form'); @@ -25,9 +25,10 @@ define([ var $label = $form.find('label'); var $table = $iframe.find('#status'); var $progress = $iframe.find('#progress'); + var $body = $iframe.find('body'); - $iframe.find('body').on('dragover', function (e) { e.preventDefault(); }); - $iframe.find('body').on('drop', function (e) { e.preventDefault(); }); + $body.on('dragover', function (e) { e.preventDefault(); }); + $body.on('drop', function (e) { e.preventDefault(); }); Cryptpad.addLoadingScreen(); @@ -35,7 +36,7 @@ define([ var myFile; var myDataType; - +/* var queue = { queue: [], inProgress: false @@ -206,7 +207,7 @@ define([ queue.next(); }; - +*/ var uploadMode = false; var andThen = function () { @@ -329,7 +330,7 @@ define([ display: 'block', }); - var handleFile = function (file) { + /*var handleFile = function (file) { console.log(file); var reader = new FileReader(); reader.onloadend = function () { @@ -342,45 +343,17 @@ define([ }); }; reader.readAsArrayBuffer(file); - }; + };*/ + + var FM = Cryptpad.createFileManager(); $form.find("#file").on('change', function (e) { var file = e.target.files[0]; - handleFile(file); + FM.handleFile(file); }); - var counter = 0; - $label - .on('dragenter', function (e) { - e.preventDefault(); - e.stopPropagation(); - counter++; - $label.addClass('hovering'); - }) - .on('dragleave', function (e) { - e.preventDefault(); - e.stopPropagation(); - counter--; - if (counter <= 0) { - $label.removeClass('hovering'); - } - }); - - $form - .on('drag dragstart dragend dragover drop dragenter dragleave', function (e) { - e.preventDefault(); - e.stopPropagation(); - }) - .on('drop', function (e) { - e.stopPropagation(); - var dropped = e.originalEvent.dataTransfer.files; - counter = 0; - $label.removeClass('hovering'); - - Array.prototype.slice.call(dropped).forEach(function (d) { - handleFile(d); - }); - }); + //FM.createDropArea($form, $label, handleFile); + FM.createUploader($form, $label, $body); // we're in upload mode Cryptpad.removeLoadingScreen(); From d7292e69c660f1b8a630dad7d0bd842236efa159 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 13 Jun 2017 11:25:12 +0200 Subject: [PATCH 20/99] Fix undefined variable --- www/common/common-file.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/www/common/common-file.js b/www/common/common-file.js index 9781df3ef..4ace78c9c 100644 --- a/www/common/common-file.js +++ b/www/common/common-file.js @@ -220,14 +220,14 @@ define([ e.preventDefault(); e.stopPropagation(); counter++; - $label.addClass('hovering'); + $hoverArea.addClass('hovering'); }) .on('dragleave', function (e) { e.preventDefault(); e.stopPropagation(); counter--; if (counter <= 0) { - $label.removeClass('hovering'); + $hoverArea.removeClass('hovering'); } }); @@ -240,7 +240,7 @@ define([ e.stopPropagation(); var dropped = e.originalEvent.dataTransfer.files; counter = 0; - $label.removeClass('hovering'); + $hoverArea.removeClass('hovering'); Array.prototype.slice.call(dropped).forEach(function (d) { todo(d); From c779e328e8ce73e24ef94339e88e64d09a0f86b0 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 13 Jun 2017 11:33:25 +0200 Subject: [PATCH 21/99] warn users if their browser does not support localStorage --- www/common/boot2.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/www/common/boot2.js b/www/common/boot2.js index 76ce9bcf9..64d4bd71d 100644 --- a/www/common/boot2.js +++ b/www/common/boot2.js @@ -20,5 +20,16 @@ define([], function () { }; } + if (!localStorage || typeof(localStorage.getItem) !== 'function') { + require('jquery', function ($) { + $.ajax({ + type: 'HEAD', + url: '/common/feedback.html?NO_LOCALSTORAGE=' + (+new Date()), + }); + }); + window.alert("CryptPad needs localStorage to work, try a different browser"); + return; + } + require([document.querySelector('script[data-bootload]').getAttribute('data-bootload')]); }); From 11e2226f1a11e7a1fbd29feaade1bd9b0c60e6af Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 13 Jun 2017 11:38:04 +0200 Subject: [PATCH 22/99] Fix lint errors --- www/common/common-file.js | 32 +++---- www/file/main.js | 187 -------------------------------------- 2 files changed, 15 insertions(+), 204 deletions(-) diff --git a/www/common/common-file.js b/www/common/common-file.js index 4ace78c9c..7cb62a083 100644 --- a/www/common/common-file.js +++ b/www/common/common-file.js @@ -59,7 +59,7 @@ define([ var sendChunk = function (box, cb) { var enc = Nacl.util.encodeBase64(box); - Cryptpad.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) { + common.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) { console.log(box); cb(e, msg); }); @@ -84,14 +84,14 @@ define([ } // if not box then done - Cryptpad.uploadComplete(function (e, id) { + common.uploadComplete(function (e, id) { if (e) { return void console.error(e); } var uri = ['', 'blob', id.slice(0,2), id].join('/'); console.log("encrypted blob is now available as %s", uri); var b64Key = Nacl.util.encodeBase64(key); - var hash = Cryptpad.getFileHashFromKeys(id, b64Key); + var hash = common.getFileHashFromKeys(id, b64Key); var href = '/file/#' + hash; $link.attr('href', href) .click(function (e) { @@ -103,42 +103,40 @@ define([ //APP.toolbar.addElement(['fileshare'], {}); var title = document.title = metadata.name; - myFile = blob; - myDataType = metadata.type; - Cryptpad.renamePad(title || "", href, function (err) { + common.renamePad(title || "", href, function (err) { if (err) { console.error(err); } // TODO console.log(title); - Cryptpad.log(Messages._getKey('upload_success', [title])); + common.log(Messages._getKey('upload_success', [title])); queue.inProgress = false; queue.next(); - }) + }); //Title.updateTitle(title || "", href); //APP.toolbar.title.show(); }); }; - Cryptpad.uploadStatus(estimate, function (e, pending) { + common.uploadStatus(estimate, function (e, pending) { if (e) { queue.inProgress = false; queue.next(); if (e === 'TOO_LARGE') { // TODO update table to say too big? - return void Cryptpad.alert(Messages.upload_tooLarge); + return void common.alert(Messages.upload_tooLarge); } if (e === 'NOT_ENOUGH_SPACE') { // TODO update table to say not enough space? - return void Cryptpad.alert(Messages.upload_notEnoughSpace); + return void common.alert(Messages.upload_notEnoughSpace); } console.error(e); - return void Cryptpad.alert(Messages.upload_serverError); + return void common.alert(Messages.upload_serverError); } if (pending) { // TODO keep this message in case of pending files in another window? - return void Cryptpad.confirm(Messages.upload_uploadPending, function (yes) { + return void common.confirm(Messages.upload_uploadPending, function (yes) { if (!yes) { return; } - Cryptpad.uploadCancel(function (e, res) { + common.uploadCancel(function (e, res) { if (e) { return void console.error(e); } @@ -152,9 +150,9 @@ define([ }; var prettySize = function (bytes) { - var kB = Cryptpad.bytesToKilobytes(bytes); + var kB = common.bytesToKilobytes(bytes); if (kB < 1024) { return kB + Messages.KB; } - var mB = Cryptpad.bytesToMegabytes(bytes); + var mB = common.bytesToMegabytes(bytes); return mB + Messages.MB; }; @@ -250,7 +248,7 @@ define([ File.createUploader = function ($area, $hover, $body) { createAreaHandlers($area, null, handleFile); - var $c = createTableContainer($body); + createTableContainer($body); }; return File; diff --git a/www/file/main.js b/www/file/main.js index 3e7c7faa3..7ae8a622e 100644 --- a/www/file/main.js +++ b/www/file/main.js @@ -23,7 +23,6 @@ define([ var $form = $iframe.find('#upload-form'); var $dlform = $iframe.find('#download-form'); var $label = $form.find('label'); - var $table = $iframe.find('#status'); var $progress = $iframe.find('#progress'); var $body = $iframe.find('body'); @@ -36,178 +35,7 @@ define([ var myFile; var myDataType; -/* - var queue = { - queue: [], - inProgress: false - }; - var uid = function () { - return 'file-' + String(Math.random()).substring(2); - }; - - var upload = function (blob, metadata, id) { - console.log(metadata); - if (queue.inProgress) { return; } - queue.inProgress = true; - - var $row = $table.find('tr[id="'+id+'"]'); - - $row.find('.upCancel').html('-'); - var $pv = $row.find('.progressValue'); - var $pb = $row.find('.progressContainer'); - var $link = $row.find('.upLink'); - - var updateProgress = function (progressValue) { - $pv.text(Math.round(progressValue*100)/100 + '%'); - $pb.css({ - width: (progressValue/100)*188+'px' - }); - }; - - var u8 = new Uint8Array(blob); - - var key = Nacl.randomBytes(32); - var next = FileCrypto.encrypt(u8, metadata, key); - - var estimate = FileCrypto.computeEncryptedSize(blob.byteLength, metadata); - - var sendChunk = function (box, cb) { - var enc = Nacl.util.encodeBase64(box); - Cryptpad.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) { - console.log(box); - cb(e, msg); - }); - }; - - var actual = 0; - var again = function (err, box) { - if (err) { throw new Error(err); } - if (box) { - actual += box.length; - var progressValue = (actual / estimate * 100); - updateProgress(progressValue); - - return void sendChunk(box, function (e) { - if (e) { return console.error(e); } - next(again); - }); - } - - if (actual !== estimate) { - console.error('Estimated size does not match actual size'); - } - - // if not box then done - Cryptpad.uploadComplete(function (e, id) { - if (e) { return void console.error(e); } - var uri = ['', 'blob', id.slice(0,2), id].join('/'); - console.log("encrypted blob is now available as %s", uri); - - var b64Key = Nacl.util.encodeBase64(key); - - var hash = Cryptpad.getFileHashFromKeys(id, b64Key); - - $link.attr('href', '/file/#' + hash) - .click(function (e) { - e.preventDefault(); - window.open($link.attr('href'), '_blank'); - }); - - // TODO add button to table which copies link to clipboard? - //APP.toolbar.addElement(['fileshare'], {}); - - var title = document.title = metadata.name; - myFile = blob; - myDataType = metadata.type; - var defaultName = Cryptpad.getDefaultName(Cryptpad.parsePadUrl(window.location.href)); - Title.updateTitle(title || defaultName); - APP.toolbar.title.show(); - console.log(title); - Cryptpad.log(Messages._getKey('upload_success', [title])); - queue.inProgress = false; - queue.next(); - }); - }; - - Cryptpad.uploadStatus(estimate, function (e, pending) { - if (e) { - queue.inProgress = false; - queue.next(); - if (e === 'TOO_LARGE') { - // TODO update table to say too big? - return void Cryptpad.alert(Messages.upload_tooLarge); - } - if (e === 'NOT_ENOUGH_SPACE') { - // TODO update table to say not enough space? - return void Cryptpad.alert(Messages.upload_notEnoughSpace); - } - console.error(e); - return void Cryptpad.alert(Messages.upload_serverError); - } - - if (pending) { - // TODO keep this message in case of pending files in another window? - return void Cryptpad.confirm(Messages.upload_uploadPending, function (yes) { - if (!yes) { return; } - Cryptpad.uploadCancel(function (e, res) { - if (e) { - return void console.error(e); - } - console.log(res); - next(again); - }); - }); - } - next(again); - }); - }; - - var prettySize = function (bytes) { - var kB = Cryptpad.bytesToKilobytes(bytes); - if (kB < 1024) { return kB + Messages.KB; } - var mB = Cryptpad.bytesToMegabytes(bytes); - return mB + Messages.MB; - }; - - queue.next = function () { - if (queue.queue.length === 0) { return; } - if (queue.inProgress) { return; } - var file = queue.queue.shift(); - upload(file.blob, file.metadata, file.id); - }; - queue.push = function (obj) { - var id = uid(); - obj.id = id; - queue.queue.push(obj); - - $table.show(); - var estimate = FileCrypto.computeEncryptedSize(obj.blob.byteLength, obj.metadata); - - var $progressBar = $('
', {'class':'progressContainer'}); - var $progressValue = $('', {'class':'progressValue'}).text(Messages.upload_pending); - - var $tr = $('
', {id: id}).appendTo($table); - - var $cancel = $('', {'class': 'cancel fa fa-times'}).click(function () { - queue.queue = queue.queue.filter(function (el) { return el.id !== id; }); - $cancel.remove(); - $tr.find('.upCancel').text('-'); - $tr.find('.progressValue').text(Messages.upload_cancelled); - }); - - var $link = $('', { - 'class': 'upLink', - }).text(obj.metadata.name); - - $('