Merge branch 'staging' of github.com:xwiki-labs/cryptpad into staging

This commit is contained in:
yflory
2017-12-11 12:21:17 +01:00
57 changed files with 734 additions and 2711 deletions

View File

@@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<script src="respond.js"></script>
</head>
<body>

147
www/assert/frame/frame.js Normal file
View File

@@ -0,0 +1,147 @@
(function () {
var Frame = {};
var uid = function () {
return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
.toString(32).replace(/\./g, '');
};
// create an invisible iframe with a given source
// append it to a parent element
// execute a callback when it has loaded
Frame.create = function (parent, src, onload, timeout) {
var iframe = document.createElement('iframe');
timeout = timeout || 10000;
var to = window.setTimeout(function () {
onload('[timeoutError] could not load iframe at ' + src);
}, timeout);
iframe.setAttribute('id', 'cors-store');
iframe.onload = function (e) {
onload(void 0, iframe, e);
window.clearTimeout(to);
};
// We must pass a unique parameter here to avoid cache problems in Firefox with
// the NoScript plugin: if the iframe's content is taken from the cache, the JS
// is not executed with NoScript....
iframe.setAttribute('src', src + '?t=' + new Date().getTime());
iframe.style.display = 'none';
parent.appendChild(iframe);
};
/* given an iframe with an rpc script loaded, create a frame object
with an asynchronous 'send' method */
Frame.open = function (e, A, timeout) {
var win = e.contentWindow;
var frame = {};
frame.id = uid();
var listeners = {};
var timeouts = {};
timeout = timeout || 5000;
frame.accepts = function (o) {
return A.some(function (e) {
switch (typeof(e)) {
case 'string': return e === o;
case 'object': return e.test(o);
}
});
};
var changeHandlers = frame.changeHandlers = [];
frame.change = function (f) {
if (typeof(f) !== 'function') {
throw new Error('[Frame.change] expected callback');
}
changeHandlers.push(f);
};
var _listener = function (e) {
if (!frame.accepts(e.origin)) {
console.log("message from %s rejected!", e.origin);
return;
}
var message = JSON.parse(e.data);
var uid = message._uid;
var error = message.error;
var data = message.data;
if (!uid) {
console.log("No uid!");
return;
}
if (uid === 'change' && changeHandlers.length) {
changeHandlers.forEach(function (f) {
f(data);
});
return;
}
if (timeouts[uid]) {
window.clearTimeout(timeouts[uid]);
}
if (listeners[uid]) {
listeners[uid](error, data, e);
delete listeners[uid];
}
};
window.addEventListener('message', _listener);
frame.close = function () {
window.removeEventListener('message', _listener);
};
/* method (string): (set|get|remove)
key (string)
data (string)
cb (function) */
frame.send = function (method, content, cb) {
var req = {
method: method,
//key: key,
data: content, //data,
};
var id = req._uid = uid();
// uid must not equal 'change'
while(id === 'change') {
id = req._uid = uid();
}
if (typeof(cb) === 'function') {
//console.log("setting callback!");
listeners[id] = cb;
//console.log("setting timeout of %sms", timeout);
timeouts[id] = window.setTimeout(function () {
// when the callback is executed it will clear this timeout
cb('[TimeoutError] request timed out after ' + timeout + 'ms');
}, timeout);
} else {
console.log(typeof(cb));
}
win.postMessage(JSON.stringify(req), '*');
};
return frame;
};
if (typeof(module) !== 'undefined' && module.exports) {
module.exports = Frame;
} else if (typeof(define) === 'function' && define.amd) {
define(['jquery'], function () {
return Frame;
});
} else {
window.Frame = Frame;
}
}());

View File

