Merge branch 'staging' into newCk

This commit is contained in:
yflory
2017-06-20 10:57:40 +02:00
59 changed files with 1651 additions and 671 deletions
+3
View File
@@ -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;
+3
View File
@@ -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 {
+1
View File
@@ -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
View File
@@ -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')]);
});
+294
View File
@@ -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;
});
+12
View File
@@ -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);
+4 -2
View File
@@ -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);
+5 -1
View File
@@ -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
View File
@@ -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;
}
+34 -3
View File
@@ -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
+4 -2
View File
@@ -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
View File
@@ -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) {
+4
View File
@@ -96,6 +96,10 @@ define([
} else {
styleToolbar($container);
}
$container.on('drop dragover', function (e) {
e.preventDefault();
e.stopPropagation();
});
return $toolbar;
};
+5 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+8 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+9
View File
@@ -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
View File
@@ -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
View File
@@ -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());
}
});
});
};
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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 = '!['+cleanName+']('+data.href+')';
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 = '!['+cleanName+']('+data.url+')';
/*if (data.mediatag) {
text = '!'+text;
}*/
editor.replaceSelection(text);
}
};
APP.FM = Cryptpad.createFileManager(fmConfig);
};
config.onRemote = function () {
+88 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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>
+1 -1
View File
@@ -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">&nbsp;</div>
+3 -3
View File
@@ -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); }
};