Merge branch 'staging' into newCk
This commit is contained in:
@@ -53,6 +53,9 @@ body {
|
||||
font-family: Calibri, Ubuntu, sans-serif;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#previewContainer media-tag * {
|
||||
max-width: 100%;
|
||||
}
|
||||
#preview {
|
||||
max-width: 40vw;
|
||||
margin: auto;
|
||||
|
||||
@@ -56,6 +56,9 @@ body {
|
||||
box-sizing: border-box;
|
||||
font-family: Calibri,Ubuntu,sans-serif;
|
||||
word-wrap: break-word;
|
||||
media-tag * {
|
||||
max-width:100%;
|
||||
}
|
||||
}
|
||||
|
||||
#preview {
|
||||
|
||||
@@ -9,6 +9,7 @@ define([
|
||||
'/common/cryptpad-common.js',
|
||||
'/common/cryptget.js',
|
||||
'/common/diffMarked.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js', // needed for media-tag
|
||||
], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad,
|
||||
Cryptget, DiffMd) {
|
||||
var Messages = Cryptpad.Messages;
|
||||
|
||||
@@ -20,5 +20,25 @@ define([], function () {
|
||||
};
|
||||
}
|
||||
|
||||
var failStore = function () {
|
||||
console.error(new Error('wut'));
|
||||
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");
|
||||
};
|
||||
|
||||
try {
|
||||
var test_key = 'localStorage_test';
|
||||
var testval = Math.random().toString();
|
||||
localStorage.setItem(test_key, testval);
|
||||
if (localStorage.getItem(test_key) !== testval) {
|
||||
failStore();
|
||||
}
|
||||
} catch (e) { console.error(e); failStore(); }
|
||||
|
||||
require([document.querySelector('script[data-bootload]').getAttribute('data-bootload')]);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
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, config) {
|
||||
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 = $('<table>', { id: 'uploadStatus' });
|
||||
var $thead = $('<tr>').appendTo($table);
|
||||
$('<td>').text(Messages.upload_name).appendTo($thead);
|
||||
$('<td>').text(Messages.upload_size).appendTo($thead);
|
||||
$('<td>').text(Messages.upload_progress).appendTo($thead);
|
||||
$('<td>').text(Messages.cancel).appendTo($thead);
|
||||
|
||||
var createTableContainer = function ($body) {
|
||||
File.$container = $('<div>', { id: 'uploadStatusContainer' }).append($table).appendTo($body);
|
||||
return File.$container;
|
||||
};
|
||||
|
||||
var getData = function (file, href) {
|
||||
var data = {};
|
||||
|
||||
data.name = file.metadata.name;
|
||||
data.url = href;
|
||||
if (file.metadata.type.slice(0,6) === 'image/') {
|
||||
data.mediatag = true;
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
var upload = function (file) {
|
||||
var blob = file.blob;
|
||||
var metadata = file.metadata;
|
||||
var id = file.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);
|
||||
common.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
|
||||
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 = common.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 = metadata.name;
|
||||
|
||||
common.renamePad(title || "", href, function (err) {
|
||||
if (err) { return void console.error(err); } // TODO
|
||||
console.log(title);
|
||||
common.log(Messages._getKey('upload_success', [title]));
|
||||
common.prepareFeedback('upload')();
|
||||
|
||||
if (config.onUploaded) {
|
||||
var data = getData(file, href);
|
||||
config.onUploaded(file.dropEvent, data);
|
||||
}
|
||||
|
||||
queue.inProgress = false;
|
||||
queue.next();
|
||||
});
|
||||
//Title.updateTitle(title || "", href);
|
||||
//APP.toolbar.title.show();
|
||||
});
|
||||
};
|
||||
|
||||
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 common.alert(Messages.upload_tooLarge);
|
||||
}
|
||||
if (e === 'NOT_ENOUGH_SPACE') {
|
||||
// TODO update table to say not enough space?
|
||||
return void common.alert(Messages.upload_notEnoughSpace);
|
||||
}
|
||||
console.error(e);
|
||||
return void common.alert(Messages.upload_serverError);
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
// TODO keep this message in case of pending files in another window?
|
||||
return void common.confirm(Messages.upload_uploadPending, function (yes) {
|
||||
if (!yes) { return; }
|
||||
common.uploadCancel(function (e, res) {
|
||||
if (e) {
|
||||
return void console.error(e);
|
||||
}
|
||||
console.log(res);
|
||||
next(again);
|
||||
});
|
||||
});
|
||||
}
|
||||
next(again);
|
||||
});
|
||||
};
|
||||
|
||||
var prettySize = function (bytes) {
|
||||
var kB = common.bytesToKilobytes(bytes);
|
||||
if (kB < 1024) { return kB + Messages.KB; }
|
||||
var mB = common.bytesToMegabytes(bytes);
|
||||
return mB + Messages.MB;
|
||||
};
|
||||
|
||||
queue.next = function () {
|
||||
if (queue.queue.length === 0) {
|
||||
queue.to = window.setTimeout(function () {
|
||||
if (config.keepTable) { return; }
|
||||
File.$container.fadeOut();
|
||||
}, 3000);
|
||||
return;
|
||||
}
|
||||
if (queue.inProgress) { return; }
|
||||
File.$container.show();
|
||||
var file = queue.queue.shift();
|
||||
upload(file);
|
||||
};
|
||||
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 = $('<div>', {'class':'progressContainer'});
|
||||
var $progressValue = $('<span>', {'class':'progressValue'}).text(Messages.upload_pending);
|
||||
|
||||
var $tr = $('<tr>', {id: id}).appendTo($table);
|
||||
|
||||
var $cancel = $('<span>', {'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 = $('<a>', {
|
||||
'class': 'upLink',
|
||||
'rel': 'noopener noreferrer'
|
||||
}).text(obj.metadata.name);
|
||||
|
||||
$('<td>').append($link).appendTo($tr);
|
||||
$('<td>').text(prettySize(estimate)).appendTo($tr);
|
||||
$('<td>', {'class': 'upProgress'}).append($progressBar).append($progressValue).appendTo($tr);
|
||||
$('<td>', {'class': 'upCancel'}).append($cancel).appendTo($tr);
|
||||
|
||||
queue.next();
|
||||
};
|
||||
|
||||
var handleFile = File.handleFile = function (file, e) {
|
||||
var reader = new FileReader();
|
||||
reader.onloadend = function () {
|
||||
queue.push({
|
||||
blob: this.result,
|
||||
metadata: {
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
},
|
||||
dropEvent: e
|
||||
});
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
};
|
||||
|
||||
var onFileDrop = File.onFileDrop = function (file, e) {
|
||||
Array.prototype.slice.call(file).forEach(function (d) {
|
||||
handleFile(d, e);
|
||||
});
|
||||
};
|
||||
|
||||
var createAreaHandlers = File.createDropArea = function ($area, $hoverArea) {
|
||||
var counter = 0;
|
||||
if (!$hoverArea) { $hoverArea = $area; }
|
||||
$hoverArea
|
||||
.on('dragenter', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
counter++;
|
||||
$hoverArea.addClass('hovering');
|
||||
})
|
||||
.on('dragleave', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
counter--;
|
||||
if (counter <= 0) {
|
||||
$hoverArea.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;
|
||||
$hoverArea.removeClass('hovering');
|
||||
onFileDrop(dropped, e);
|
||||
});
|
||||
};
|
||||
|
||||
var createUploader = function ($area, $hover, $body) {
|
||||
if (!config.noHandlers) {
|
||||
createAreaHandlers($area, null);
|
||||
}
|
||||
createTableContainer($body);
|
||||
};
|
||||
|
||||
createUploader(config.dropArea, config.hoverArea, config.body);
|
||||
|
||||
return File;
|
||||
};
|
||||
|
||||
return module;
|
||||
});
|
||||
@@ -37,6 +37,17 @@ define([
|
||||
|
||||
var parsed = config.href ? common.parsePadUrl(config.href) : {};
|
||||
var secret = common.getSecrets(parsed.type, parsed.hash);
|
||||
|
||||
History.readOnly = 1;
|
||||
if (!secret.keys) {
|
||||
secret.keys = secret.key;
|
||||
History.readOnly = 2;
|
||||
}
|
||||
else if (!secret.keys.validateKey) {
|
||||
secret.keys.validateKey = true;
|
||||
History.readOnly = 0;
|
||||
}
|
||||
|
||||
var crypto = Crypto.createEncryptor(secret.keys);
|
||||
|
||||
var to = window.setTimeout(function () {
|
||||
@@ -185,6 +196,7 @@ define([
|
||||
'class':'revertHistory buttonSuccess',
|
||||
title: Messages.history_restoreTitle
|
||||
}).text(Messages.history_restore).appendTo($nav);
|
||||
if (!History.readOnly) { $rev.hide(); }
|
||||
|
||||
onUpdate = function () {
|
||||
$cur.attr('max', states.length);
|
||||
|
||||
@@ -14,6 +14,7 @@ define(function () {
|
||||
var getHeadingText = cfg.getHeadingText || function () { return; };
|
||||
var updateLocalTitle = function (newTitle) {
|
||||
exp.title = newTitle;
|
||||
onLocal();
|
||||
if (typeof cfg.updateLocalTitle === "function") {
|
||||
cfg.updateLocalTitle(newTitle);
|
||||
} else {
|
||||
@@ -43,11 +44,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);
|
||||
|
||||
@@ -68,7 +68,11 @@ define([], function () {
|
||||
Util.replaceHash = function (hash) {
|
||||
if (window.history && window.history.replaceState) {
|
||||
if (!/^#/.test(hash)) { hash = '#' + hash; }
|
||||
return void window.history.replaceState({}, window.document.title, hash);
|
||||
void window.history.replaceState({}, window.document.title, hash);
|
||||
if (typeof(window.onhashchange) === 'function') {
|
||||
window.onhashchange();
|
||||
}
|
||||
return;
|
||||
}
|
||||
window.location.hash = hash;
|
||||
};
|
||||
|
||||
+100
-28
@@ -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 (config) { return Files.create(common, config); };
|
||||
|
||||
// History
|
||||
common.getHistory = function (config) { return History.create(common, config); };
|
||||
|
||||
@@ -126,6 +131,11 @@ define([
|
||||
return store.getProxy().proxy;
|
||||
}
|
||||
};
|
||||
common.getFO = function () {
|
||||
if (store && store.getProxy()) {
|
||||
return store.getProxy().fo;
|
||||
}
|
||||
};
|
||||
var getNetwork = common.getNetwork = function () {
|
||||
if (store) {
|
||||
if (store.getProxy() && store.getProxy().info) {
|
||||
@@ -144,7 +154,6 @@ define([
|
||||
}
|
||||
|
||||
var href = '/common/feedback.html?' + action + '=' + (+new Date());
|
||||
console.log('[feedback] %s', href);
|
||||
$.ajax({
|
||||
type: "HEAD",
|
||||
url: href,
|
||||
@@ -300,7 +309,7 @@ define([
|
||||
cb(parsed);
|
||||
}
|
||||
if (!pad.title) {
|
||||
pad.title = common.getDefaultname(parsed);
|
||||
pad.title = common.getDefaultName(parsed);
|
||||
}
|
||||
return parsed.hashData;
|
||||
};
|
||||
@@ -520,8 +529,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);
|
||||
@@ -581,19 +590,6 @@ define([
|
||||
return pad;
|
||||
});
|
||||
|
||||
if (!contains && href) {
|
||||
var data = makePad(href, name);
|
||||
getStore().pushData(data, function (e, id) {
|
||||
if (e) {
|
||||
if (e === 'E_OVER_LIMIT') {
|
||||
common.alert(Messages.pinLimitNotPinned, null, true);
|
||||
return;
|
||||
}
|
||||
else { throw new Error("Cannot push this pad to CryptDrive", e); }
|
||||
}
|
||||
getStore().addPad(id, common.initialPath);
|
||||
});
|
||||
}
|
||||
if (updateWeaker.length > 0) {
|
||||
updateWeaker.forEach(function (obj) {
|
||||
// If we have a stronger url, and if all the occurences of the weaker were
|
||||
@@ -601,6 +597,20 @@ define([
|
||||
getStore().restoreHref(obj.n);
|
||||
});
|
||||
}
|
||||
if (!contains && href) {
|
||||
var data = makePad(href, name);
|
||||
getStore().pushData(data, function (e, id) {
|
||||
if (e) {
|
||||
if (e === 'E_OVER_LIMIT') {
|
||||
common.alert(Messages.pinLimitNotPinned, null, true);
|
||||
}
|
||||
return void cb(e);
|
||||
}
|
||||
getStore().addPad(id, common.initialPath);
|
||||
cb(err, recent);
|
||||
});
|
||||
return;
|
||||
}
|
||||
cb(err, recent);
|
||||
});
|
||||
};
|
||||
@@ -621,24 +631,41 @@ 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);
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
callback(null, title);
|
||||
});
|
||||
};
|
||||
|
||||
common.getUserFilesList = function () {
|
||||
var store = common.getStore();
|
||||
var proxy = store.getProxy();
|
||||
var fo = proxy.fo;
|
||||
var hashes = [];
|
||||
var list = fo.getFiles().filter(function (id) {
|
||||
var href = fo.getFileData(id).href;
|
||||
var parsed = parsePadUrl(href);
|
||||
if ((parsed.type === 'file' || parsed.type === 'media')
|
||||
&& hashes.indexOf(parsed.hash) === -1) {
|
||||
hashes.push(parsed.hash);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return list;
|
||||
};
|
||||
|
||||
var getUserChannelList = common.getUserChannelList = function () {
|
||||
var store = common.getStore();
|
||||
var proxy = store.getProxy();
|
||||
@@ -879,6 +906,21 @@ define([
|
||||
common.getPinnedUsage(todo);
|
||||
};
|
||||
|
||||
var getAppSuffix = function () {
|
||||
var parts = window.location.pathname.split('/')
|
||||
.filter(function (x) { return x; });
|
||||
|
||||
if (!parts[0]) { return ''; }
|
||||
return '_' + parts[0].toUpperCase();
|
||||
};
|
||||
|
||||
var prepareFeedback = common.prepareFeedback = function (key) {
|
||||
if (typeof(key) !== 'string') { return $.noop; }
|
||||
return function () {
|
||||
feedback(key.toUpperCase() + getAppSuffix());
|
||||
};
|
||||
};
|
||||
|
||||
common.createButton = function (type, rightside, data, callback) {
|
||||
var button;
|
||||
var size = "17px";
|
||||
@@ -887,6 +929,8 @@ define([
|
||||
button = $('<button>', {
|
||||
title: Messages.exportButtonTitle,
|
||||
}).append($('<span>', {'class':'fa fa-download', style: 'font:'+size+' FontAwesome'}));
|
||||
|
||||
button.click(prepareFeedback(type));
|
||||
if (callback) {
|
||||
button.click(callback);
|
||||
}
|
||||
@@ -896,18 +940,40 @@ define([
|
||||
title: Messages.importButtonTitle,
|
||||
}).append($('<span>', {'class':'fa fa-upload', style: 'font:'+size+' FontAwesome'}));
|
||||
if (callback) {
|
||||
button.click(UI.importContent('text/plain', function (content, file) {
|
||||
button
|
||||
.click(prepareFeedback(type))
|
||||
.click(UI.importContent('text/plain', function (content, file) {
|
||||
callback(content, file);
|
||||
}));
|
||||
}
|
||||
break;
|
||||
case 'upload':
|
||||
button = $('<button>', {
|
||||
'class': 'btn btn-primary new',
|
||||
title: Messages.uploadButtonTitle,
|
||||
}).append($('<span>', {'class':'fa fa-upload'})).append(' '+Messages.uploadButton);
|
||||
if (!data.FM) { return; }
|
||||
var $input = $('<input>', {
|
||||
'type': 'file',
|
||||
'style': 'display: none;'
|
||||
}).on('change', function (e) {
|
||||
var file = e.target.files[0];
|
||||
var ev = {
|
||||
target: data.target
|
||||
};
|
||||
data.FM.handleFile(file, ev);
|
||||
if (callback) { callback(); }
|
||||
});
|
||||
button.click(function () { $input.click(); });
|
||||
break;
|
||||
case 'template':
|
||||
if (!AppConfig.enableTemplates) { return; }
|
||||
button = $('<button>', {
|
||||
title: Messages.saveTemplateButton,
|
||||
}).append($('<span>', {'class':'fa fa-bookmark', style: 'font:'+size+' FontAwesome'}));
|
||||
if (data.rt && data.Crypt) {
|
||||
button.click(function () {
|
||||
button
|
||||
.click(function () {
|
||||
var title = data.getTitle() || document.title;
|
||||
var todo = function (val) {
|
||||
if (typeof(val) !== "string") { return; }
|
||||
@@ -965,7 +1031,9 @@ define([
|
||||
}
|
||||
});
|
||||
if (callback) {
|
||||
button.click(function() {
|
||||
button
|
||||
.click(prepareFeedback(type))
|
||||
.click(function() {
|
||||
var href = window.location.href;
|
||||
var msg = isLoggedIn() ? Messages.forgetPrompt : Messages.fm_removePermanentlyDialog;
|
||||
common.confirm(msg, function (yes) {
|
||||
@@ -1021,7 +1089,9 @@ define([
|
||||
style: 'font:'+size+' FontAwesome'
|
||||
});
|
||||
if (data.histConfig) {
|
||||
button.click(function () {
|
||||
button
|
||||
.click(prepareFeedback(type))
|
||||
.click(function () {
|
||||
common.getHistory(data.histConfig);
|
||||
});
|
||||
}
|
||||
@@ -1030,7 +1100,8 @@ define([
|
||||
button = $('<button>', {
|
||||
'class': "fa fa-question",
|
||||
style: 'font:'+size+' FontAwesome'
|
||||
});
|
||||
})
|
||||
.click(prepareFeedback(type));
|
||||
}
|
||||
if (rightside) {
|
||||
button.addClass('rightside-button');
|
||||
@@ -1378,8 +1449,8 @@ define([
|
||||
initialized = true;
|
||||
|
||||
updateLocalVersion();
|
||||
|
||||
f(void 0, env);
|
||||
if (typeof(window.onhashchange) === 'function') { window.onhashchange(); }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1422,6 +1493,7 @@ define([
|
||||
|| parsedOld.channel !== parsedNew.channel
|
||||
|| parsedOld.mode !== parsedNew.mode
|
||||
|| parsedOld.key !== parsedNew.key)) {
|
||||
if (!parsedOld.channel) { oldHref = newHref; return; }
|
||||
document.location.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
define([
|
||||
'jquery',
|
||||
'/bower_components/marked/marked.min.js',
|
||||
'/bower_components/diff-dom/diffDOM.js'
|
||||
],function ($, Marked) {
|
||||
'/common/cryptpad-common.js',
|
||||
'/common/media-tag.js',
|
||||
'/bower_components/diff-dom/diffDOM.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
],function ($, Marked, Cryptpad, MediaTag) {
|
||||
var DiffMd = {};
|
||||
|
||||
var DiffDOM = window.diffDOM;
|
||||
@@ -33,6 +36,20 @@ define([
|
||||
var cls = (isCheckedTaskItem || isUncheckedTaskItem) ? ' class="todo-list-item"' : '';
|
||||
return '<li'+ cls + '>' + text + '</li>\n';
|
||||
};
|
||||
renderer.image = function (href, title, text) {
|
||||
if (href.slice(0,6) === '/file/') {
|
||||
var parsed = Cryptpad.parsePadUrl(href);
|
||||
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel);
|
||||
var mt = '<media-tag src="/blob/' + hexFileName.slice(0,2) + '/' + hexFileName + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>';
|
||||
return mt;
|
||||
}
|
||||
var out = '<img src="' + href + '" alt="' + text + '"';
|
||||
if (title) {
|
||||
out += ' title="' + title + '"';
|
||||
}
|
||||
out += this.options.xhtml ? '/>' : '>';
|
||||
return out;
|
||||
};
|
||||
|
||||
var forbiddenTags = [
|
||||
'SCRIPT',
|
||||
@@ -43,6 +60,10 @@ define([
|
||||
'AUDIO',
|
||||
];
|
||||
var unsafeTag = function (info) {
|
||||
if (info.node && $(info.node).parents('media-tag').length) {
|
||||
// Do not remove elements inside a media-tag
|
||||
return true;
|
||||
}
|
||||
if (['addAttribute', 'modifyAttribute'].indexOf(info.diff.action) !== -1) {
|
||||
if (/^on/.test(info.diff.name)) {
|
||||
console.log("Rejecting forbidden element attribute with name", info.diff.name);
|
||||
@@ -61,6 +82,7 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var slice = function (coll) {
|
||||
return Array.prototype.slice.call(coll);
|
||||
};
|
||||
@@ -85,7 +107,7 @@ define([
|
||||
var DD = new DiffDOM({
|
||||
preDiffApply: function (info) {
|
||||
if (unsafeTag(info)) { return true; }
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
var makeDiff = function (A, B, id) {
|
||||
@@ -119,9 +141,18 @@ define([
|
||||
throw new Error(patch);
|
||||
} else {
|
||||
DD.apply($content[0], patch);
|
||||
var $mts = $content.find('media-tag:not(:has(*))');
|
||||
$mts.each(function (i, el) {
|
||||
MediaTag(el);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$(window.document).on('decryption', function (e) {
|
||||
var decrypted = e.originalEvent;
|
||||
if (decrypted.callback) { decrypted.callback(); }
|
||||
});
|
||||
|
||||
return DiffMd;
|
||||
});
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -105,7 +105,7 @@ define([
|
||||
var oldFo = FO.init(parsed.drive, {
|
||||
Cryptpad: Cryptpad
|
||||
});
|
||||
var todo = function () {
|
||||
var onMigrated = function () {
|
||||
oldFo.fixFiles();
|
||||
var newData = Cryptpad.getStore().getProxy();
|
||||
var newFo = newData.fo;
|
||||
@@ -151,8 +151,10 @@ define([
|
||||
proxy.FS_hashes = [];
|
||||
}
|
||||
proxy.FS_hashes.push(localStorage.FS_hash);
|
||||
if (typeof(cb) === "function") { cb(); }
|
||||
};
|
||||
oldFo.migrate(todo);
|
||||
oldFo.migrate(onMigrated);
|
||||
return;
|
||||
}
|
||||
if (typeof(cb) === "function") { cb(); }
|
||||
};
|
||||
|
||||
+2
-1
@@ -63,6 +63,7 @@ types of messages:
|
||||
// RPC responses are arrays. this message isn't meant for us.
|
||||
return;
|
||||
}
|
||||
if (/FULL_HISTORY/.test(parsed[0])) { return; }
|
||||
|
||||
var response = parsed.slice(2);
|
||||
|
||||
@@ -98,7 +99,7 @@ types of messages:
|
||||
delete ctx.pending[txid];
|
||||
return;
|
||||
}
|
||||
console.error("received message for txid[%s] with no callback", txid);
|
||||
console.error("received message [%s] for txid[%s] with no callback", msg, txid);
|
||||
};
|
||||
|
||||
var create = function (network, edPrivateKey, edPublicKey, cb) {
|
||||
|
||||
@@ -96,6 +96,10 @@ define([
|
||||
} else {
|
||||
styleToolbar($container);
|
||||
}
|
||||
$container.on('drop dragover', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
return $toolbar;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+15
-9
@@ -132,7 +132,7 @@ span.fa-folder-open {
|
||||
width: calc(100% - 30px);
|
||||
}
|
||||
#tree li > span.element-row {
|
||||
width: calc(100% + 5px);
|
||||
min-width: calc(100% + 5px);
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin-left: -5px;
|
||||
@@ -223,6 +223,11 @@ span.fa-folder-open {
|
||||
display: none;
|
||||
}
|
||||
/* CONTENT */
|
||||
#rightCol {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
flex: 1;
|
||||
}
|
||||
#content {
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
@@ -340,9 +345,9 @@ span.fa-folder-open {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
margin: 8px 0;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#content div.grid li.element {
|
||||
position: relative;
|
||||
@@ -450,9 +455,6 @@ span.fa-folder-open {
|
||||
#driveToolbar {
|
||||
background: #ddd;
|
||||
color: #555;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: ;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
@@ -516,21 +518,25 @@ span.fa-folder-open {
|
||||
width: 250px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
#driveToolbar .rightside {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex: 1;
|
||||
display: inline-block;
|
||||
float: right;
|
||||
}
|
||||
#driveToolbar .path {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
cursor: default;
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
direction: rtl;
|
||||
max-width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
#driveToolbar .path .element {
|
||||
padding: 5px;
|
||||
|
||||
+19
-10
@@ -171,7 +171,7 @@ span {
|
||||
width: ~"calc(100% - 30px)";
|
||||
}
|
||||
& > span.element-row {
|
||||
width: ~"calc(100% + 5px)";
|
||||
min-width: ~"calc(100% + 5px)";
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin-left: -5px;
|
||||
@@ -272,7 +272,11 @@ span {
|
||||
}
|
||||
|
||||
/* CONTENT */
|
||||
|
||||
#rightCol {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
flex: 1;
|
||||
}
|
||||
#content {
|
||||
box-sizing: border-box;
|
||||
background: @content-bg;
|
||||
@@ -398,11 +402,12 @@ span {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
margin: 8px 0;
|
||||
display: inline-flex;
|
||||
display: inline-block;
|
||||
//align-items: center;
|
||||
justify-content: center;
|
||||
//justify-content: center;
|
||||
overflow: hidden;
|
||||
//text-overflow: ellipsis;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
&.element {
|
||||
position: relative;
|
||||
@@ -519,9 +524,9 @@ span {
|
||||
#driveToolbar {
|
||||
background: @toolbar-bg;
|
||||
color: @toolbar-fg;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
//height: 30px;
|
||||
//display: flex;
|
||||
//flex-flow: row;
|
||||
border-top: 1px solid @toolbar-border-col;
|
||||
border-bottom: ;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||
@@ -586,21 +591,25 @@ span {
|
||||
width: 250px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
.rightside {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex: 1;
|
||||
display: inline-block;
|
||||
float: right;
|
||||
}
|
||||
.path {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
cursor: default;
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
direction: rtl;
|
||||
max-width: 100%;
|
||||
text-align: left;
|
||||
.element {
|
||||
padding: 5px;
|
||||
border: 1px solid @toolbar-bg;
|
||||
|
||||
@@ -10,13 +10,14 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="toolbar" class="toolbar-container"></div>
|
||||
<div id="driveToolbar"></div>
|
||||
<div class="app-container" tabindex="0">
|
||||
<div id="tree">
|
||||
</div>
|
||||
<div id="content" tabindex="2">
|
||||
<div id="rightCol">
|
||||
<div id="driveToolbar"></div>
|
||||
<div id="content" tabindex="2"></div>
|
||||
</div>
|
||||
<div id="treeContextMenu" class="contextMenu dropdown clearfix">
|
||||
<div id="treeContextMenu" class="contextMenu dropdown clearfix unselectable">
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
|
||||
<li><a tabindex="-1" data-icon="fa-folder-open" class="open dropdown-item" data-localization="fc_open">Open</a></li>
|
||||
<li><a tabindex="-1" data-icon="fa-eye" class="open_ro dropdown-item" data-localization="fc_open_ro">Open (read-only)</a></li>
|
||||
@@ -26,7 +27,7 @@
|
||||
<li><a tabindex="-1" data-icon="fa-database" class="properties dropdown-item" data-localization="fc_prop">Properties</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="contentContextMenu" class="contextMenu dropdown clearfix">
|
||||
<div id="contentContextMenu" class="contextMenu dropdown clearfix unselectable">
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
|
||||
<li><a tabindex="-1" data-icon="fa-folder" class="newfolder editable dropdown-item" data-localization="fc_newfolder">New folder</a></li>
|
||||
<li><a tabindex="-1" data-icon="fa-file-word-o" class="newdoc own editable dropdown-item" data-type="pad" data-localization="button_newpad">New pad</a></li>
|
||||
@@ -36,7 +37,7 @@
|
||||
<li><a tabindex="-1" data-icon="fa-paint-brush" class="newdoc own editable dropdown-item" data-type="whiteboard" data-localization="button_newwhiteboard">New whiteboard</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="defaultContextMenu" class="contextMenu dropdown clearfix">
|
||||
<div id="defaultContextMenu" class="contextMenu dropdown clearfix unselectable">
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
|
||||
<li><a tabindex="-1" data-icon="fa-folder-open" class="open dropdown-item" data-localization="fc_open">Open</a></li>
|
||||
<li><a tabindex="-1" data-icon="fa-eye" class="open_ro dropdown-item" data-localization="fc_open_ro">Open (read-only)</a></li>
|
||||
@@ -44,12 +45,12 @@
|
||||
<li><a tabindex="-1" data-icon="fa-database" class="properties dropdown-item" data-localization="fc_prop">Properties</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="trashTreeContextMenu" class="contextMenu dropdown clearfix">
|
||||
<div id="trashTreeContextMenu" class="contextMenu dropdown clearfix unselectable">
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
|
||||
<li><a tabindex="-1" data-icon="fa-trash-o" class="empty editable dropdown-item" data-localization="fc_empty">Empty the trash</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="trashContextMenu" class="contextMenu dropdown clearfix">
|
||||
<div id="trashContextMenu" class="contextMenu dropdown clearfix unselectable">
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
|
||||
<li><a tabindex="-1" data-icon="fa-eraser" class="remove editable dropdown-item" data-localization="fc_remove">Delete permanently</a></li>
|
||||
<li><a tabindex="-1" data-icon="fa-repeat" class="restore editable dropdown-item" data-localization="fc_restore">Restore</a></li>
|
||||
|
||||
+90
-54
@@ -205,6 +205,17 @@ define([
|
||||
var $trashTreeContextMenu = $iframe.find("#trashTreeContextMenu");
|
||||
var $trashContextMenu = $iframe.find("#trashContextMenu");
|
||||
|
||||
$tree.on('drop dragover', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
$driveToolbar.on('drop dragover', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
|
||||
|
||||
// TOOLBAR
|
||||
|
||||
/* add a "change username" button */
|
||||
@@ -317,6 +328,7 @@ define([
|
||||
width: '0px',
|
||||
height: '0px'
|
||||
});
|
||||
module.hideMenu(e);
|
||||
if (sel.move) { return; }
|
||||
sel.move = function (ev) {
|
||||
var rectMove = ev.currentTarget.getBoundingClientRect(),
|
||||
@@ -638,12 +650,7 @@ define([
|
||||
};
|
||||
|
||||
var updatePathSize = function () {
|
||||
var $context = $iframe.find('#contextButtonsContainer');
|
||||
var l = 50;
|
||||
if ($context.length) {
|
||||
l += $context.width() || 0;
|
||||
}
|
||||
$driveToolbar.find('.path').css('max-width', 'calc(100vw - '+$tree.width()+'px - '+l+'px)');
|
||||
$driveToolbar.find('.path').css('max-width', 'calc(100vw - '+$tree.width()+'px - 50px)');
|
||||
};
|
||||
|
||||
var getSelectedPaths = function ($element) {
|
||||
@@ -954,23 +961,7 @@ define([
|
||||
if (filesOp.isPathIn(newPath, [TRASH]) && paths.length && paths[0][0] === TRASH) {
|
||||
return;
|
||||
}
|
||||
// "force" is currently unused but may be configurable by user
|
||||
if (newPath[0] !== TRASH || force) {
|
||||
andThen();
|
||||
return;
|
||||
}
|
||||
var msg = Messages._getKey('fm_removeSeveralDialog', [paths.length]);
|
||||
if (paths.length === 1) {
|
||||
var path = paths[0].slice();
|
||||
var el = filesOp.find(path);
|
||||
var name = filesOp.isFile(el) ? getElementName(path) : path.pop();
|
||||
msg = Messages._getKey('fm_removeDialog', [name]);
|
||||
}
|
||||
Cryptpad.confirm(msg, function (res) {
|
||||
$(ifrw).focus();
|
||||
if (!res) { return; }
|
||||
andThen();
|
||||
});
|
||||
andThen();
|
||||
};
|
||||
// Drag & drop:
|
||||
// The data transferred is a stringified JSON containing the path of the dragged element
|
||||
@@ -1012,13 +1003,30 @@ define([
|
||||
ev.dataTransfer.setData("text", stringify(data));
|
||||
};
|
||||
|
||||
var onFileDrop = APP.onFileDrop = function (file, e) {
|
||||
APP.FM.onFileDrop(file, e);
|
||||
};
|
||||
var findDropPath = function (target) {
|
||||
var $target = $(target);
|
||||
var $el = findDataHolder($target);
|
||||
var newPath = $el.data('path');
|
||||
if ((!newPath || filesOp.isFile(filesOp.find(newPath)))
|
||||
&& $target.parents('#content')) {
|
||||
newPath = currentPath;
|
||||
}
|
||||
return newPath;
|
||||
};
|
||||
var onDrop = function (ev) {
|
||||
ev.preventDefault();
|
||||
$iframe.find('.droppable').removeClass('droppable');
|
||||
var data = ev.dataTransfer.getData("text");
|
||||
|
||||
// Don't the the normal drop handler for file upload
|
||||
var fileDrop = ev.dataTransfer.files;
|
||||
if (fileDrop.length) { return void onFileDrop(fileDrop, ev); }
|
||||
|
||||
var oldPaths = JSON.parse(data).path;
|
||||
if (!oldPaths) { return; }
|
||||
|
||||
// Dropped elements can be moved from the same file manager or imported from another one.
|
||||
// A moved element should be removed from its previous location
|
||||
var movedPaths = [];
|
||||
@@ -1032,8 +1040,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
var $el = findDataHolder($(ev.target));
|
||||
var newPath = $el.data('path');
|
||||
var newPath = findDropPath(ev.target);
|
||||
if (!newPath) { return; }
|
||||
if (movedPaths && movedPaths.length) {
|
||||
moveElements(movedPaths, newPath, null, refresh);
|
||||
@@ -1069,6 +1076,8 @@ define([
|
||||
e.preventDefault();
|
||||
});
|
||||
$element.on('drop', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onDrop(e.originalEvent);
|
||||
});
|
||||
$element.on('dragenter', function (e) {
|
||||
@@ -1087,6 +1096,7 @@ define([
|
||||
}
|
||||
});
|
||||
};
|
||||
addDragAndDropHandlers($content, null, true, true);
|
||||
|
||||
// In list mode, display metadata from the filesData object
|
||||
// _WORKGROUP_ : Do not display title, atime and ctime columns since we don't have files data
|
||||
@@ -1247,7 +1257,7 @@ define([
|
||||
var createTitle = function (path, noStyle) {
|
||||
if (!path || path.length === 0) { return; }
|
||||
var isTrash = filesOp.isPathIn(path, [TRASH]);
|
||||
var $title = $('<span>', {'class': 'path unselectable'});
|
||||
var $title = $driveToolbar.find('.path');
|
||||
if (APP.mobile()) {
|
||||
return $title;
|
||||
}
|
||||
@@ -1427,6 +1437,16 @@ define([
|
||||
return $block;
|
||||
};
|
||||
|
||||
var createUploadButton = function () {
|
||||
var inTrash = filesOp.isPathIn(currentPath, [TRASH]);
|
||||
if (inTrash) { return; }
|
||||
var data = {
|
||||
FM: APP.FM,
|
||||
target: $content[0]
|
||||
};
|
||||
return Cryptpad.createButton('upload', false, data);
|
||||
};
|
||||
|
||||
var hideNewButton = function () {
|
||||
$iframe.find('.dropdown-bar-content').hide();
|
||||
};
|
||||
@@ -1599,11 +1619,9 @@ define([
|
||||
var createToolbar = function () {
|
||||
var $toolbar = $driveToolbar;
|
||||
$toolbar.html('');
|
||||
var $leftside = $('<div>', {'class': 'leftside'}).appendTo($toolbar);
|
||||
if (!APP.mobile()) {
|
||||
$leftside.width($tree.width());
|
||||
}
|
||||
$('<div>', {'class': 'leftside'}).appendTo($toolbar);
|
||||
$('<div>', {'class': 'rightside'}).appendTo($toolbar);
|
||||
$('<div>', {'class': 'path unselectable'}).appendTo($toolbar);
|
||||
return $toolbar;
|
||||
};
|
||||
|
||||
@@ -1765,6 +1783,7 @@ define([
|
||||
module.hideMenu();
|
||||
if (!APP.editable) { debug("Read-only mode"); }
|
||||
if (!appStatus.isReady && !force) { return; }
|
||||
|
||||
// Only Trash and Root are available in not-owned files manager
|
||||
if (displayedCategories.indexOf(path[0]) === -1) {
|
||||
log(Messages.categoryError);
|
||||
@@ -1798,7 +1817,6 @@ define([
|
||||
if (!isSearch) { delete APP.Search.oldLocation; }
|
||||
|
||||
module.resetTree();
|
||||
|
||||
if (displayedCategories.indexOf(SEARCH) !== -1 && $tree.find('#searchInput').length) {
|
||||
// in history mode we want to focus the version number input
|
||||
if (!history.isHistoryMode && !APP.mobile()) {
|
||||
@@ -1828,7 +1846,7 @@ define([
|
||||
}
|
||||
var $list = $('<ul>').appendTo($dirContent);
|
||||
|
||||
createTitle(path).appendTo($toolbar.find('.rightside'));
|
||||
createTitle(path).appendTo($toolbar.find('.path'));
|
||||
updatePathSize();
|
||||
|
||||
if (APP.mobile()) {
|
||||
@@ -1863,6 +1881,7 @@ define([
|
||||
|
||||
// NewButton can be undefined if we're in read only mode
|
||||
$toolbar.find('.leftside').append(createNewButton(isInRoot));
|
||||
$toolbar.find('.leftside').append(createUploadButton());
|
||||
|
||||
|
||||
var $folderHeader = getFolderListHeader();
|
||||
@@ -2375,8 +2394,7 @@ define([
|
||||
var name = paths[0].path[paths[0].path.length - 1];
|
||||
if ($(this).hasClass("remove")) {
|
||||
if (paths.length === 1) {
|
||||
if (path.length === 4) { name = path[1]; }
|
||||
Cryptpad.confirm(Messages._getKey("fm_removePermanentlyDialog", [name]), function(res) {
|
||||
Cryptpad.confirm(Messages.fm_removePermanentlyDialog, function(res) {
|
||||
if (!res) { return; }
|
||||
filesOp.delete([path], refresh);
|
||||
});
|
||||
@@ -2392,7 +2410,14 @@ define([
|
||||
}
|
||||
else if ($(this).hasClass("restore")) {
|
||||
if (paths.length !== 1) { return; }
|
||||
if (path.length === 4) { name = path[1]; }
|
||||
if (path.length === 4) {
|
||||
var el = filesOp.find(path);
|
||||
if (filesOp.isFile(el)) {
|
||||
name = filesOp.getTitle(el);
|
||||
} else {
|
||||
name = path[1];
|
||||
}
|
||||
}
|
||||
Cryptpad.confirm(Messages._getKey("fm_restoreDialog", [name]), function(res) {
|
||||
if (!res) { return; }
|
||||
filesOp.restore(path, refresh);
|
||||
@@ -2413,7 +2438,7 @@ define([
|
||||
e.preventDefault();
|
||||
});
|
||||
$appContainer.on('mouseup', function (e) {
|
||||
if (sel.down) { return; }
|
||||
//if (sel.down) { return; }
|
||||
if (e.which !== 1) { return ; }
|
||||
module.hideMenu(e);
|
||||
//removeSelected(e);
|
||||
@@ -2511,22 +2536,10 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
$iframe.find('#tree').mousedown(function () {
|
||||
if (APP.mobile()) { return; }
|
||||
if (APP.resizeTree) { return; }
|
||||
APP.resizeTree = window.setInterval(function () {
|
||||
$driveToolbar.find('.leftside').width($tree.width());
|
||||
updatePathSize();
|
||||
}, 100);
|
||||
});
|
||||
$appContainer.mouseup(function () {
|
||||
window.clearInterval(APP.resizeTree);
|
||||
APP.resizeTree = undefined;
|
||||
});
|
||||
|
||||
history.onEnterHistory = function (obj) {
|
||||
var files = obj.drive;
|
||||
filesOp = FO.init(files, config);
|
||||
appStatus.isReady = true;
|
||||
refresh();
|
||||
};
|
||||
history.onLeaveHistory = function () {
|
||||
@@ -2550,8 +2563,8 @@ define([
|
||||
filesOp.pushData(data, function (e, id) {
|
||||
if (e) { return void console.error("Error while creating the default pad:", e); } // TODO LIMIT?
|
||||
filesOp.add(id);
|
||||
if (typeof(cb) === "function") { cb(); }
|
||||
});
|
||||
if (typeof(cb) === "function") { cb(); }
|
||||
});
|
||||
delete sessionStorage.createReadme;
|
||||
return;
|
||||
@@ -2559,6 +2572,29 @@ define([
|
||||
if (typeof(cb) === "function") { cb(); }
|
||||
};
|
||||
|
||||
var fmConfig = {
|
||||
noHandlers: true,
|
||||
onUploaded: function (ev, data) {
|
||||
try {
|
||||
// Get the folder path
|
||||
var newPath = findDropPath(ev.target);
|
||||
if (!newPath) { return void refresh(); }
|
||||
var href = data.url;
|
||||
// Get the current file location in ROOT
|
||||
var id = filesOp.getIdFromHref(href);
|
||||
var paths = filesOp.findFile(id);
|
||||
if (paths.length !== 1) { return; }
|
||||
// Try to move and refresh
|
||||
moveElements([paths[0]], newPath, true);
|
||||
refresh();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
};
|
||||
APP.FM = Cryptpad.createFileManager(fmConfig);
|
||||
|
||||
createReadme(proxy, function () {
|
||||
refresh();
|
||||
APP.userList.onChange();
|
||||
@@ -2657,7 +2693,7 @@ define([
|
||||
|
||||
var userList = APP.userList = info.userList;
|
||||
var config = {
|
||||
displayed: ['useradmin', 'spinner', 'lag', 'state', 'limit'],
|
||||
displayed: ['useradmin', 'spinner', 'lag', 'state', 'limit', 'newpad'],
|
||||
userList: {
|
||||
list: userList,
|
||||
userNetfluxId: info.myID
|
||||
@@ -2689,7 +2725,7 @@ define([
|
||||
if (err) { return void logError(err); }
|
||||
$leftside.html('');
|
||||
$leftside.append($limitContainer);
|
||||
});
|
||||
}, true);
|
||||
|
||||
/* add a history button */
|
||||
var histConfig = {
|
||||
@@ -2704,7 +2740,7 @@ define([
|
||||
history.onEnterHistory(obj);
|
||||
},
|
||||
$toolbar: APP.$bar,
|
||||
href: window.location.origin + window.location.pathname + APP.hash
|
||||
href: window.location.origin + window.location.pathname + '#' + APP.hash
|
||||
};
|
||||
var $hist = Cryptpad.createButton('history', true, {histConfig: histConfig});
|
||||
$rightside.append($hist);
|
||||
|
||||
+35
-45
@@ -6,6 +6,19 @@ body {
|
||||
#toolbar {
|
||||
display: flex;
|
||||
}
|
||||
body {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
#app {
|
||||
flex: 1;
|
||||
background: url('/customize/bg3.jpg') no-repeat center center;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.cryptpad-toolbar {
|
||||
padding: 0px;
|
||||
display: inline-block;
|
||||
@@ -25,6 +38,10 @@ body {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
media-tag img {
|
||||
max-width: 100%;
|
||||
max-height: calc(100vh - 64px);
|
||||
}
|
||||
#upload-form,
|
||||
#download-form {
|
||||
padding: 0px;
|
||||
@@ -48,6 +65,19 @@ body {
|
||||
height: 50vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#download-form label {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#download-form label span {
|
||||
width: 50vh;
|
||||
max-width: 80vw;
|
||||
text-align: center;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
.hovering {
|
||||
background-color: rgba(255, 0, 115, 0.5) !important;
|
||||
}
|
||||
@@ -58,20 +88,14 @@ body {
|
||||
display: none;
|
||||
}
|
||||
.inputfile + label {
|
||||
border: 2px solid black;
|
||||
background-color: rgba(50, 50, 50, 0.1);
|
||||
display: block;
|
||||
}
|
||||
.inputfile:focus + label,
|
||||
.inputfile + label:hover {
|
||||
background-color: rgba(50, 50, 50, 0.3);
|
||||
}
|
||||
#progress {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
transition: width 500ms;
|
||||
transition: width 200ms;
|
||||
width: 0%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
@@ -79,42 +103,8 @@ body {
|
||||
z-index: 10000;
|
||||
display: block;
|
||||
}
|
||||
#status {
|
||||
display: none;
|
||||
width: 80vw;
|
||||
margin-top: 50px;
|
||||
margin-left: 10vw;
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
#status tr:nth-child(1) {
|
||||
background-color: #ccc;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
#status tr:nth-child(1) td {
|
||||
text-align: center;
|
||||
}
|
||||
#status td {
|
||||
border-left: 1px solid #BBB;
|
||||
border-right: 1px solid #BBB;
|
||||
padding: 0 10px;
|
||||
}
|
||||
#status .upProgress {
|
||||
width: 200px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
#status .progressContainer {
|
||||
position: absolute;
|
||||
width: 0px;
|
||||
left: 5px;
|
||||
top: 1px;
|
||||
bottom: 1px;
|
||||
background-color: rgba(0, 0, 255, 0.3);
|
||||
}
|
||||
#status .upCancel {
|
||||
text-align: center;
|
||||
}
|
||||
#status .fa.cancel {
|
||||
color: #ff0073;
|
||||
body #uploadStatusContainer {
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
color: black;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
+43
-38
@@ -12,6 +12,19 @@ html, body {
|
||||
display: flex; // We need this to remove a 3px border at the bottom of the toolbar
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
#app {
|
||||
flex: 1;
|
||||
background: url('/customize/bg3.jpg') no-repeat center center;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.cryptpad-toolbar {
|
||||
padding: 0px;
|
||||
display: inline-block;
|
||||
@@ -32,6 +45,13 @@ html, body {
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
media-tag {
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: ~"calc(100vh - 64px)";
|
||||
}
|
||||
}
|
||||
|
||||
#upload-form, #download-form {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
@@ -54,6 +74,21 @@ html, body {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
#download-form {
|
||||
label {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
span {
|
||||
width: 50vh;
|
||||
max-width: 80vw;
|
||||
text-align: center;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.hovering {
|
||||
background-color: rgba(255, 0, 115, 0.5) !important;
|
||||
}
|
||||
@@ -65,14 +100,14 @@ html, body {
|
||||
display: none;
|
||||
}
|
||||
.inputfile + label {
|
||||
border: 2px solid black;
|
||||
background-color: rgba(50, 50, 50, .10);
|
||||
//border: 2px solid black;
|
||||
//background-color: rgba(50, 50, 50, .10);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.inputfile:focus + label,
|
||||
.inputfile + label:hover {
|
||||
background-color: rgba(50, 50, 50, 0.30);
|
||||
//background-color: rgba(50, 50, 50, 0.30);
|
||||
}
|
||||
|
||||
#progress {
|
||||
@@ -82,7 +117,7 @@ html, body {
|
||||
height: 100%;
|
||||
|
||||
|
||||
transition: width 500ms;
|
||||
transition: width 200ms;
|
||||
width: 0%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
@@ -91,38 +126,8 @@ html, body {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#status {
|
||||
display: none;
|
||||
width: 80vw;
|
||||
margin-top: 50px;
|
||||
margin-left: 10vw;
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
tr:nth-child(1) {
|
||||
background-color: #ccc;
|
||||
border: 1px solid #999;
|
||||
td { text-align: center; }
|
||||
}
|
||||
td {
|
||||
border-left: 1px solid #BBB;
|
||||
border-right: 1px solid #BBB;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.upProgress {
|
||||
width: 200px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
.progressContainer {
|
||||
position: absolute;
|
||||
width: 0px;
|
||||
left: 5px;
|
||||
top: 1px; bottom: 1px;
|
||||
background-color: rgba(0,0,255,0.3);
|
||||
}
|
||||
.upCancel { text-align: center; }
|
||||
.fa.cancel {
|
||||
color: rgb(255, 0, 115);
|
||||
}
|
||||
body #uploadStatusContainer {
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
color: black;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
|
||||
+16
-20
@@ -10,26 +10,22 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="toolbar" class="toolbar-container"></div>
|
||||
<div id="upload-form" style="display: none;">
|
||||
<input type="file" name="file" id="file" class="inputfile" />
|
||||
<label for="file" class="block unselectable" data-localization-title="upload_choose"
|
||||
data-localization="upload_choose"></label>
|
||||
</div>
|
||||
<div id="download-form" style="display: none;">
|
||||
<input type="button" name="dl" id="dl" class="inputfile" />
|
||||
<label for="dl" class="block unselectable" data-localization-title="download_button"
|
||||
data-localization="download_button"></label>
|
||||
<span class="block" id="progress"></span>
|
||||
</div>
|
||||
<table id="status" style="display: none;">
|
||||
<tr>
|
||||
<td data-localization="upload_name">File name</td>
|
||||
<td data-localization="upload_size">Size</td>
|
||||
<td data-localization="upload_progress">Progress</td>
|
||||
<td data-localization="cancel">Cancel</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="feedback" class="block hidden">
|
||||
<div id="app">
|
||||
<div id="upload-form" style="display: none;">
|
||||
<input type="file" name="file" id="file" class="inputfile" />
|
||||
<label for="file" class="btn btn-primary block unselectable" data-localization-title="upload_choose"
|
||||
data-localization="upload_choose"></label>
|
||||
</div>
|
||||
<div id="download-form" style="display: none;">
|
||||
<input type="button" name="dl" id="dl" class="inputfile" />
|
||||
<label for="dl" class="btn btn-success block unselectable" data-localization-title="download_button"><span data-localization="download_button"></span></label>
|
||||
<span class="block" id="progress"></span>
|
||||
</div>
|
||||
<div id="download-view" style="display: none;">
|
||||
<media-tag id="encryptedFile"></media-tag>
|
||||
</div>
|
||||
<div id="feedback" class="block hidden">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+118
-283
@@ -7,209 +7,38 @@ define([
|
||||
'/common/visible.js',
|
||||
'/common/notify.js',
|
||||
'/file/file-crypto.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
'/bower_components/file-saver/FileSaver.min.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
], function ($, Crypto, realtimeInput, Toolbar, Cryptpad, Visible, Notify, FileCrypto) {
|
||||
var Messages = Cryptpad.Messages;
|
||||
var saveAs = window.saveAs;
|
||||
var Nacl = window.nacl;
|
||||
|
||||
var APP = {};
|
||||
var APP = window.APP = {};
|
||||
|
||||
$(function () {
|
||||
|
||||
var ifrw = $('#pad-iframe')[0].contentWindow;
|
||||
var $iframe = $('#pad-iframe').contents();
|
||||
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');
|
||||
|
||||
$iframe.find('body').on('dragover', function (e) { e.preventDefault(); });
|
||||
$iframe.find('body').on('drop', function (e) { e.preventDefault(); });
|
||||
|
||||
Cryptpad.addLoadingScreen();
|
||||
|
||||
var Title;
|
||||
|
||||
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 = $('<div>', {'class':'progressContainer'});
|
||||
var $progressValue = $('<span>', {'class':'progressValue'}).text(Messages.upload_pending);
|
||||
|
||||
var $tr = $('<tr>', {id: id}).appendTo($table);
|
||||
|
||||
var $cancel = $('<span>', {'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 = $('<a>', {
|
||||
'class': 'upLink',
|
||||
}).text(obj.metadata.name);
|
||||
|
||||
$('<td>').append($link).appendTo($tr);
|
||||
$('<td>').text(prettySize(estimate)).appendTo($tr);
|
||||
$('<td>', {'class': 'upProgress'}).append($progressBar).append($progressValue).appendTo($tr);
|
||||
$('<td>', {'class': 'upCancel'}).append($cancel).appendTo($tr);
|
||||
|
||||
queue.next();
|
||||
};
|
||||
|
||||
var uploadMode = false;
|
||||
|
||||
var andThen = function () {
|
||||
var ifrw = $('#pad-iframe')[0].contentWindow;
|
||||
var $iframe = $('#pad-iframe').contents();
|
||||
var $appContainer = $iframe.find('#app');
|
||||
var $form = $iframe.find('#upload-form');
|
||||
var $dlform = $iframe.find('#download-form');
|
||||
var $dlview = $iframe.find('#download-view');
|
||||
var $label = $form.find('label');
|
||||
var $dllabel = $dlform.find('label span');
|
||||
var $progress = $iframe.find('#progress');
|
||||
var $body = $iframe.find('body');
|
||||
|
||||
$body.on('dragover', function (e) { e.preventDefault(); });
|
||||
$body.on('drop', function (e) { e.preventDefault(); });
|
||||
|
||||
Cryptpad.addLoadingScreen();
|
||||
|
||||
var Title;
|
||||
|
||||
var uploadMode = false;
|
||||
|
||||
var $bar = $iframe.find('.toolbar-container');
|
||||
|
||||
var secret;
|
||||
@@ -229,13 +58,6 @@ define([
|
||||
return data ? data.title : undefined;
|
||||
};
|
||||
|
||||
var exportFile = function () {
|
||||
var filename = Cryptpad.fixFileName(document.title);
|
||||
if (!(typeof(filename) === 'string' && filename)) { return; }
|
||||
var blob = new Blob([myFile], {type: myDataType});
|
||||
saveAs(blob, filename);
|
||||
};
|
||||
|
||||
Title = Cryptpad.createTitle({}, function(){}, Cryptpad);
|
||||
|
||||
var displayed = ['title', 'useradmin', 'newpad', 'limit', 'upgrade'];
|
||||
@@ -257,60 +79,112 @@ define([
|
||||
|
||||
if (uploadMode) { toolbar.title.hide(); }
|
||||
|
||||
var $rightside = toolbar.$rightside;
|
||||
|
||||
var $export = Cryptpad.createButton('export', true, {}, exportFile);
|
||||
$rightside.append($export);
|
||||
|
||||
Title.updateTitle(Cryptpad.initialName || getTitle() || Title.defaultTitle);
|
||||
|
||||
if (!uploadMode) {
|
||||
$dlform.show();
|
||||
var src = Cryptpad.getBlobPathFromHex(hexFileName);
|
||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
||||
var key = Nacl.util.decodeBase64(cryptKey);
|
||||
|
||||
|
||||
FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) {
|
||||
if (e) { return void console.error(e); }
|
||||
var title = document.title = metadata.name;
|
||||
Title.updateTitle(title || Title.defaultTitle);
|
||||
|
||||
Cryptpad.removeLoadingScreen();
|
||||
var decrypting = false;
|
||||
$dlform.find('#dl, #progress').click(function () {
|
||||
if (decrypting) { return; }
|
||||
if (myFile) { return void exportFile(); }
|
||||
decrypting = true;
|
||||
var displayFile = function (ev) {
|
||||
var $mt = $dlview.find('media-tag');
|
||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
||||
var hexFileName = Cryptpad.base64ToHex(secret.channel);
|
||||
$mt.attr('src', '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName);
|
||||
$mt.attr('data-crypto-key', 'cryptpad:'+cryptKey);
|
||||
|
||||
return Cryptpad.fetch(src, function (e, u8) {
|
||||
if (e) {
|
||||
decrypting = false;
|
||||
return void Cryptpad.alert(e);
|
||||
$(window.document).on('decryption', function (e) {
|
||||
var decrypted = e.originalEvent;
|
||||
if (decrypted.callback) { decrypted.callback(); }
|
||||
|
||||
console.log(decrypted);
|
||||
$dlview.show();
|
||||
$dlform.hide();
|
||||
var $dlButton = $dlview.find('media-tag button');
|
||||
if (ev) { $dlButton.click(); }
|
||||
if (!$dlButton.length) {
|
||||
$appContainer.css('background', 'white');
|
||||
}
|
||||
$dlButton.addClass('btn btn-success');
|
||||
|
||||
// now decrypt the u8
|
||||
if (!u8 || !u8.length) {
|
||||
return void Cryptpad.errorLoadingScreen(e);
|
||||
}
|
||||
toolbar.$rightside.append(Cryptpad.createButton('export', true, {}, function () {
|
||||
saveAs(decrypted.blob, decrypted.metadata.name);
|
||||
}))
|
||||
.append(Cryptpad.createButton('forget', true, {}, function () {
|
||||
// not sure what to do here
|
||||
}));
|
||||
|
||||
FileCrypto.decrypt(u8, key, function (e, data) {
|
||||
if (e) {
|
||||
decrypting = false;
|
||||
return console.error(e);
|
||||
}
|
||||
console.log(data);
|
||||
var title = document.title = data.metadata.name;
|
||||
myFile = data.content;
|
||||
myDataType = data.metadata.type;
|
||||
Title.updateTitle(title || Title.defaultTitle);
|
||||
exportFile();
|
||||
decrypting = false;
|
||||
}, function (progress) {
|
||||
var p = progress * 100 +'%';
|
||||
$progress.width(p);
|
||||
console.error(progress);
|
||||
// make pdfs big
|
||||
$iframe.find('media-tag iframe').css({
|
||||
'height': 'calc(100vh - 64px)',
|
||||
width: 'calc(100vw - 15px)',
|
||||
});
|
||||
})
|
||||
.on('decryptionError', function (e) {
|
||||
var error = e.originalEvent;
|
||||
Cryptpad.alert(error.message);
|
||||
})
|
||||
.on('decryptionProgress', function (e) {
|
||||
var progress = e.originalEvent;
|
||||
var p = progress.percent +'%';
|
||||
$progress.width(p);
|
||||
console.log(progress.percent);
|
||||
});
|
||||
|
||||
require(['/common/media-tag.js'], function (MediaTag) {
|
||||
/**
|
||||
* Allowed mime types that have to be set for a rendering after a decryption.
|
||||
*
|
||||
* @type {Array}
|
||||
*/
|
||||
var allowedMediaTypes = [
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/gif',
|
||||
'audio/mp3',
|
||||
'audio/ogg',
|
||||
'audio/wav',
|
||||
'audio/webm',
|
||||
'video/mp4',
|
||||
'video/ogg',
|
||||
'video/webm',
|
||||
'application/pdf',
|
||||
'application/dash+xml',
|
||||
'download'
|
||||
];
|
||||
MediaTag.CryptoFilter.setAllowedMediaTypes(allowedMediaTypes);
|
||||
|
||||
MediaTag($mt[0]);
|
||||
});
|
||||
};
|
||||
|
||||
var todoBigFile = function (sizeMb) {
|
||||
$dlform.show();
|
||||
Cryptpad.removeLoadingScreen();
|
||||
$dllabel.append($('<br>'));
|
||||
$dllabel.append(metadata.name);
|
||||
$dllabel.append($('<br>'));
|
||||
$dllabel.append(Messages._getKey('formattedMB', [sizeMb]));
|
||||
var decrypting = false;
|
||||
var onClick = function (ev) {
|
||||
if (decrypting) { return; }
|
||||
decrypting = true;
|
||||
displayFile(ev);
|
||||
};
|
||||
if (sizeMb < 5) { return void onClick(); }
|
||||
$dlform.find('#dl, #progress').click(onClick);
|
||||
};
|
||||
Cryptpad.getFileSize(window.location.href, function (e, data) {
|
||||
if (e) { return void Cryptpad.errorLoadingScreen(e); }
|
||||
var size = Cryptpad.bytesToMegabytes(data);
|
||||
return void todoBigFile(size);
|
||||
});
|
||||
});
|
||||
return;
|
||||
@@ -329,57 +203,18 @@ define([
|
||||
display: 'block',
|
||||
});
|
||||
|
||||
var 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 fmConfig = {
|
||||
dropArea: $form,
|
||||
hoverArea: $label,
|
||||
body: $body,
|
||||
keepTable: true // Don't fadeOut the tbale with the uploaded files
|
||||
};
|
||||
|
||||
var FM = Cryptpad.createFileManager(fmConfig);
|
||||
|
||||
$form.find("#file").on('change', function (e) {
|
||||
var file = e.target.files[0];
|
||||
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.handleFile(file);
|
||||
});
|
||||
|
||||
// we're in upload mode
|
||||
|
||||
+15
-3
@@ -64,7 +64,12 @@ define([
|
||||
$('button.login').click();
|
||||
});
|
||||
|
||||
var hashing = false;
|
||||
$('button.login').click(function () {
|
||||
if (hashing) { return void console.log("hashing is already in progress"); }
|
||||
|
||||
hashing = true;
|
||||
|
||||
// setTimeout 100ms to remove the keyboard on mobile devices before the loading screen pops up
|
||||
window.setTimeout(function () {
|
||||
Cryptpad.addLoadingScreen(Messages.login_hashing);
|
||||
@@ -89,6 +94,7 @@ define([
|
||||
Cryptpad.feedback('LOGIN', true);
|
||||
Cryptpad.whenRealtimeSyncs(result.realtime, function() {
|
||||
Cryptpad.login(result.userHash, result.userName, function () {
|
||||
hashing = false;
|
||||
if (sessionStorage.redirectTo) {
|
||||
var h = sessionStorage.redirectTo;
|
||||
var parser = document.createElement('a');
|
||||
@@ -107,17 +113,23 @@ define([
|
||||
switch (err) {
|
||||
case 'NO_SUCH_USER':
|
||||
Cryptpad.removeLoadingScreen(function () {
|
||||
Cryptpad.alert(Messages.login_noSuchUser);
|
||||
Cryptpad.alert(Messages.login_noSuchUser, function () {
|
||||
hashing = false;
|
||||
});
|
||||
});
|
||||
break;
|
||||
case 'INVAL_USER':
|
||||
Cryptpad.removeLoadingScreen(function () {
|
||||
Cryptpad.alert(Messages.login_invalUser);
|
||||
Cryptpad.alert(Messages.login_invalUser, function () {
|
||||
hashing = false;
|
||||
});
|
||||
});
|
||||
break;
|
||||
case 'INVAL_PASS':
|
||||
Cryptpad.removeLoadingScreen(function () {
|
||||
Cryptpad.alert(Messages.login_invalPass);
|
||||
Cryptpad.alert(Messages.login_invalPass, function () {
|
||||
hashing = false;
|
||||
});
|
||||
});
|
||||
break;
|
||||
default: // UNHANDLED ERROR
|
||||
|
||||
@@ -8,17 +8,26 @@
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0px;
|
||||
height: 100%;
|
||||
}
|
||||
.cryptpad-toolbar {
|
||||
margin-bottom: 1px;
|
||||
padding: 0px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
media-tag * {
|
||||
max-width: 100%;
|
||||
margin: auto;
|
||||
display: block;
|
||||
}
|
||||
media-tag *:not(button) {
|
||||
height: 100%;
|
||||
}
|
||||
media-tag video {
|
||||
min-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ define([
|
||||
'/common/cryptpad-common.js',
|
||||
//'/common/visible.js',
|
||||
//'/common/notify.js',
|
||||
'pdfjs-dist/build/pdf',
|
||||
'pdfjs-dist/build/pdf.worker',
|
||||
//'pdfjs-dist/build/pdf',
|
||||
//'pdfjs-dist/build/pdf.worker',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
'/bower_components/file-saver/FileSaver.min.js',
|
||||
], function ($, Crypto, realtimeInput, Toolbar, Cryptpad /*, Visible, Notify*/) {
|
||||
|
||||
+21
-2
@@ -487,8 +487,14 @@ define([
|
||||
var updateIcon = function () {
|
||||
$collapse.removeClass('fa-caret-down').removeClass('fa-caret-up');
|
||||
var isCollapsed = !$bar.find('.cke_toolbox_main').is(':visible');
|
||||
if (isCollapsed) { $collapse.addClass('fa-caret-down'); }
|
||||
else { $collapse.addClass('fa-caret-up'); }
|
||||
if (isCollapsed) {
|
||||
if (!initializing) { Cryptpad.feedback('HIDETOOLBAR_PAD'); }
|
||||
$collapse.addClass('fa-caret-down');
|
||||
}
|
||||
else {
|
||||
if (!initializing) { Cryptpad.feedback('SHOWTOOLBAR_PAD'); }
|
||||
$collapse.addClass('fa-caret-up');
|
||||
}
|
||||
};
|
||||
updateIcon();
|
||||
$collapse.click(function () {
|
||||
@@ -666,6 +672,19 @@ define([
|
||||
onLocal();
|
||||
return test;
|
||||
};
|
||||
|
||||
$bar.find('.cke_button').click(function () {
|
||||
var e = this;
|
||||
var classString = e.getAttribute('class');
|
||||
var classes = classString.split(' ').filter(function (c) {
|
||||
return /cke_button__/.test(c);
|
||||
});
|
||||
|
||||
var id = classes[0];
|
||||
if (typeof(id) === 'string') {
|
||||
Cryptpad.feedback(id.toUpperCase());
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<div class="upper">
|
||||
<button id="publish" data-localization-title="poll_publish_button" data-localization="poll_publish_button" style="display: none;">publish poll</button>
|
||||
<button id="admin" data-localization-title="poll_admin_button" data-localization="poll_admin_button" style="display: none;">admin</button>
|
||||
<button id="help" data-localization-title="poll_show_help_button" data-localization="poll_show_help_button">help</button>
|
||||
</div>
|
||||
|
||||
<div class="realtime">
|
||||
|
||||
+80
-19
@@ -46,7 +46,8 @@ define([
|
||||
editable: {
|
||||
row: [],
|
||||
col: []
|
||||
}
|
||||
},
|
||||
locked: false
|
||||
};
|
||||
|
||||
var sortColumns = function (order, firstcol) {
|
||||
@@ -97,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; }
|
||||
@@ -108,12 +109,14 @@ define([
|
||||
|
||||
var unlockElements = function () {
|
||||
APP.editable.row.forEach(function (id) {
|
||||
$('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[disabled="disabled"][data-rt-id^="' + id + '"]').removeAttr('disabled');
|
||||
$('input[type="checkbox"][data-rt-id^="' + id + '"]').addClass('enabled');
|
||||
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);
|
||||
});
|
||||
};
|
||||
@@ -274,10 +277,17 @@ 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);
|
||||
var value = parseInt(input.value);
|
||||
|
||||
if (isNaN(value)) {
|
||||
console.error("Got NaN?!");
|
||||
break;
|
||||
}
|
||||
|
||||
Render.setValue(object, id, value);
|
||||
change();
|
||||
} else {
|
||||
debug('checkbox locked');
|
||||
@@ -290,12 +300,14 @@ define([
|
||||
};
|
||||
|
||||
var hideInputs = function (target, isKeyup) {
|
||||
if (APP.locked) { return; }
|
||||
if (!isKeyup && $(target).is('[type="text"]')) {
|
||||
return;
|
||||
}
|
||||
$('.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 = [];
|
||||
@@ -349,9 +361,13 @@ define([
|
||||
};
|
||||
|
||||
var handleClick = function (e, isKeyup) {
|
||||
if (APP.locked) { return; }
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
if (!APP.ready) { return; }
|
||||
if (!isKeyup && e.which !== 1) { return; } // only allow left clicks
|
||||
|
||||
var target = e && e.target;
|
||||
|
||||
if (!target) { return void debug("NO TARGET"); }
|
||||
@@ -369,10 +385,19 @@ define([
|
||||
hideInputs(target, isKeyup);
|
||||
break;
|
||||
}
|
||||
if ($(target).is('input[type="number"]')) { console.error("number input focused?"); 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;
|
||||
}
|
||||
@@ -421,6 +446,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;
|
||||
|
||||
@@ -457,7 +491,7 @@ var ready = function (info, userid, readOnly) {
|
||||
APP.$createRow = $('#create-option').click(function () {
|
||||
Render.createRow(proxy, function (empty, id) {
|
||||
change(null, null, null, null, function() {
|
||||
$('.edit[data-rt-id="' + id + '"]').click();
|
||||
handleSpan($('.edit[data-rt-id="' + id + '"]')[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -465,7 +499,7 @@ var ready = function (info, userid, readOnly) {
|
||||
APP.$createCol = $('#create-user').click(function () {
|
||||
Render.createColumn(proxy, function (empty, id) {
|
||||
change(null, null, null, null, function() {
|
||||
$('.lock[data-rt-id="' + id + '"]').click();
|
||||
handleSpan($('.lock[data-rt-id="' + id + '"]')[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -486,12 +520,16 @@ 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();
|
||||
});
|
||||
|
||||
// Title
|
||||
if (APP.proxy.info.defaultTitle) {
|
||||
Title.updateDefaultTitle(APP.proxy.info.defaultTitle);
|
||||
@@ -527,7 +565,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) {
|
||||
@@ -570,14 +611,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, .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)
|
||||
.css({'cursor': 'default'});
|
||||
} else {
|
||||
// enable
|
||||
$('span.edit, span.remove').show();
|
||||
$('span.lock').css({'cursor': ''});
|
||||
$('.realtime button, .upper button, .realtime textarea').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();
|
||||
};
|
||||
@@ -632,7 +692,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);
|
||||
@@ -698,12 +758,13 @@ var create = function (info) {
|
||||
Cryptpad.setAttribute(HIDE_INTRODUCTION_TEXT, "1", function (e) {
|
||||
if (e) { console.error(e); }
|
||||
});
|
||||
} else if (value === "1") {
|
||||
$('#howItWorks').hide();
|
||||
showHelp(true);
|
||||
} else {
|
||||
showHelp(false);
|
||||
}
|
||||
});
|
||||
|
||||
//Cryptpad.onLogout(function () { setEditable(false); }); TODO
|
||||
Cryptpad.onLogout(function () { setEditable(false); });
|
||||
});
|
||||
Cryptpad.onError(function (info) {
|
||||
if (info) {
|
||||
|
||||
+50
-23
@@ -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;
|
||||
@@ -225,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;
|
||||
@@ -298,8 +325,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"],
|
||||
|
||||
+37
-7
@@ -4,10 +4,13 @@
|
||||
@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;
|
||||
@poll-fg: #000;
|
||||
@poll-option-yellow: #ff5;
|
||||
@poll-option-gray: #ccc;
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
@@ -195,6 +198,9 @@ form.realtime, div.realtime {
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: ~"calc(100% - 1px)";
|
||||
.editing {
|
||||
background-color: @poll-editing;
|
||||
}
|
||||
tr {
|
||||
td:first-child {
|
||||
position:absolute;
|
||||
@@ -247,7 +253,7 @@ form.realtime, div.realtime {
|
||||
}
|
||||
|
||||
input {
|
||||
&[type="checkbox"] {
|
||||
&[type="number"] {
|
||||
&:not(.editable) {
|
||||
display: none;
|
||||
|
||||
@@ -255,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;
|
||||
}
|
||||
@@ -282,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: "?"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,8 +353,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)";
|
||||
|
||||
+15
-9
@@ -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;
|
||||
|
||||
+19
-3
@@ -63,6 +63,7 @@ define([
|
||||
|
||||
var $register = $('button#register');
|
||||
|
||||
var registering = false;
|
||||
var logMeIn = function (result) {
|
||||
if (Test.testing) {
|
||||
Test.passed();
|
||||
@@ -79,6 +80,7 @@ define([
|
||||
|
||||
Cryptpad.whenRealtimeSyncs(result.realtime, function () {
|
||||
Cryptpad.login(result.userHash, result.userName, function () {
|
||||
registering = false;
|
||||
if (sessionStorage.redirectTo) {
|
||||
var h = sessionStorage.redirectTo;
|
||||
var parser = document.createElement('a');
|
||||
@@ -95,6 +97,11 @@ define([
|
||||
};
|
||||
|
||||
$register.click(function () {
|
||||
if (registering) {
|
||||
console.log("registration is already in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
var uname = $uname.val();
|
||||
var passwd = $passwd.val();
|
||||
var confirmPassword = $confirm.val();
|
||||
@@ -115,6 +122,7 @@ define([
|
||||
function (yes) {
|
||||
if (!yes) { return; }
|
||||
|
||||
registering = true;
|
||||
// setTimeout 100ms to remove the keyboard on mobile devices before the loading screen pops up
|
||||
window.setTimeout(function () {
|
||||
Cryptpad.addLoadingScreen(Messages.login_hashing);
|
||||
@@ -127,20 +135,27 @@ define([
|
||||
switch (err) {
|
||||
case 'NO_SUCH_USER':
|
||||
Cryptpad.removeLoadingScreen(function () {
|
||||
Cryptpad.alert(Messages.login_noSuchUser);
|
||||
Cryptpad.alert(Messages.login_noSuchUser, function () {
|
||||
registering = false;
|
||||
});
|
||||
});
|
||||
break;
|
||||
case 'INVAL_USER':
|
||||
Cryptpad.removeLoadingScreen(function () {
|
||||
Cryptpad.alert(Messages.login_invalUser);
|
||||
Cryptpad.alert(Messages.login_invalUser, function () {
|
||||
registering = false;
|
||||
});
|
||||
});
|
||||
break;
|
||||
case 'INVAL_PASS':
|
||||
Cryptpad.removeLoadingScreen(function () {
|
||||
Cryptpad.alert(Messages.login_invalPass);
|
||||
Cryptpad.alert(Messages.login_invalPass, function () {
|
||||
registering = false;
|
||||
});
|
||||
});
|
||||
break;
|
||||
case 'ALREADY_REGISTERED':
|
||||
// logMeIn should reset registering = false
|
||||
Cryptpad.removeLoadingScreen(function () {
|
||||
Cryptpad.confirm(Messages.register_alreadyRegistered, function (yes) {
|
||||
if (!yes) { return; }
|
||||
@@ -155,6 +170,7 @@ define([
|
||||
});
|
||||
break;
|
||||
default: // UNHANDLED ERROR
|
||||
registering = false;
|
||||
Cryptpad.errorLoadingScreen(Messages.login_unhandledError);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="version-footer">CryptPad v1.8.0 (Igopogo)</div>
|
||||
<div class="version-footer">CryptPad v1.9.0 (Jackelope)</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
|
||||
+11
-10
@@ -40,18 +40,19 @@
|
||||
<div id="bar"></div>
|
||||
<!-- <textarea></textarea>-->
|
||||
<div id="cme_toolbox" class="toolbar-container"></div>
|
||||
<textarea id="editor1" name="editor1"></textarea>
|
||||
|
||||
|
||||
<span class="cp slide">
|
||||
<div id="modal">
|
||||
<div id="button_exit" class="button"><span class="fa fa-times"></span></div>
|
||||
<div id="button_left" class="button"><span class="fa fa-chevron-left"></span></div>
|
||||
<div id="button_right" class="button"><span class="fa fa-chevron-right"></span></div>
|
||||
<div id="content"></div>
|
||||
<div id="editorContainer">
|
||||
<textarea id="editor1" name="editor1"></textarea>
|
||||
<div class="cp slide" tabindex="2">
|
||||
<div id="modal">
|
||||
<div id="button_exit" class="button"><span class="fa fa-times"></span></div>
|
||||
<div id="button_left" class="button"><span class="fa fa-chevron-left"></span></div>
|
||||
<div id="button_right" class="button"><span class="fa fa-chevron-right"></span></div>
|
||||
<div id="content"></div>
|
||||
</div>
|
||||
<div id="print"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="print"></div>
|
||||
</span>
|
||||
<div id="nope"></div>
|
||||
<div id="colorPicker_check"></div>
|
||||
</body>
|
||||
|
||||
+120
-9
@@ -9,6 +9,7 @@ define([
|
||||
'/common/cryptpad-common.js',
|
||||
'/common/cryptget.js',
|
||||
'/slide/slide.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js', // needed for media-tag
|
||||
], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Cryptget, Slide) {
|
||||
var Messages = Cryptpad.Messages;
|
||||
|
||||
@@ -47,6 +48,7 @@ define([
|
||||
};
|
||||
|
||||
var andThen = function (CMeditor) {
|
||||
var $iframe = $('#pad-iframe').contents();
|
||||
var CodeMirror = Cryptpad.createCodemirror(CMeditor, ifrw, Cryptpad);
|
||||
editor = CodeMirror.editor;
|
||||
|
||||
@@ -66,7 +68,7 @@ define([
|
||||
|
||||
var setTabTitle = function (title) {
|
||||
var slideNumber = '';
|
||||
if (Slide.index && Slide.content.length) {
|
||||
if (Slide.shown) { //Slide.index && Slide.content.length) {
|
||||
slideNumber = ' (' + Slide.index + '/' + Slide.content.length + ')';
|
||||
}
|
||||
document.title = title + slideNumber;
|
||||
@@ -194,6 +196,66 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
var createFileDialog = function () {
|
||||
var $body = $iframe.find('body');
|
||||
var $block = $body.find('#fileDialog');
|
||||
if (!$block.length) {
|
||||
$block = $('<div>', {id: "fileDialog"}).appendTo($body);
|
||||
}
|
||||
$block.html('');
|
||||
$('<span>', {
|
||||
'class': 'close fa fa-times',
|
||||
'title': Messages.filePicker_close
|
||||
}).click(function () {
|
||||
$block.hide();
|
||||
}).appendTo($block);
|
||||
var $description = $('<p>').text(Messages.filePicker_description);
|
||||
$block.append($description);
|
||||
var $filter = $('<p>').appendTo($block);
|
||||
var $container = $('<span>', {'class': 'fileContainer'}).appendTo($block);
|
||||
var updateContainer = function () {
|
||||
$container.html('');
|
||||
var filter = $filter.find('.filter').val().trim();
|
||||
var list = Cryptpad.getUserFilesList();
|
||||
var fo = Cryptpad.getFO();
|
||||
list.forEach(function (id) {
|
||||
var data = fo.getFileData(id);
|
||||
var name = fo.getTitle(id);
|
||||
if (filter && name.toLowerCase().indexOf(filter.toLowerCase()) === -1) {
|
||||
return;
|
||||
}
|
||||
var $span = $('<span>', {'class': 'element'}).appendTo($container);
|
||||
var $inner = $('<span>').text(name);
|
||||
$span.append($inner).click(function () {
|
||||
var cleanName = name.replace(/[\[\]]/g, '');
|
||||
var text = '';
|
||||
editor.replaceSelection(text);
|
||||
$block.hide();
|
||||
console.log(data.href);
|
||||
});
|
||||
});
|
||||
};
|
||||
var to;
|
||||
$('<input>', {
|
||||
type: 'text',
|
||||
'class': 'filter',
|
||||
'placeholder': Messages.filePicker_filter
|
||||
}).appendTo($filter).on('keypress', function () {
|
||||
if (to) { window.clearTimeout(to); }
|
||||
to = window.setTimeout(updateContainer, 300);
|
||||
});
|
||||
$filter.append(' '+Messages.or+' ');
|
||||
var data = {FM: APP.FM};
|
||||
$filter.append(Cryptpad.createButton('upload', false, data, function () {
|
||||
$block.hide();
|
||||
}));
|
||||
updateContainer();
|
||||
$body.keydown(function (e) {
|
||||
if (e.which === 27) { $block.hide(); }
|
||||
});
|
||||
$block.show();
|
||||
};
|
||||
|
||||
var createPrintDialog = function () {
|
||||
var slideOptionsTmp = {
|
||||
title: false,
|
||||
@@ -355,6 +417,33 @@ define([
|
||||
var $forgetPad = Cryptpad.createButton('forget', true, {}, forgetCb);
|
||||
$rightside.append($forgetPad);
|
||||
|
||||
$('<button>', {
|
||||
title: Messages.filePickerButton,
|
||||
'class': 'rightside-button fa fa-picture-o',
|
||||
style: 'font-size: 17px'
|
||||
}).click(function () {
|
||||
$('body').append(createFileDialog());
|
||||
}).appendTo($rightside);
|
||||
|
||||
var $previewButton = APP.$previewButton = Cryptpad.createButton(null, true);
|
||||
$previewButton.removeClass('fa-question').addClass('fa-eye');
|
||||
$previewButton.attr('title', Messages.previewButtonTitle);
|
||||
$previewButton.click(function () {
|
||||
var $c = $iframe.find('#editorContainer');
|
||||
if ($c.hasClass('preview')) {
|
||||
Cryptpad.setPadAttribute('previewMode', false, function (e) {
|
||||
if (e) { return console.log(e); }
|
||||
});
|
||||
return void $c.removeClass('preview');
|
||||
}
|
||||
Cryptpad.setPadAttribute('previewMode', true, function (e) {
|
||||
if (e) { return console.log(e); }
|
||||
});
|
||||
$c.addClass('preview');
|
||||
Slide.updateFontSize();
|
||||
});
|
||||
$rightside.append($previewButton);
|
||||
|
||||
var $printButton = $('<button>', {
|
||||
title: Messages.printButtonTitle,
|
||||
'class': 'rightside-button fa fa-print',
|
||||
@@ -483,21 +572,27 @@ define([
|
||||
|
||||
// Update the user list (metadata) from the hyperjson
|
||||
Metadata.update(userDoc);
|
||||
|
||||
editor.setValue(newDoc || initialState);
|
||||
|
||||
if (Cryptpad.initialName && Title.isDefaultTitle()) {
|
||||
Title.updateTitle(Cryptpad.initialName);
|
||||
onLocal();
|
||||
}
|
||||
|
||||
Slide.onChange(function (o, n, l) {
|
||||
if (n !== null) {
|
||||
document.title = Title.title + ' (' + (++n) + '/' + l + ')';
|
||||
return;
|
||||
Cryptpad.getPadAttribute('previewMode', function (e, data) {
|
||||
if (e) { return void console.error(e); }
|
||||
if (data === true && APP.$previewButton) {
|
||||
APP.$previewButton.click();
|
||||
}
|
||||
console.log("Exiting presentation mode");
|
||||
document.title = Title.title;
|
||||
});
|
||||
|
||||
Slide.onChange(function (o, n, l) {
|
||||
var slideNumber = '';
|
||||
if (n !== null) {
|
||||
if (Slide.shown) { //Slide.index && Slide.content.length) {
|
||||
slideNumber = ' (' + (++n) + '/' + l + ')';
|
||||
}
|
||||
}
|
||||
document.title = Title.title + slideNumber;
|
||||
});
|
||||
|
||||
Cryptpad.removeLoadingScreen();
|
||||
@@ -505,9 +600,25 @@ define([
|
||||
initializing = false;
|
||||
|
||||
onLocal(); // push local state to avoid parse errors later.
|
||||
Slide.update(editor.getValue());
|
||||
|
||||
if (readOnly) { return; }
|
||||
UserList.getLastName(toolbar.$userNameButton, isNew);
|
||||
|
||||
var fmConfig = {
|
||||
dropArea: $iframe.find('.CodeMirror'),
|
||||
body: $iframe.find('body'),
|
||||
onUploaded: function (ev, data) {
|
||||
//var cursor = editor.getCursor();
|
||||
var cleanName = data.name.replace(/[\[\]]/g, '');
|
||||
var text = '';
|
||||
/*if (data.mediatag) {
|
||||
text = '!'+text;
|
||||
}*/
|
||||
editor.replaceSelection(text);
|
||||
}
|
||||
};
|
||||
APP.FM = Cryptpad.createFileManager(fmConfig);
|
||||
};
|
||||
|
||||
config.onRemote = function () {
|
||||
|
||||
+88
-7
@@ -71,6 +71,44 @@ body .CodeMirror-focused .cm-matchhighlight {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
#cme_toolbox {
|
||||
z-index: 10000;
|
||||
}
|
||||
#editorContainer {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
#editorContainer .CodeMirror {
|
||||
resize: none;
|
||||
width: 100vw;
|
||||
}
|
||||
#editorContainer.preview .CodeMirror {
|
||||
width: 50vw;
|
||||
}
|
||||
.preview .cp {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.preview .cp div#modal:not(.shown) {
|
||||
position: relative;
|
||||
top: auto;
|
||||
left: auto;
|
||||
width: auto;
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
.preview .cp div#modal:not(.shown) #content .slide-container {
|
||||
width: 100%;
|
||||
}
|
||||
.preview .cp div#modal:not(.shown) #content .slide-frame {
|
||||
width: 50vw;
|
||||
height: 28.125vw;
|
||||
max-height: 100vh;
|
||||
max-width: 177.78vh;
|
||||
}
|
||||
.cp {
|
||||
/* Slide position (print mode) */
|
||||
/* Slide position (present mode) */
|
||||
@@ -94,6 +132,7 @@ body .CodeMirror-focused .cm-matchhighlight {
|
||||
page-break-after: always;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -114,6 +153,8 @@ body .CodeMirror-focused .cm-matchhighlight {
|
||||
}
|
||||
.cp div.modal,
|
||||
.cp div#modal {
|
||||
background-color: black;
|
||||
color: white;
|
||||
/* Navigation buttons */
|
||||
box-sizing: border-box;
|
||||
z-index: 9001;
|
||||
@@ -160,9 +201,7 @@ body .CodeMirror-focused .cm-matchhighlight {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 100;
|
||||
background-color: black;
|
||||
color: white;
|
||||
z-index: 100000;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -174,12 +213,9 @@ body .CodeMirror-focused .cm-matchhighlight {
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.cp div.modal #content.transition,
|
||||
.cp div#modal #content.transition {
|
||||
transition: margin-left 1s;
|
||||
}
|
||||
.cp div.modal #content .slide-frame,
|
||||
.cp div#modal #content .slide-frame {
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid;
|
||||
@@ -207,6 +243,10 @@ body .CodeMirror-focused .cm-matchhighlight {
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
.cp div.modal #content.transition .slide-container,
|
||||
.cp div#modal #content.transition .slide-container {
|
||||
transition: margin-left 1s;
|
||||
}
|
||||
.cp div.modal .center,
|
||||
.cp div#modal .center {
|
||||
position: relative;
|
||||
@@ -300,6 +340,8 @@ body .CodeMirror-focused .cm-matchhighlight {
|
||||
width: 90%;
|
||||
margin: auto;
|
||||
padding-left: .25vw;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
.cp div#modal #content .slide-frame ul,
|
||||
.cp #print .slide-frame ul,
|
||||
@@ -345,3 +387,42 @@ body .CodeMirror-focused .cm-matchhighlight {
|
||||
font-size: 10%;
|
||||
line-height: 110%;
|
||||
}
|
||||
#fileDialog {
|
||||
position: absolute;
|
||||
background-color: rgba(200, 200, 200, 0.8);
|
||||
top: 15vh;
|
||||
bottom: 15vh;
|
||||
left: 10vw;
|
||||
right: 10vw;
|
||||
border: 1px solid black;
|
||||
z-index: 10;
|
||||
overflow: auto;
|
||||
display: none;
|
||||
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
#fileDialog .close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#fileDialog .element {
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 1px solid #ccc;
|
||||
margin: 5px;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
background-color: white;
|
||||
padding: 5px;
|
||||
align-items: center;
|
||||
}
|
||||
#fileDialog .element span {
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
+23
-8
@@ -38,15 +38,23 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
var updateFontSize = Slide.updateFontSize = function() {
|
||||
var updateFontSize = Slide.updateFontSize = function () {
|
||||
// 20vh
|
||||
// 20 * 16 / 9vw
|
||||
if ($(window).width() > 16/9*$(window).height()) {
|
||||
$content.css('font-size', '20vh');
|
||||
var wbase = 20;
|
||||
var vh = 20;
|
||||
var $elem = $(window);
|
||||
if (!Slide.shown) {
|
||||
wbase = 10;
|
||||
vh *= $content.height()/$(window).height();
|
||||
$elem = $content;
|
||||
}
|
||||
if ($elem.width() > 16/9*$elem.height()) {
|
||||
$content.css('font-size', vh+'vh');
|
||||
// $print.css('font-size', '20vh');
|
||||
return;
|
||||
}
|
||||
$content.css('font-size', (20*9/16)+'vw');
|
||||
$content.css('font-size', (wbase*9/16)+'vw');
|
||||
// $print.css('font-size', (20*9/16)+'vw');
|
||||
};
|
||||
|
||||
@@ -86,7 +94,8 @@ define([
|
||||
}
|
||||
//$content.find('.' + slideClass).hide();
|
||||
//$content.find('.' + slideClass + ':eq( ' + i + ' )').show();
|
||||
$content.css('margin-left', -(i*100)+'vw');
|
||||
//$content.css('margin-left', -(i*100)+'vw');
|
||||
$content.find('.slide-container').first().css('margin-left', -(i*100)+'%');
|
||||
updateFontSize();
|
||||
change(Slide.lastIndex, Slide.index);
|
||||
};
|
||||
@@ -121,6 +130,7 @@ define([
|
||||
$pad.addClass('fullscreen');
|
||||
$('#iframe-container').addClass('fullscreen');
|
||||
$('.top-bar').hide();
|
||||
updateFontSize();
|
||||
return;
|
||||
}
|
||||
window.location.hash = window.location.hash.replace(/\/present$/, '/');
|
||||
@@ -131,10 +141,12 @@ define([
|
||||
$('#iframe-container').removeClass('fullscreen');
|
||||
$('.top-bar').show();
|
||||
$modal.removeClass('shown');
|
||||
updateFontSize();
|
||||
};
|
||||
|
||||
Slide.update = function (content, init) {
|
||||
if (!Slide.shown && !init) { return; }
|
||||
Slide.update = function (content) {
|
||||
updateFontSize();
|
||||
//if (!init) { return; }
|
||||
if (!content) { content = ''; }
|
||||
var old = Slide.content;
|
||||
Slide.content = content.replace(/\n\s*\-\-\-\s*\n/g, '\n\n'+separator+'\n\n');
|
||||
@@ -198,13 +210,16 @@ define([
|
||||
$modal.trigger(ev);
|
||||
});
|
||||
$modal.find('#button_right').click(function () {
|
||||
console.log('right');
|
||||
var ev = $.Event("keyup");
|
||||
ev.which = 39;
|
||||
$modal.trigger(ev);
|
||||
});
|
||||
|
||||
$pad.contents().find('.CodeMirror').keyup(function (e) { e.stopPropagation(); });
|
||||
$(ifrw).on('keyup', function (e) {
|
||||
if (!Slide.shown) { return; }
|
||||
//if (!Slide.shown) { return; }
|
||||
if (e.ctrlKey) { return; }
|
||||
switch(e.which) {
|
||||
case 33: // pageup
|
||||
case 38: // up
|
||||
|
||||
+94
-6
@@ -77,6 +77,49 @@ body {
|
||||
}
|
||||
|
||||
|
||||
#cme_toolbox {
|
||||
z-index: 10000;
|
||||
}
|
||||
#editorContainer {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
.CodeMirror {
|
||||
resize: none;
|
||||
width: 100vw;
|
||||
}
|
||||
&.preview {
|
||||
.CodeMirror {
|
||||
//resize: horizontal;
|
||||
width: 50vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
.preview .cp {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
div#modal:not(.shown) {
|
||||
position: relative;
|
||||
top: auto;
|
||||
left: auto;
|
||||
width: auto;
|
||||
display: block;
|
||||
height: 100%;
|
||||
#content {
|
||||
.slide-container {
|
||||
width: 100%;
|
||||
}
|
||||
.slide-frame {
|
||||
width: 50vw;
|
||||
height: 28.125vw; // height:width ratio = 9/16 = .5625
|
||||
max-height: 100vh;
|
||||
max-width: 177.78vh; // 16/9 = 1.778
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp {
|
||||
|
||||
/* Slide position (print mode) */
|
||||
@@ -95,6 +138,7 @@ body {
|
||||
page-break-after: always;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
li {
|
||||
min-width: @ratio*50vw;
|
||||
}
|
||||
@@ -123,6 +167,8 @@ body {
|
||||
/* Slide position (present mode) */
|
||||
div.modal, div#modal {
|
||||
display: none;
|
||||
background-color: black;
|
||||
color: white;
|
||||
|
||||
/* Navigation buttons */
|
||||
.button {
|
||||
@@ -154,22 +200,18 @@ div.modal, div#modal {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 100;
|
||||
background-color: black;
|
||||
color: white;
|
||||
z-index: 100000;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
#content {
|
||||
&.transition {
|
||||
transition: margin-left 1s;
|
||||
}
|
||||
font-size: 20vh;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
.slide-frame {
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid;
|
||||
@@ -198,6 +240,11 @@ div.modal, div#modal {
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
&.transition {
|
||||
.slide-container {
|
||||
transition: margin-left 1s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
box-sizing: border-box;
|
||||
@@ -266,6 +313,8 @@ div#modal #content, #print {
|
||||
width: 90%;
|
||||
margin: auto;
|
||||
padding-left: .25vw;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
@@ -308,3 +357,42 @@ div#modal #content, #print {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#fileDialog {
|
||||
position: absolute;
|
||||
background-color: rgba(200, 200, 200, 0.8);
|
||||
top: 15vh; bottom: 15vh;
|
||||
left: 10vw; right: 10vw;
|
||||
border: 1px solid black;
|
||||
z-index: 10;
|
||||
overflow: auto;
|
||||
display: none;
|
||||
font-family: -apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.element {
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 1px solid #ccc;
|
||||
margin: 5px;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
background-color: white;
|
||||
padding: 5px;
|
||||
align-items: center;
|
||||
span {
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="version-footer">CryptPad v1.8.0 (Igopogo)</div>
|
||||
<div class="version-footer">CryptPad v1.9.0 (Jackelope)</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<button id="toggleDraw" data-localization="canvas_disable"></button>
|
||||
<button id="delete" style="display: none;" data-localization="canvas_delete"></button>
|
||||
<input id="width" data-localization-title="canvas_width" type="range" value="5" min="1" max="100"></input><label for="width">5</label>
|
||||
<input id="opacity" data-localization-title="canvas_opacity" type="range" value="1" min="0" max="1" step="0.1"></input><label for="opacity">1</label>
|
||||
<input id="opacity" data-localization-title="canvas_opacity" type="range" value="1" min="0.1" max="1" step="0.1"></input><label for="opacity">1</label>
|
||||
<span class="selected"></span>
|
||||
</div>
|
||||
<div id="colors"> </div>
|
||||
|
||||
@@ -97,7 +97,7 @@ window.canvas = canvas;
|
||||
var updateBrushWidth = function () {
|
||||
var val = $width.val();
|
||||
canvas.freeDrawingBrush.width = Number(val);
|
||||
$widthLabel.text(val);
|
||||
$widthLabel.text(Cryptpad.Messages._getKey("canvas_widthLabel", [val]));
|
||||
createCursor();
|
||||
};
|
||||
updateBrushWidth();
|
||||
@@ -108,7 +108,7 @@ 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(Cryptpad.Messages._getKey("canvas_opacityLabel", [val]));
|
||||
createCursor();
|
||||
};
|
||||
updateBrushOpacity();
|
||||
@@ -338,12 +338,12 @@ window.canvas = canvas;
|
||||
});
|
||||
$rightside.append($forget);
|
||||
|
||||
makeColorButton($rightside);
|
||||
|
||||
var editHash;
|
||||
|
||||
if (!readOnly) {
|
||||
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
||||
makeColorButton($rightside);
|
||||
}
|
||||
if (!readOnly) { Cryptpad.replaceHash(editHash); }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user