@@ -0,0 +1,32 @@
var validDomains = [ /.*/i, ];
var isValidDomain = function (o) {
return validDomains.some(function (e) {
switch (typeof(e)) {
case 'string': return e === o;
case 'object': return e.test(o);
}
});
};
window.addEventListener('message', function(e) {
if (!isValidDomain(e.origin)) { return; }
var payload = JSON.parse(e.data);
var parent = window.parent;
var respond = function (error, data) {
var res = {
_uid: payload._uid,
error: error,
data: data,
};
parent.postMessage(JSON.stringify(res), '*');
};
//console.error(payload);
switch(payload.method) {
case undefined:
return respond('No method supplied');
default:
return respond(void 0, "EHLO");
}
});

View File

@@ -249,7 +249,6 @@ define([
}
};
var evt = Util.mkEvent();
var respond = function (e, out) {
evt.fire(e, out);
@@ -259,9 +258,8 @@ define([
try {
var parsed = JSON.parse(raw);
var txid = parsed.txid;
var message = parsed.message;
setTimeout(function () {
service(message.command, message.content, function (e, result) {
service(parsed.q, parsed.content, function (e, result) {
respond(JSON.stringify({
txid: txid,
error: e,
@@ -285,33 +283,56 @@ define([
});
}, "Test rpc factory");
/*
assert(function (cb) {
var getBlob = function (url, cb) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "blob";
xhr.onload = function () {
cb(void 0, this.response);
};
xhr.send();
};
require([
'/assert/frame/frame.js',
], function (Frame) {
Frame.create(document.body, '/assert/frame/frame.html', function (e, frame) {
if (e) { return cb(false); }
var $img = $('img#thumb-orig');
getBlob($img.attr('src'), function (e, blob) {
console.log(e, blob);
Thumb.fromImageBlob(blob, function (e, thumb) {
console.log(thumb);
var th = new Image();
th.src = URL.createObjectURL(thumb);
th.onload = function () {
$(document.body).append($(th).addClass('thumb'));
cb(th.width === Thumb.dimension && th.height === Thumb.dimension);
};
var channel = Frame.open(frame, [
/.*/i,
], 5000);
channel.send('HELO', null, function (e, res) {
if (res === 'EHLO') { return cb(true); }
cb(false);
});
});
});
});
*/
}, "PEWPEW");
(function () {
var guid = Wire.uid();
var t = Wire.tracker({
timeout: 1000,
hook: function (txid, q, content) {
console.info(JSON.stringify({
guid: guid,
txid: txid,
q: q,
content: content,
}));
},
});
assert(function (cb) {
t.call('SHOULD_TIMEOUT', null, function (e) {
if (e === 'TIMEOUT') { return cb(true); }
cb(false);
});
}, 'tracker should timeout');
assert(function (cb) {
var id = t.call('SHOULD_NOT_TIMEOUT', null, function (e, out) {
if (e) { return cb(false); }
if (out === 'YES') { return cb(true); }
cb(false);
});
t.respond(id, void 0, 'YES');
}, "tracker should not timeout");
}());
Drive.test(assert);

View File

@@ -5,9 +5,11 @@
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/tokenfield.less';
@_cp-toolbar-color-warn: @colortheme_code-warn;
.toolbar_main();
.toolbar_main(
@bg-color: @colortheme_code-bg,
@warn-color: @colortheme_code-warn,
@color: @colortheme_code-color
);
.fileupload_main();
.alertify_main();
.tokenfield_main();

View File

@@ -719,7 +719,7 @@ define([
return $titleContainer;
};
var createUnpinnedWarning = function (toolbar, config) {
var createUnpinnedWarning0 = function (toolbar, config) {
if (Common.isLoggedIn()) { return; }
var pd = config.metadataMgr.getPrivateData();
var o = pd.origin;
@@ -728,17 +728,48 @@ define([
var cid = Hash.hrefToHexChannelId(url);
Common.sendAnonRpcMsg('IS_CHANNEL_PINNED', cid, function (x) {
if (x.error || !Array.isArray(x.response)) { return void console.log(x); }
if (x.response[0] === true) { return; }
var msg = $('<span>', {
'class': 'cp-pad-not-pinned',
}).append(
Messages._getKey('padNotPinned', [o + '/login', o + '/register'])
);
$('.cp-toolbar-title').append(msg);
console.log("This pad is not pinned");
if (x.response[0] === true) {
$('.cp-pad-not-pinned').remove();
return;
}
if ($('.cp-pad-not-pinned').length) { return; }
var pnpTitle = Messages._getKey('padNotPinned', ['','','','']);
var pnpMsg = Messages._getKey('padNotPinned', [
'<a href="' + o + '/login" class="cp-pnp-login" target="blank" title>',
'</a>',
'<a href="' + o + '/register" class="cp-pnp-register" target="blank" title>',
'</a>'
]);
var $msg = $('<span>', {
'class': 'cp-pad-not-pinned'
}).append([
$('<span>', {'class': 'fa fa-exclamation-triangle', 'title': pnpTitle}),
$('<span>', {'class': 'cp-pnp-msg'}).append(pnpMsg)
]);
$msg.find('a.cp-pnp-login').click(function (ev) {
ev.preventDefault();
Common.setLoginRedirect(function () {
window.parent.location = o + '/login/';
});
});
$msg.find('a.cp-pnp-register').click(function (ev) {
ev.preventDefault();
Common.setLoginRedirect(function () {
window.parent.location = o + '/register/';
});
});
$('.cp-toolbar-top').append($msg);
UI.addTooltips();
});
};
var createUnpinnedWarning = function (toolbar, config) {
config.metadataMgr.onChange(function () {
createUnpinnedWarning0(toolbar, config);
});
createUnpinnedWarning0(toolbar, config);
};
var createPageTitle = function (toolbar, config) {
if (config.title || !config.pageTitle) { return; }
var $titleContainer = $('<span>', {

View File

@@ -7,25 +7,118 @@ define([
Requirements
* some transmission methods can be interrupted
* handle disconnects and reconnects
* handle callbacks
* configurable timeout
* Service should expose 'addClient' method
* and handle broadcast
* [x] some transmission methods can be interrupted
* [x] handle disconnects and reconnects
* [x] handle callbacks
* [x] configurable timeout
* [x] be able to answer only queries with a particular id
* be able to implement arbitrary listeners on the service-side
* and not call 'ready' until those listeners are ready
* identical API for:
* iframe postMessage
* server calls over netflux
* postMessage to webworker
* postMessage to sharedWorker
* on-wire protocol should actually be the same for rewriting purposes
* q
* guid (globally unique id)
* txid (message id)
* content
* be able to compose different RPCs as streams
* intercept and rewrite capacity
* multiplex multiple streams over one stream
* blind redirect
* intelligent router
* broadcast (with ACK?)
* message
*
*/
var uid = function () {
var uid = Wire.uid = function () {
return Number(Math.floor(Math.random () *
Number.MAX_SAFE_INTEGER)).toString(32);
};
/* tracker(options)
maintains a registry of asynchronous function calls
allows you to:
hook each call to actually send to a remote service...
abort any call
trigger the pending callback with arguments
set the state of the tracker (active/inactive)
*/
Wire.tracker = function (opt) {
opt = opt || {};
var hook = opt.hook || function () {};
var timeout = opt.timeout || 5000;
var pending = {};
var timeouts = {};
var call = function (method, data, cb) {
var id = uid();
// if the callback is not invoked in time, time out
timeouts[id] = setTimeout(function () {
if (typeof(pending[id]) === 'function') {
cb("TIMEOUT");
delete pending[id];
return;
}
throw new Error('timed out without function to call');
}, timeout);
pending[id] = function () {
// invoke the function with arguments...
cb.apply(null, Array.prototype.slice.call(arguments));
// clear its timeout
clearTimeout(timeouts[id]);
// remove the function from pending
delete pending[id];
};
hook(id, method, data);
return id;
};
var respond = function (id, err, response) {
if (typeof(pending[id]) !== 'function') {
throw new Error('invoked non-existent callback');
}
pending[id](err, response);
};
var abort = function (id) {
if (pending[id]) {
clearTimeout(timeouts[id]);
delete pending[id];
return true;
}
return false;
};
var t = {
call: call,
respond: respond,
abort: abort,
state: true,
};
t.setState = function (active) {
t.state = Boolean(active);
};
return t;
};
/*
opt = {
timeout: 30000,
send: function () {
},
@@ -45,50 +138,44 @@ opt = {
};
*/
var parseMessage = function (raw) {
try { return JSON.parse(raw); } catch (e) { return; }
};
Wire.create = function (opt, cb) {
var ctx = {};
var pending = ctx.pending = {};
ctx.connected = false;
var rpc = {};
opt.constructor(function (e, service) {
if (e) { return setTimeout(function () { cb(e); }); }
var rpc = {};
var guid = Wire.uid();
var t = Wire.tracker({
timeout: opt.timeout,
hook: function (txid, q, content) {
service.send(JSON.stringify({
guid: guid,
q: q,
txid: txid,
content: content,
}));
},
});
rpc.send = function (type, data, cb) {
var txid = uid();
if (typeof(cb) !== 'function') {
throw new Error('expected callback');
}
ctx.pending[txid] = function (err, response) {
cb(err, response);
};
service.send(JSON.stringify({
txid: txid,
message: {
command: type,
content: data,
},
}));
t.call(type, data, cb);
};
service.receive(function (raw) {
try {
var data = JSON.parse(raw);
var txid = data.txid;
if (!txid) { throw new Error('NO_TXID'); }
var cb = pending[txid];
if (data.error) { return void cb(data.error); }
cb(void 0, data.content);
} catch (e) { console.error("UNHANDLED_MESSAGE", raw); }
var data = parseMessage(raw);
if (typeof(data) === 'undefined') {
return console.error("UNHANDLED_MESSAGE", raw);
}
if (!data.txid) { throw new Error('NO_TXID'); }
t.respond(data.txid, data.error, data.content);
});
cb(void 0, rpc);
});
};
return Wire;
});

View File

@@ -3,14 +3,13 @@
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
//@import (once) '../../customize/src/less/mixins.less';
//@import (once) '../../customize/src/less/variables.less";
@import (once) '../../customize/src/less2/include/avatar.less';
@_cp-toolbar-color-warn: @colortheme_friends-warn;;
.toolbar_main();
.toolbar_main(
@bg-color: @colortheme_friends-bg,
@warn-color: @colortheme_friends-warn,
@color: @colortheme_friends-color
);
.fileupload_main();
.alertify_main();

View File

@@ -8,9 +8,11 @@
@import (once) "../../customize/src/less2/include/limit-bar.less";
@import (once) "../../customize/src/less2/include/tokenfield.less";
@_cp-toolbar-color-warn: @colortheme_drive-warn;
.toolbar_main();
.toolbar_main(
@bg-color: @colortheme_drive-bg,
@warn-color: @colortheme_drive-warn,
@color: @colortheme_drive-color
);
.fileupload_main();
.alertify_main();
.limit-bar_main();

View File

@@ -5,9 +5,11 @@
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/tokenfield.less';
@_cp-toolbar-color-warn: @colortheme_file-warn;
.toolbar_main();
.toolbar_main(
@bg-color: @colortheme_file-bg,
@warn-color: @colortheme_file-warn,
@color: @colortheme_file-color
);
.fileupload_main();
.alertify_main();
.tokenfield_main();

View File

@@ -1,14 +1,12 @@
// Defaults to avoid breaking existing themes
@colortheme_pad-toolbar-bg: #c1e7ff;
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/tokenfield.less';
@_cp-toolbar-color-warn: @colortheme_pad-warn;
.toolbar_main();
.toolbar_main(
@bg-color: @colortheme_pad-bg,
@warn-color: @colortheme_pad-warn,
@color: @colortheme_pad-color
);
.alertify_main();
// body

View File

@@ -1,9 +1,3 @@
// Defaults to avoid breaking existing themes
@colortheme_poll-th-bg: #005bef;
@colortheme_poll-th-fg: #fff;
@colortheme_poll-help-bg: #bbffbb;
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@@ -13,9 +7,11 @@
@import (once) '../../customize/src/less2/include/tools.less';
@import (once) '../../customize/src/less2/include/avatar.less';
@_cp-toolbar-color-warn: @colortheme_poll-warn;
.toolbar_main();
.toolbar_main(
@bg-color: @colortheme_poll-bg,
@warn-color: @colortheme_poll-warn,
@color: @colortheme_poll-color
);
.fileupload_main();
.alertify_main();
.tokenfield_main();

View File

@@ -1079,7 +1079,16 @@ define([
Title = common.createTitle(titleCfg);
var configTb = {
displayed: ['title', 'useradmin', 'spinner', 'share', 'userlist', 'newpad', 'limit'],
displayed: [
'userlist',
'title',
'useradmin',
'spinner',
'newpad',
'share',
'limit',
'unpinnedWarning'
],
title: Title.getTitleConfig(),
metadataMgr: metadataMgr,
readOnly: APP.readOnly,

View File

@@ -7,8 +7,11 @@
@import (once) '../../customize/src/less2/include/avatar.less';
@import (once) '../../customize/src/less2/include/sidebar-layout.less';
.toolbar_main();
.toolbar_main(
@bg-color: @colortheme_profile-bg,
@warn-color: @colortheme_profile-warn,
@color: @colortheme_profile-color
);
.fileupload_main();
.alertify_main();
.sidebar-layout_main();

View File

@@ -6,9 +6,11 @@
@import (once) '../../customize/src/less2/include/sidebar-layout.less';
@import (once) "../../customize/src/less2/include/limit-bar.less";
@_cp-toolbar-color-warn: @colortheme_settings-warn;
.toolbar_main();
.toolbar_main(
@bg-color: @colortheme_settings-bg,
@warn-color: @colortheme_settings-warn,
@color: @colortheme_settings-color
);
.alertify_main();
.sidebar-layout_main();
.limit-bar_main();

View File

@@ -6,10 +6,12 @@
@import (once) "../../customize/src/less2/include/mediatag.less";
@import (once) '../../customize/src/less2/include/tokenfield.less';
@_cp-toolbar-color-warn: @colortheme_slide-warn;
.mediatag_base();
.toolbar_main();
.toolbar_main(
@bg-color: @colortheme_slide-bg,
@warn-color: @colortheme_slide-warn,
@color: @colortheme_slide-color
);
.fileupload_main();
.alertify_main();
.tokenfield_main();

View File

@@ -3,13 +3,13 @@
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
//@import (once) '../../customize/src/less/mixins.less';
//@import (once) '../../customize/src/less/variables.less";
@import (once) '../../customize/src/less2/include/avatar.less';
.toolbar_main();
.toolbar_main(
@bg-color: @colortheme_todo-bg,
@warn-color: @colortheme_todo-warn,
@color: @colortheme_todo-color
);
.fileupload_main();
.alertify_main();

View File

@@ -6,9 +6,11 @@
@import (once) '../../customize/src/less2/include/tools.less';
@import (once) '../../customize/src/less2/include/tokenfield.less';
@_cp-toolbar-color-warn: @colortheme_whiteboard-warn;
.toolbar_main();
.toolbar_main(
@bg-color: @colortheme_whiteboard-bg,
@warn-color: @colortheme_whiteboard-warn,
@color: @colortheme_whiteboard-color
);
.fileupload_main();
.alertify_main();
.tokenfield_main();

View File

@@ -3,9 +3,6 @@
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
//@import (once) '../../customize/src/less/mixins.less';
//@import (once) '../../customize/src/less/variables.less";
@import (once) '../../customize/src/less2/include/avatar.less';