Merge branch 'staging' of github.com:xwiki-labs/cryptpad into staging
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -12,3 +12,5 @@ www/scratch
|
|||||||
data
|
data
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
pins/
|
pins/
|
||||||
|
blob/
|
||||||
|
privileged.conf
|
||||||
|
|||||||
@@ -9,4 +9,7 @@ server.js
|
|||||||
NetFluxWebsocketSrv.js
|
NetFluxWebsocketSrv.js
|
||||||
NetFluxWebsocketServer.js
|
NetFluxWebsocketServer.js
|
||||||
WebRTCSrv.js
|
WebRTCSrv.js
|
||||||
|
www/common/media-tag.js
|
||||||
|
www/scratch
|
||||||
|
|
||||||
|
www/common/toolbar.js
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"notypeof": true,
|
"notypeof": true,
|
||||||
"shadow": false,
|
"shadow": false,
|
||||||
"undef": true,
|
"undef": true,
|
||||||
"unused": false,
|
"unused": true,
|
||||||
"futurehostile":true,
|
"futurehostile":true,
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"predef": [
|
"predef": [
|
||||||
|
|||||||
@@ -39,10 +39,10 @@ module.exports = {
|
|||||||
if you are deploying to production, you'll probably want to remove
|
if you are deploying to production, you'll probably want to remove
|
||||||
the ws://* directive, and change '*' to your domain
|
the ws://* directive, and change '*' to your domain
|
||||||
*/
|
*/
|
||||||
"connect-src 'self' ws://* wss://*",
|
"connect-src 'self' ws: wss:",
|
||||||
|
|
||||||
// data: is used by codemirror
|
// data: is used by codemirror
|
||||||
"img-src 'self' data:",
|
"img-src 'self' data: blob:",
|
||||||
].join('; '),
|
].join('; '),
|
||||||
|
|
||||||
// CKEditor requires significantly more lax content security policy in order to function.
|
// CKEditor requires significantly more lax content security policy in order to function.
|
||||||
@@ -59,7 +59,7 @@ module.exports = {
|
|||||||
"child-src 'self' *",
|
"child-src 'self' *",
|
||||||
|
|
||||||
// see the comment above in the 'contentSecurity' section
|
// see the comment above in the 'contentSecurity' section
|
||||||
"connect-src 'self' ws://* wss://*",
|
"connect-src 'self' ws: wss:",
|
||||||
|
|
||||||
// (insecure remote) images are included by users of the wysiwyg who embed photos in their pads
|
// (insecure remote) images are included by users of the wysiwyg who embed photos in their pads
|
||||||
"img-src *",
|
"img-src *",
|
||||||
@@ -141,6 +141,23 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
filePath: './datastore/',
|
filePath: './datastore/',
|
||||||
|
|
||||||
|
/* CryptPad allows logged in users to request that particular documents be
|
||||||
|
* stored by the server indefinitely. This is called 'pinning'.
|
||||||
|
* Pin requests are stored in a pin-store. The location of this store is
|
||||||
|
* defined here.
|
||||||
|
*/
|
||||||
|
pinPath: './pins',
|
||||||
|
|
||||||
|
/* CryptPad allows logged in users to upload encrypted files. Files/blobs
|
||||||
|
* are stored in a 'blob-store'. Set its location here.
|
||||||
|
*/
|
||||||
|
blobPath: './blob',
|
||||||
|
|
||||||
|
/* CryptPad stores incomplete blobs in a 'staging' area until they are
|
||||||
|
* fully uploaded. Set its location here.
|
||||||
|
*/
|
||||||
|
blobStagingPath: './blobstage',
|
||||||
|
|
||||||
/* Cryptpad's file storage adaptor closes unused files after a configurale
|
/* Cryptpad's file storage adaptor closes unused files after a configurale
|
||||||
* number of milliseconds (default 30000 (30 seconds))
|
* number of milliseconds (default 30000 (30 seconds))
|
||||||
*/
|
*/
|
||||||
@@ -163,6 +180,31 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
suppressRPCErrors: false,
|
suppressRPCErrors: false,
|
||||||
|
|
||||||
|
|
||||||
|
/* WARNING: EXPERIMENTAL
|
||||||
|
*
|
||||||
|
* CryptPad features experimental support for encrypted file upload.
|
||||||
|
* Our encryption format is still liable to change. As such, we do not
|
||||||
|
* guarantee that files uploaded now will be supported in the future
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Setting this value to anything other than true will cause file upload
|
||||||
|
* attempts to be rejected outright.
|
||||||
|
*/
|
||||||
|
enableUploads: true,
|
||||||
|
|
||||||
|
/* If you have enabled file upload, you have the option of restricting it
|
||||||
|
* to a list of users identified by their public keys. If this value is set
|
||||||
|
* to true, your server will query a file (cryptpad/privileged.conf) when
|
||||||
|
* users connect via RPC. Only users whose public keys can be found within
|
||||||
|
* the file will be allowed to upload.
|
||||||
|
*
|
||||||
|
* privileged.conf uses '#' for line comments, and splits keys by newline.
|
||||||
|
* This is a temporary measure until a better quota system is in place.
|
||||||
|
* registered users' public keys can be found on the settings page.
|
||||||
|
*/
|
||||||
|
restrictUploads: true,
|
||||||
|
|
||||||
/* it is recommended that you serve cryptpad over https
|
/* it is recommended that you serve cryptpad over https
|
||||||
* the filepaths below are used to configure your certificates
|
* the filepaths below are used to configure your certificates
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -106,7 +106,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li class="title" data-localization="footer_contact"><li>
|
<li class="title" data-localization="footer_contact"><li>
|
||||||
<li><a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" target="_blank" rel="noopener noreferrer">IRC</a></li>
|
<li><a href="https://riot.im/app/#/room/!cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
|
||||||
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
||||||
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
||||||
<li><a href="/contact.html">Email</a></li>
|
<li><a href="/contact.html">Email</a></li>
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -37,5 +37,20 @@ define(function() {
|
|||||||
|
|
||||||
config.enableHistory = true;
|
config.enableHistory = true;
|
||||||
|
|
||||||
|
//config.enablePinLimit = true;
|
||||||
|
//config.pinLimit = 1000;
|
||||||
|
|
||||||
|
/* user passwords are hashed with scrypt, and salted with their username.
|
||||||
|
this value will be appended to the username, causing the resulting hash
|
||||||
|
to differ from other CryptPad instances if customized. This makes it
|
||||||
|
such that anyone who wants to bruteforce common credentials must do so
|
||||||
|
again on each CryptPad instance that they wish to attack.
|
||||||
|
|
||||||
|
WARNING: this should only be set when your CryptPad instance is first
|
||||||
|
created. Changing it at a later time will break logins for all existing
|
||||||
|
users.
|
||||||
|
*/
|
||||||
|
config.loginSalt = '';
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -103,7 +103,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li class="title" data-localization="footer_contact"><li>
|
<li class="title" data-localization="footer_contact"><li>
|
||||||
<li><a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" target="_blank" rel="noopener noreferrer">IRC</a></li>
|
<li><a href="https://riot.im/app/#/room/!cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
|
||||||
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
||||||
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
||||||
<li><a href="/contact.html">Email</a></li>
|
<li><a href="/contact.html">Email</a></li>
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -225,7 +225,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li class="title" data-localization="footer_contact"><li>
|
<li class="title" data-localization="footer_contact"><li>
|
||||||
<li><a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" target="_blank" rel="noopener noreferrer">IRC</a></li>
|
<li><a href="https://riot.im/app/#/room/!cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
|
||||||
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
||||||
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
||||||
<li><a href="/contact.html">Email</a></li>
|
<li><a href="/contact.html">Email</a></li>
|
||||||
@@ -233,7 +233,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -388,6 +388,11 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
@media screen and (max-height: 600px) {
|
||||||
|
.cp #loadingTip {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
.cp #loadingTip span {
|
.cp #loadingTip span {
|
||||||
background-color: #302B28;
|
background-color: #302B28;
|
||||||
color: #fafafa;
|
color: #fafafa;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ define([
|
|||||||
'/common/cryptpad-common.js'
|
'/common/cryptpad-common.js'
|
||||||
], function ($, Config, Cryptpad) {
|
], function ($, Config, Cryptpad) {
|
||||||
|
|
||||||
var APP = window.APP = {
|
window.APP = {
|
||||||
Cryptpad: Cryptpad,
|
Cryptpad: Cryptpad,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -118,68 +118,70 @@ define([
|
|||||||
$('button.login').click();
|
$('button.login').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('button.login').click(function (e) {
|
$('button.login').click(function () {
|
||||||
Cryptpad.addLoadingScreen(Messages.login_hashing);
|
// setTimeout 100ms to remove the keyboard on mobile devices before the loading screen pops up
|
||||||
// We need a setTimeout(cb, 0) otherwise the loading screen is only displayed after hashing the password
|
|
||||||
window.setTimeout(function () {
|
window.setTimeout(function () {
|
||||||
loginReady(function () {
|
Cryptpad.addLoadingScreen(Messages.login_hashing);
|
||||||
var uname = $uname.val();
|
// We need a setTimeout(cb, 0) otherwise the loading screen is only displayed after hashing the password
|
||||||
var passwd = $passwd.val();
|
window.setTimeout(function () {
|
||||||
Login.loginOrRegister(uname, passwd, false, function (err, result) {
|
loginReady(function () {
|
||||||
if (!err) {
|
var uname = $uname.val();
|
||||||
var proxy = result.proxy;
|
var passwd = $passwd.val();
|
||||||
|
Login.loginOrRegister(uname, passwd, false, function (err, result) {
|
||||||
|
if (!err) {
|
||||||
|
var proxy = result.proxy;
|
||||||
|
|
||||||
// successful validation and user already exists
|
// successful validation and user already exists
|
||||||
// set user hash in localStorage and redirect to drive
|
// set user hash in localStorage and redirect to drive
|
||||||
if (proxy && !proxy.login_name) {
|
if (proxy && !proxy.login_name) {
|
||||||
proxy.login_name = result.userName;
|
proxy.login_name = result.userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy.edPrivate = result.edPrivate;
|
||||||
|
proxy.edPublic = result.edPublic;
|
||||||
|
|
||||||
|
Cryptpad.whenRealtimeSyncs(result.realtime, function () {
|
||||||
|
Cryptpad.login(result.userHash, result.userName, function () {
|
||||||
|
document.location.href = '/drive/';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
switch (err) {
|
||||||
proxy.edPrivate = result.edPrivate;
|
case 'NO_SUCH_USER':
|
||||||
proxy.edPublic = result.edPublic;
|
Cryptpad.removeLoadingScreen(function () {
|
||||||
|
Cryptpad.alert(Messages.login_noSuchUser);
|
||||||
Cryptpad.whenRealtimeSyncs(result.realtime, function () {
|
});
|
||||||
Cryptpad.login(result.userHash, result.userName, function () {
|
break;
|
||||||
document.location.href = '/drive/';
|
case 'INVAL_USER':
|
||||||
});
|
Cryptpad.removeLoadingScreen(function () {
|
||||||
});
|
Cryptpad.alert(Messages.login_invalUser);
|
||||||
return;
|
});
|
||||||
}
|
break;
|
||||||
switch (err) {
|
case 'INVAL_PASS':
|
||||||
case 'NO_SUCH_USER':
|
Cryptpad.removeLoadingScreen(function () {
|
||||||
Cryptpad.removeLoadingScreen(function () {
|
Cryptpad.alert(Messages.login_invalPass);
|
||||||
Cryptpad.alert(Messages.login_noSuchUser);
|
});
|
||||||
});
|
break;
|
||||||
break;
|
default: // UNHANDLED ERROR
|
||||||
case 'INVAL_USER':
|
Cryptpad.errorLoadingScreen(Messages.login_unhandledError);
|
||||||
Cryptpad.removeLoadingScreen(function () {
|
}
|
||||||
Cryptpad.alert(Messages.login_invalUser);
|
});
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'INVAL_PASS':
|
|
||||||
Cryptpad.removeLoadingScreen(function () {
|
|
||||||
Cryptpad.alert(Messages.login_invalPass);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default: // UNHANDLED ERROR
|
|
||||||
Cryptpad.errorLoadingScreen(Messages.login_unhandledError);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
}, 0);
|
||||||
}, 0);
|
}, 100);
|
||||||
});
|
});
|
||||||
/* End Log in UI */
|
/* End Log in UI */
|
||||||
|
|
||||||
var addButtonHandlers = function () {
|
var addButtonHandlers = function () {
|
||||||
$('button.register').click(function (e) {
|
$('button.register').click(function () {
|
||||||
var username = $('#name').val();
|
var username = $('#name').val();
|
||||||
var passwd = $('#password').val();
|
var passwd = $('#password').val();
|
||||||
var remember = $('#rememberme').is(':checked');
|
|
||||||
sessionStorage.login_user = username;
|
sessionStorage.login_user = username;
|
||||||
sessionStorage.login_pass = passwd;
|
sessionStorage.login_pass = passwd;
|
||||||
document.location.href = '/register/';
|
document.location.href = '/register/';
|
||||||
});
|
});
|
||||||
$('button.gotodrive').click(function (e) {
|
$('button.gotodrive').click(function () {
|
||||||
document.location.href = '/drive/';
|
document.location.href = '/drive/';
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -112,9 +112,7 @@ define(req, function($, Default, Language) {
|
|||||||
|
|
||||||
if (!selector.length) { return; }
|
if (!selector.length) { return; }
|
||||||
|
|
||||||
var $button = $(selector).find('button .buttonTitle');
|
|
||||||
// Select the current language in the list
|
// Select the current language in the list
|
||||||
var option = $(selector).find('[data-value="' + language + '"]');
|
|
||||||
selector.setValue(language || 'English');
|
selector.setValue(language || 'English');
|
||||||
|
|
||||||
// Listen for language change
|
// Listen for language change
|
||||||
@@ -137,12 +135,12 @@ define(req, function($, Default, Language) {
|
|||||||
var key = $el.data('localization-append');
|
var key = $el.data('localization-append');
|
||||||
$el.append(messages[key]);
|
$el.append(messages[key]);
|
||||||
};
|
};
|
||||||
var translateTitle = function (i, e) {
|
var translateTitle = function () {
|
||||||
var $el = $(this);
|
var $el = $(this);
|
||||||
var key = $el.data('localization-title');
|
var key = $el.data('localization-title');
|
||||||
$el.attr('title', messages[key]);
|
$el.attr('title', messages[key]);
|
||||||
};
|
};
|
||||||
var translatePlaceholder = function (i, e) {
|
var translatePlaceholder = function () {
|
||||||
var $el = $(this);
|
var $el = $(this);
|
||||||
var key = $el.data('localization-placeholder');
|
var key = $el.data('localization-placeholder');
|
||||||
$el.attr('placeholder', messages[key]);
|
$el.attr('placeholder', messages[key]);
|
||||||
|
|||||||
@@ -124,7 +124,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li class="title" data-localization="footer_contact"><li>
|
<li class="title" data-localization="footer_contact"><li>
|
||||||
<li><a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" target="_blank" rel="noopener noreferrer">IRC</a></li>
|
<li><a href="https://riot.im/app/#/room/!cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
|
||||||
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
||||||
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
||||||
<li><a href="/contact.html">Email</a></li>
|
<li><a href="/contact.html">Email</a></li>
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
// create an invisible iframe with a given source
|
// create an invisible iframe with a given source
|
||||||
// append it to a parent element
|
// append it to a parent element
|
||||||
// execute a callback when it has loaded
|
// execute a callback when it has loaded
|
||||||
var create = Frame.create = function (parent, src, onload, timeout) {
|
Frame.create = function (parent, src, onload, timeout) {
|
||||||
var iframe = document.createElement('iframe');
|
var iframe = document.createElement('iframe');
|
||||||
|
|
||||||
timeout = timeout || 10000;
|
timeout = timeout || 10000;
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
/* given an iframe with an rpc script loaded, create a frame object
|
/* given an iframe with an rpc script loaded, create a frame object
|
||||||
with an asynchronous 'send' method */
|
with an asynchronous 'send' method */
|
||||||
var open = Frame.open = function (e, A, timeout) {
|
Frame.open = function (e, A, timeout) {
|
||||||
var win = e.contentWindow;
|
var win = e.contentWindow;
|
||||||
|
|
||||||
var frame = {};
|
var frame = {};
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
|
|
||||||
timeout = timeout || 5000;
|
timeout = timeout || 5000;
|
||||||
|
|
||||||
var accepts = frame.accepts = function (o) {
|
frame.accepts = function (o) {
|
||||||
return A.some(function (e) {
|
return A.some(function (e) {
|
||||||
switch (typeof(e)) {
|
switch (typeof(e)) {
|
||||||
case 'string': return e === o;
|
case 'string': return e === o;
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
var changeHandlers = frame.changeHandlers = [];
|
var changeHandlers = frame.changeHandlers = [];
|
||||||
|
|
||||||
var change = frame.change = function (f) {
|
frame.change = function (f) {
|
||||||
if (typeof(f) !== 'function') {
|
if (typeof(f) !== 'function') {
|
||||||
throw new Error('[Frame.change] expected callback');
|
throw new Error('[Frame.change] expected callback');
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
};
|
};
|
||||||
window.addEventListener('message', _listener);
|
window.addEventListener('message', _listener);
|
||||||
|
|
||||||
var close = frame.close = function () {
|
frame.close = function () {
|
||||||
window.removeEventListener('message', _listener);
|
window.removeEventListener('message', _listener);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -130,31 +130,31 @@
|
|||||||
win.postMessage(JSON.stringify(req), '*');
|
win.postMessage(JSON.stringify(req), '*');
|
||||||
};
|
};
|
||||||
|
|
||||||
var set = frame.set = function (key, val, cb) {
|
frame.set = function (key, val, cb) {
|
||||||
send('set', key, val, cb);
|
send('set', key, val, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
var batchset = frame.setBatch = function (map, cb) {
|
frame.setBatch = function (map, cb) {
|
||||||
send('batchset', void 0, map, cb);
|
send('batchset', void 0, map, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
var get = frame.get = function (key, cb) {
|
frame.get = function (key, cb) {
|
||||||
send('get', key, void 0, cb);
|
send('get', key, void 0, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
var batchget = frame.getBatch = function (keys, cb) {
|
frame.getBatch = function (keys, cb) {
|
||||||
send('batchget', void 0, keys, cb);
|
send('batchget', void 0, keys, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
var remove = frame.remove = function (key, cb) {
|
frame.remove = function (key, cb) {
|
||||||
send('remove', key, void 0, cb);
|
send('remove', key, void 0, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
var batchremove = frame.removeBatch = function (keys, cb) {
|
frame.removeBatch = function (keys, cb) {
|
||||||
send('batchremove', void 0, keys, cb);
|
send('batchremove', void 0, keys, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
var keys = frame.keys = function (cb) {
|
frame.keys = function (cb) {
|
||||||
send('keys', void 0, void 0, cb);
|
send('keys', void 0, void 0, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@
|
|||||||
if (typeof(module) !== 'undefined' && module.exports) {
|
if (typeof(module) !== 'undefined' && module.exports) {
|
||||||
module.exports = Frame;
|
module.exports = Frame;
|
||||||
} else if (typeof(define) === 'function' && define.amd) {
|
} else if (typeof(define) === 'function' && define.amd) {
|
||||||
define(['jquery'], function ($) {
|
define(['jquery'], function () {
|
||||||
return Frame;
|
return Frame;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ define([
|
|||||||
return !keys.some(function (k) { return data[k] !== null; });
|
return !keys.some(function (k) { return data[k] !== null; });
|
||||||
};
|
};
|
||||||
|
|
||||||
Frame.create(document.body, domain + path, function (err, iframe, loadEvent) {
|
Frame.create(document.body, domain + path, function (err, iframe) {
|
||||||
if (handleErr(err)) { return; }
|
if (handleErr(err)) { return; }
|
||||||
console.log("Created iframe");
|
console.log("Created iframe");
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ define([
|
|||||||
|
|
||||||
[function (i) { // test #1
|
[function (i) { // test #1
|
||||||
var pew = randInt();
|
var pew = randInt();
|
||||||
frame.set('pew', pew, function (err, data) {
|
frame.set('pew', pew, function (err) {
|
||||||
if (handleErr(err)) { return; }
|
if (handleErr(err)) { return; }
|
||||||
frame.get('pew', function (err, num) {
|
frame.get('pew', function (err, num) {
|
||||||
if (handleErr(err)) { return; }
|
if (handleErr(err)) { return; }
|
||||||
@@ -76,9 +76,9 @@ define([
|
|||||||
|
|
||||||
var keys = Object.keys(map);
|
var keys = Object.keys(map);
|
||||||
|
|
||||||
frame.setBatch(map, function (err, data) {
|
frame.setBatch(map, function (err) {
|
||||||
if (handleErr(err)) { return; }
|
if (handleErr(err)) { return; }
|
||||||
frame.getBatch(keys, function (err, data) {
|
frame.getBatch(keys, function (err) {
|
||||||
if (handleErr(err)) { return; }
|
if (handleErr(err)) { return; }
|
||||||
frame.removeBatch(Object.keys(map), function (err) {
|
frame.removeBatch(Object.keys(map), function (err) {
|
||||||
if (handleErr(err)) { return; }
|
if (handleErr(err)) { return; }
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li class="title" data-localization="footer_contact"><li>
|
<li class="title" data-localization="footer_contact"><li>
|
||||||
<li><a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" target="_blank" rel="noopener noreferrer">IRC</a></li>
|
<li><a href="https://riot.im/app/#/room/!cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
|
||||||
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
||||||
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
||||||
<li><a href="/contact.html">Email</a></li>
|
<li><a href="/contact.html">Email</a></li>
|
||||||
@@ -39,5 +39,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -36,6 +36,9 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@media screen and (max-height: @media-medium-screen) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
span {
|
span {
|
||||||
background-color: @bg-loading;
|
background-color: @bg-loading;
|
||||||
color: @color-loading;
|
color: @color-loading;
|
||||||
|
|||||||
@@ -42,12 +42,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
&#shareButton {
|
&#shareButton, &.buttonSuccess {
|
||||||
// Bootstrap 4 colors
|
// Bootstrap 4 colors
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: @toolbar-green;
|
background: @toolbar-green;
|
||||||
border-color: @toolbar-green;
|
border-color: @toolbar-green;
|
||||||
&:hover {
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
background: #449d44;
|
background: #449d44;
|
||||||
border: 1px solid #419641;
|
border: 1px solid #419641;
|
||||||
}
|
}
|
||||||
@@ -58,12 +59,13 @@
|
|||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&#newdoc {
|
&#newdoc, &.buttonPrimary {
|
||||||
// Bootstrap 4 colors
|
// Bootstrap 4 colors
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #0275d8;
|
background: #0275d8;
|
||||||
border-color: #0275d8;
|
border-color: #0275d8;
|
||||||
&:hover {
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
background: #025aa5;
|
background: #025aa5;
|
||||||
border: 1px solid #01549b;
|
border: 1px solid #01549b;
|
||||||
}
|
}
|
||||||
@@ -77,26 +79,84 @@
|
|||||||
&.hidden {
|
&.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.cryptpad-lag {
|
// Bootstrap 4 colors (btn-secondary)
|
||||||
box-sizing: content-box;
|
border: 1px solid transparent;
|
||||||
height: 16px;
|
border-radius: .25rem;
|
||||||
width: 16px;
|
color: #292b2c;
|
||||||
display: inline-block;
|
background-color: #fff;
|
||||||
padding: 5px;
|
border-color: #ccc;
|
||||||
margin: 3px 0;
|
&:hover {
|
||||||
div {
|
color: #292b2c;
|
||||||
margin: auto;
|
background-color: #e6e6e6;
|
||||||
|
border-color: #adadad;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.clag () {
|
button.upgrade {
|
||||||
background: transparent;
|
font-size: 14px;
|
||||||
}
|
vertical-align: top;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.cryptpad-drive-limit {
|
||||||
|
display: inline-block;
|
||||||
|
height: 26px;
|
||||||
|
width: 200px;
|
||||||
|
margin: 2px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #999;
|
||||||
|
background: white;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 24px;
|
||||||
|
.usage {
|
||||||
|
height: 24px;
|
||||||
|
display: inline-block;
|
||||||
|
background: blue;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
z-index:1;
|
||||||
|
&.normal {
|
||||||
|
background: @toolbar-green;
|
||||||
|
}
|
||||||
|
&.warning {
|
||||||
|
background: orange;
|
||||||
|
}
|
||||||
|
&.above {
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.usageText {
|
||||||
|
position: relative;
|
||||||
|
color: black;
|
||||||
|
text-shadow: 1px 0 2px white, 0 1px 2px white, -1px 0 2px white, 0 -1px 2px white;
|
||||||
|
z-index: 2;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.cryptpad-limit {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 26px;
|
||||||
|
width: 26px;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px;
|
||||||
|
margin: 0px;
|
||||||
|
margin-right: 3px;
|
||||||
|
vertical-align: middle;
|
||||||
|
span {
|
||||||
|
color: red;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: auto;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.clag () {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
#newLag {
|
.cryptpad-lag {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: 23px;
|
width: 23px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@@ -179,17 +239,6 @@
|
|||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
|
||||||
color: #000;
|
|
||||||
background-color: inherit;
|
|
||||||
background-image: linear-gradient(to bottom,#fff,#e4e4e4);
|
|
||||||
border: 1px solid #A6A6A6;
|
|
||||||
border-bottom-color: #979797;
|
|
||||||
border-radius: 3px;
|
|
||||||
&:hover {
|
|
||||||
background-image:linear-gradient(to bottom,#f2f2f2,#ccc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.cryptpad-state {
|
.cryptpad-state {
|
||||||
line-height: 32px; /* equivalent to 26px + 2*2px margin used for buttons */
|
line-height: 32px; /* equivalent to 26px + 2*2px margin used for buttons */
|
||||||
}
|
}
|
||||||
@@ -378,9 +427,8 @@
|
|||||||
.cryptpad-user {
|
.cryptpad-user {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
span:not(.cryptpad-lag) {
|
:not(.cryptpad-lag) span {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
//display: inline-block;
|
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
span.fa {
|
span.fa {
|
||||||
@@ -392,11 +440,9 @@
|
|||||||
.cryptpad-toolbar-leftside {
|
.cryptpad-toolbar-leftside {
|
||||||
float: left;
|
float: left;
|
||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
.cryptpad-user-list {
|
.cryptpad-dropdown-users {
|
||||||
//float: right;
|
|
||||||
pre {
|
pre {
|
||||||
white-space: pre;
|
margin: 5px 0px;
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
@@ -413,14 +459,20 @@
|
|||||||
display: none;
|
display: none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
.next {
|
.next {
|
||||||
float: right;
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 20px;
|
||||||
}
|
}
|
||||||
.previous {
|
.previous {
|
||||||
float: left;
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 20px;
|
||||||
}
|
}
|
||||||
.goto {
|
.goto {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
input { width: 50px; }
|
vertical-align: middle;
|
||||||
|
text-align: center;
|
||||||
|
input { width: 75px; }
|
||||||
}
|
}
|
||||||
.gotoInput {
|
.gotoInput {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
@@ -434,7 +486,7 @@
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.cryptpad-spinner {
|
.cryptpad-spinner > span {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
|
|||||||
@@ -107,7 +107,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li class="title" data-localization="footer_contact"><li>
|
<li class="title" data-localization="footer_contact"><li>
|
||||||
<li><a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" target="_blank" rel="noopener noreferrer">IRC</a></li>
|
<li><a href="https://riot.im/app/#/room/!cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
|
||||||
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
||||||
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
||||||
<li><a href="/contact.html">Email</a></li>
|
<li><a href="/contact.html">Email</a></li>
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -117,51 +117,120 @@
|
|||||||
.cryptpad-toolbar a {
|
.cryptpad-toolbar a {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar button#shareButton {
|
.cryptpad-toolbar button {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: .25rem;
|
||||||
|
color: #292b2c;
|
||||||
|
background-color: #fff;
|
||||||
|
border-color: #ccc;
|
||||||
|
}
|
||||||
|
.cryptpad-toolbar button#shareButton,
|
||||||
|
.cryptpad-toolbar button.buttonSuccess {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #5cb85c;
|
background: #5cb85c;
|
||||||
border-color: #5cb85c;
|
border-color: #5cb85c;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar button#shareButton:hover {
|
.cryptpad-toolbar button#shareButton:hover,
|
||||||
|
.cryptpad-toolbar button.buttonSuccess:hover {
|
||||||
|
color: #fff;
|
||||||
background: #449d44;
|
background: #449d44;
|
||||||
border: 1px solid #419641;
|
border: 1px solid #419641;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar button#shareButton span {
|
.cryptpad-toolbar button#shareButton span,
|
||||||
|
.cryptpad-toolbar button.buttonSuccess span {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar button#shareButton .large {
|
.cryptpad-toolbar button#shareButton .large,
|
||||||
|
.cryptpad-toolbar button.buttonSuccess .large {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar button#newdoc {
|
.cryptpad-toolbar button#newdoc,
|
||||||
|
.cryptpad-toolbar button.buttonPrimary {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #0275d8;
|
background: #0275d8;
|
||||||
border-color: #0275d8;
|
border-color: #0275d8;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar button#newdoc:hover {
|
.cryptpad-toolbar button#newdoc:hover,
|
||||||
|
.cryptpad-toolbar button.buttonPrimary:hover {
|
||||||
|
color: #fff;
|
||||||
background: #025aa5;
|
background: #025aa5;
|
||||||
border: 1px solid #01549b;
|
border: 1px solid #01549b;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar button#newdoc span {
|
.cryptpad-toolbar button#newdoc span,
|
||||||
|
.cryptpad-toolbar button.buttonPrimary span {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar button#newdoc .large {
|
.cryptpad-toolbar button#newdoc .large,
|
||||||
|
.cryptpad-toolbar button.buttonPrimary .large {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar button.hidden {
|
.cryptpad-toolbar button.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar .cryptpad-lag {
|
.cryptpad-toolbar button:hover {
|
||||||
box-sizing: content-box;
|
color: #292b2c;
|
||||||
height: 16px;
|
background-color: #e6e6e6;
|
||||||
width: 16px;
|
border-color: #adadad;
|
||||||
|
}
|
||||||
|
.cryptpad-toolbar button.upgrade {
|
||||||
|
font-size: 14px;
|
||||||
|
vertical-align: top;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.cryptpad-toolbar .cryptpad-drive-limit {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 5px;
|
height: 26px;
|
||||||
margin: 3px 0;
|
width: 200px;
|
||||||
|
margin: 2px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #999;
|
||||||
|
background: white;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar .cryptpad-lag div {
|
.cryptpad-toolbar .cryptpad-drive-limit .usage {
|
||||||
|
height: 24px;
|
||||||
|
display: inline-block;
|
||||||
|
background: blue;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.cryptpad-toolbar .cryptpad-drive-limit .usage.normal {
|
||||||
|
background: #5cb85c;
|
||||||
|
}
|
||||||
|
.cryptpad-toolbar .cryptpad-drive-limit .usage.warning {
|
||||||
|
background: orange;
|
||||||
|
}
|
||||||
|
.cryptpad-toolbar .cryptpad-drive-limit .usage.above {
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
.cryptpad-toolbar .cryptpad-drive-limit .usageText {
|
||||||
|
position: relative;
|
||||||
|
color: black;
|
||||||
|
text-shadow: 1px 0 2px white, 0 1px 2px white, -1px 0 2px white, 0 -1px 2px white;
|
||||||
|
z-index: 2;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.cryptpad-toolbar .cryptpad-limit {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 26px;
|
||||||
|
width: 26px;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px;
|
||||||
|
margin: 0px;
|
||||||
|
margin-right: 3px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.cryptpad-toolbar .cryptpad-limit span {
|
||||||
|
color: red;
|
||||||
|
cursor: pointer;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag {
|
.cryptpad-toolbar .cryptpad-lag {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: 23px;
|
width: 23px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@@ -171,7 +240,7 @@
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag span {
|
.cryptpad-toolbar .cryptpad-lag span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 4px;
|
width: 4px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -182,50 +251,50 @@
|
|||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
transition: background 1s, border 1s;
|
transition: background 1s, border 1s;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag span:last-child {
|
.cryptpad-toolbar .cryptpad-lag span:last-child {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag span.bar1 {
|
.cryptpad-toolbar .cryptpad-lag span.bar1 {
|
||||||
height: 5px;
|
height: 5px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag span.bar2 {
|
.cryptpad-toolbar .cryptpad-lag span.bar2 {
|
||||||
height: 10px;
|
height: 10px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag span.bar3 {
|
.cryptpad-toolbar .cryptpad-lag span.bar3 {
|
||||||
height: 15px;
|
height: 15px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag span.bar4 {
|
.cryptpad-toolbar .cryptpad-lag span.bar4 {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag.lag0 span {
|
.cryptpad-toolbar .cryptpad-lag.lag0 span {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border-color: red;
|
border-color: red;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag.lag1 .bar2,
|
.cryptpad-toolbar .cryptpad-lag.lag1 .bar2,
|
||||||
.cryptpad-toolbar #newLag.lag1 .bar3,
|
.cryptpad-toolbar .cryptpad-lag.lag1 .bar3,
|
||||||
.cryptpad-toolbar #newLag.lag1 .bar4 {
|
.cryptpad-toolbar .cryptpad-lag.lag1 .bar4 {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag.lag1 span {
|
.cryptpad-toolbar .cryptpad-lag.lag1 span {
|
||||||
background-color: orange;
|
background-color: orange;
|
||||||
border-color: orange;
|
border-color: orange;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag.lag2 .bar3,
|
.cryptpad-toolbar .cryptpad-lag.lag2 .bar3,
|
||||||
.cryptpad-toolbar #newLag.lag2 .bar4 {
|
.cryptpad-toolbar .cryptpad-lag.lag2 .bar4 {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag.lag2 span {
|
.cryptpad-toolbar .cryptpad-lag.lag2 span {
|
||||||
background-color: orange;
|
background-color: orange;
|
||||||
border-color: orange;
|
border-color: orange;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag.lag3 .bar4 {
|
.cryptpad-toolbar .cryptpad-lag.lag3 .bar4 {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag.lag3 span {
|
.cryptpad-toolbar .cryptpad-lag.lag3 span {
|
||||||
background-color: #5cb85c;
|
background-color: #5cb85c;
|
||||||
border-color: #5cb85c;
|
border-color: #5cb85c;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar #newLag.lag4 span {
|
.cryptpad-toolbar .cryptpad-lag.lag4 span {
|
||||||
background-color: #5cb85c;
|
background-color: #5cb85c;
|
||||||
border-color: #5cb85c;
|
border-color: #5cb85c;
|
||||||
}
|
}
|
||||||
@@ -250,17 +319,6 @@
|
|||||||
margin-top: -3px;
|
margin-top: -3px;
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar button {
|
|
||||||
color: #000;
|
|
||||||
background-color: inherit;
|
|
||||||
background-image: linear-gradient(to bottom, #fff, #e4e4e4);
|
|
||||||
border: 1px solid #A6A6A6;
|
|
||||||
border-bottom-color: #979797;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
.cryptpad-toolbar button:hover {
|
|
||||||
background-image: linear-gradient(to bottom, #f2f2f2, #ccc);
|
|
||||||
}
|
|
||||||
.cryptpad-toolbar .cryptpad-state {
|
.cryptpad-toolbar .cryptpad-state {
|
||||||
line-height: 32px;
|
line-height: 32px;
|
||||||
/* equivalent to 26px + 2*2px margin used for buttons */
|
/* equivalent to 26px + 2*2px margin used for buttons */
|
||||||
@@ -449,7 +507,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar-top .cryptpad-user span:not(.cryptpad-lag) {
|
.cryptpad-toolbar-top .cryptpad-user :not(.cryptpad-lag) span {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar-top .cryptpad-user button span.fa {
|
.cryptpad-toolbar-top .cryptpad-user button span.fa {
|
||||||
@@ -459,9 +517,8 @@
|
|||||||
float: left;
|
float: left;
|
||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar-leftside .cryptpad-user-list pre {
|
.cryptpad-toolbar-leftside .cryptpad-dropdown-users pre {
|
||||||
white-space: pre;
|
margin: 5px 0px;
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar-leftside button {
|
.cryptpad-toolbar-leftside button {
|
||||||
margin: 2px 4px 2px 0px;
|
margin: 2px 4px 2px 0px;
|
||||||
@@ -477,16 +534,22 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar-history .next {
|
.cryptpad-toolbar-history .next {
|
||||||
float: right;
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 20px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar-history .previous {
|
.cryptpad-toolbar-history .previous {
|
||||||
float: left;
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 20px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar-history .goto {
|
.cryptpad-toolbar-history .goto {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar-history .goto input {
|
.cryptpad-toolbar-history .goto input {
|
||||||
width: 50px;
|
width: 75px;
|
||||||
}
|
}
|
||||||
.cryptpad-toolbar-history .gotoInput {
|
.cryptpad-toolbar-history .gotoInput {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
@@ -497,7 +560,7 @@
|
|||||||
padding: 3px 3px;
|
padding: 3px 3px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
.cryptpad-spinner {
|
.cryptpad-spinner > span {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ define(function () {
|
|||||||
out.type.slide = 'Présentation';
|
out.type.slide = 'Présentation';
|
||||||
out.type.drive = 'Drive';
|
out.type.drive = 'Drive';
|
||||||
out.type.whiteboard = "Tableau Blanc";
|
out.type.whiteboard = "Tableau Blanc";
|
||||||
|
out.type.file = "Fichier";
|
||||||
|
out.type.media = "Média";
|
||||||
|
|
||||||
out.button_newpad = 'Nouveau document texte';
|
out.button_newpad = 'Nouveau document texte';
|
||||||
out.button_newcode = 'Nouvelle page de code';
|
out.button_newcode = 'Nouvelle page de code';
|
||||||
@@ -49,10 +51,22 @@ define(function () {
|
|||||||
|
|
||||||
out.language = "Langue";
|
out.language = "Langue";
|
||||||
|
|
||||||
|
out.upgrade = "Améliorer";
|
||||||
|
out.upgradeTitle = "Améliorer votre compte pour augmenter la limite de stockage";
|
||||||
|
out.MB = "Mo";
|
||||||
|
|
||||||
out.greenLight = "Tout fonctionne bien";
|
out.greenLight = "Tout fonctionne bien";
|
||||||
out.orangeLight = "Votre connexion est lente, ce qui réduit la qualité de l'éditeur";
|
out.orangeLight = "Votre connexion est lente, ce qui réduit la qualité de l'éditeur";
|
||||||
out.redLight = "Vous êtes déconnectés de la session";
|
out.redLight = "Vous êtes déconnectés de la session";
|
||||||
|
|
||||||
|
out.pinLimitReached = "Vous avez atteint votre limite de stockage";
|
||||||
|
out.pinLimitReachedAlert = "Vous avez atteint votre limite de stockage. Les nouveaux pads ne seront pas enregistrés dans votre CrypDrive.<br>" +
|
||||||
|
"Pour résoudre ce problème, vous pouvez soit supprimer des pads de votre CryptDrive (y compris la corbeille), soit vous abonner à une offre premium pour augmenter la limite maximale.";
|
||||||
|
out.pinLimitNotPinned = "Vous avez atteint votre limite de stockage.<br>"+
|
||||||
|
"Ce pad n'est pas enregistré dans votre CryptDrive.";
|
||||||
|
out.pinLimitDrive = out.pinLimitReached+ ".<br>" +
|
||||||
|
"Vous ne pouvez pas créer de nouveaux pads.";
|
||||||
|
|
||||||
out.importButtonTitle = 'Importer un pad depuis un fichier local';
|
out.importButtonTitle = 'Importer un pad depuis un fichier local';
|
||||||
|
|
||||||
out.exportButtonTitle = 'Exporter ce pad vers un fichier local';
|
out.exportButtonTitle = 'Exporter ce pad vers un fichier local';
|
||||||
@@ -93,6 +107,7 @@ define(function () {
|
|||||||
out.printDate = "Afficher la date";
|
out.printDate = "Afficher la date";
|
||||||
out.printTitle = "Afficher le titre du pad";
|
out.printTitle = "Afficher le titre du pad";
|
||||||
out.printCSS = "Personnaliser l'apparence (CSS):";
|
out.printCSS = "Personnaliser l'apparence (CSS):";
|
||||||
|
out.printTransition = "Activer les animations de transition";
|
||||||
|
|
||||||
out.slideOptionsTitle = "Personnaliser la présentation";
|
out.slideOptionsTitle = "Personnaliser la présentation";
|
||||||
out.slideOptionsButton = "Enregistrer (Entrée)";
|
out.slideOptionsButton = "Enregistrer (Entrée)";
|
||||||
@@ -125,6 +140,7 @@ define(function () {
|
|||||||
out.history_restoreTitle = "Restaurer la version du document sélectionnée";
|
out.history_restoreTitle = "Restaurer la version du document sélectionnée";
|
||||||
out.history_restorePrompt = "Êtes-vous sûr de vouloir remplacer la version actuelle du document par la version affichée ?";
|
out.history_restorePrompt = "Êtes-vous sûr de vouloir remplacer la version actuelle du document par la version affichée ?";
|
||||||
out.history_restoreDone = "Document restauré";
|
out.history_restoreDone = "Document restauré";
|
||||||
|
out.history_version = "Version :";
|
||||||
|
|
||||||
// Polls
|
// Polls
|
||||||
|
|
||||||
@@ -313,6 +329,10 @@ define(function () {
|
|||||||
out.settings_pinningError = "Un problème est survenu";
|
out.settings_pinningError = "Un problème est survenu";
|
||||||
out.settings_usageAmount = "Vos pads épinglés occupent {0} Mo";
|
out.settings_usageAmount = "Vos pads épinglés occupent {0} Mo";
|
||||||
|
|
||||||
|
out.settings_logoutEverywhereTitle = "Se déconnecter partout";
|
||||||
|
out.settings_logoutEverywhere = "Se déconnecter de toutes les autres sessions.";
|
||||||
|
out.settings_logoutEverywhereConfirm = "Êtes-vous sûr ? Vous devrez vous reconnecter sur tous vos autres appareils.";
|
||||||
|
|
||||||
// index.html
|
// index.html
|
||||||
|
|
||||||
//about.html
|
//about.html
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ define(function () {
|
|||||||
out.type.slide = 'Presentation';
|
out.type.slide = 'Presentation';
|
||||||
out.type.drive = 'Drive';
|
out.type.drive = 'Drive';
|
||||||
out.type.whiteboard = 'Whiteboard';
|
out.type.whiteboard = 'Whiteboard';
|
||||||
|
out.type.file = 'File';
|
||||||
|
out.type.media = 'Media';
|
||||||
|
|
||||||
out.button_newpad = 'New Rich Text pad';
|
out.button_newpad = 'New Rich Text pad';
|
||||||
out.button_newcode = 'New Code pad';
|
out.button_newcode = 'New Code pad';
|
||||||
@@ -51,10 +53,22 @@ define(function () {
|
|||||||
|
|
||||||
out.language = "Language";
|
out.language = "Language";
|
||||||
|
|
||||||
|
out.upgrade = "Upgrade";
|
||||||
|
out.upgradeTitle = "Upgrade your account to increase the storage limit";
|
||||||
|
out.MB = "MB";
|
||||||
|
|
||||||
out.greenLight = "Everything is working fine";
|
out.greenLight = "Everything is working fine";
|
||||||
out.orangeLight = "Your slow connection may impact your experience";
|
out.orangeLight = "Your slow connection may impact your experience";
|
||||||
out.redLight = "You are disconnected from the session";
|
out.redLight = "You are disconnected from the session";
|
||||||
|
|
||||||
|
out.pinLimitReached = "You've reached your storage limit";
|
||||||
|
out.pinLimitReachedAlert = "You've reached your storage limit. New pads won't be stored in your CryptDrive.<br>" +
|
||||||
|
"To fix this problem, you can either remove pads from your CryptDrive (including the trash) or subscribe to a premium offer to increase your limit.";
|
||||||
|
out.pinLimitNotPinned = "You've reached your storage limit.<br>"+
|
||||||
|
"This pad is not stored in your CryptDrive.";
|
||||||
|
out.pinLimitDrive = "You've reached your storage limit.<br>" +
|
||||||
|
"You can't create new pads.";
|
||||||
|
|
||||||
out.importButtonTitle = 'Import a pad from a local file';
|
out.importButtonTitle = 'Import a pad from a local file';
|
||||||
|
|
||||||
out.exportButtonTitle = 'Export this pad to a local file';
|
out.exportButtonTitle = 'Export this pad to a local file';
|
||||||
@@ -95,6 +109,7 @@ define(function () {
|
|||||||
out.printDate = "Display the date";
|
out.printDate = "Display the date";
|
||||||
out.printTitle = "Display the pad title";
|
out.printTitle = "Display the pad title";
|
||||||
out.printCSS = "Custom style rules (CSS):";
|
out.printCSS = "Custom style rules (CSS):";
|
||||||
|
out.printTransition = "Enable transition animations";
|
||||||
|
|
||||||
out.slideOptionsTitle = "Customize your slides";
|
out.slideOptionsTitle = "Customize your slides";
|
||||||
out.slideOptionsButton = "Save (enter)";
|
out.slideOptionsButton = "Save (enter)";
|
||||||
@@ -127,6 +142,7 @@ define(function () {
|
|||||||
out.history_restoreTitle = "Restore the selected version of the document";
|
out.history_restoreTitle = "Restore the selected version of the document";
|
||||||
out.history_restorePrompt = "Are you sure you want to replace the current version of the document by the displayed one?";
|
out.history_restorePrompt = "Are you sure you want to replace the current version of the document by the displayed one?";
|
||||||
out.history_restoreDone = "Document restored";
|
out.history_restoreDone = "Document restored";
|
||||||
|
out.history_version = "Version:";
|
||||||
|
|
||||||
// Polls
|
// Polls
|
||||||
|
|
||||||
@@ -318,6 +334,10 @@ define(function () {
|
|||||||
out.settings_pinningError = "Something went wrong";
|
out.settings_pinningError = "Something went wrong";
|
||||||
out.settings_usageAmount = "Your pinned pads occupy {0}MB";
|
out.settings_usageAmount = "Your pinned pads occupy {0}MB";
|
||||||
|
|
||||||
|
out.settings_logoutEverywhereTitle = "Log out everywhere";
|
||||||
|
out.settings_logoutEverywhere = "Log out of all other web sessions";
|
||||||
|
out.settings_logoutEverywhereConfirm = "Are you sure? You will need to log in with all your devices.";
|
||||||
|
|
||||||
// index.html
|
// index.html
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ server {
|
|||||||
set $scriptSrc "'self'";
|
set $scriptSrc "'self'";
|
||||||
set $connectSrc "'self' wss://cryptpad.fr wss://api.cryptpad.fr";
|
set $connectSrc "'self' wss://cryptpad.fr wss://api.cryptpad.fr";
|
||||||
set $fontSrc "'self'";
|
set $fontSrc "'self'";
|
||||||
set $imgSrc "data: *";
|
set $imgSrc "data: * blob:";
|
||||||
set $frameSrc "'self' beta.cryptpad.fr";
|
set $frameSrc "'self' beta.cryptpad.fr";
|
||||||
|
|
||||||
if ($uri = /pad/inner.html) {
|
if ($uri = /pad/inner.html) {
|
||||||
@@ -65,8 +65,12 @@ server {
|
|||||||
rewrite ^.*$ /customize/api/config break;
|
rewrite ^.*$ /customize/api/config break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location ^~ /blob/ {
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
## TODO fix in the code so that we don't need this
|
## TODO fix in the code so that we don't need this
|
||||||
location ~ ^/(register|login|settings|user|pad|drive|poll|slide|code|whiteboard)$ {
|
location ~ ^/(register|login|settings|user|pad|drive|poll|slide|code|whiteboard|file|media)$ {
|
||||||
rewrite ^(.*)$ $1/ redirect;
|
rewrite ^(.*)$ $1/ redirect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "cryptpad",
|
"name": "cryptpad",
|
||||||
"description": "realtime collaborative visual editor with zero knowlege server",
|
"description": "realtime collaborative visual editor with zero knowlege server",
|
||||||
"version": "1.5.0",
|
"version": "1.6.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chainpad-server": "^1.0.1",
|
"chainpad-server": "^1.0.1",
|
||||||
"express": "~4.10.1",
|
"express": "~4.10.1",
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ These settings can be found in your configuration file in the `contentSecurity`
|
|||||||
|
|
||||||
## Maintenance
|
## Maintenance
|
||||||
|
|
||||||
|
Before upgrading your CryptPad instance to the latest version, we recommend that you check what has changed since your last update.
|
||||||
|
You can do so by checking which version you have (see package.json), and comparing it against newer [release notes](https://github.com/xwiki-labs/cryptpad/releases).
|
||||||
|
|
||||||
To get access to the most recent codebase:
|
To get access to the most recent codebase:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -70,9 +73,12 @@ bower update;
|
|||||||
# serverside dependencies
|
# serverside dependencies
|
||||||
npm update;
|
npm update;
|
||||||
```
|
```
|
||||||
|
## Deleting all data and resetting Cryptpad
|
||||||
|
|
||||||
|
|
||||||
To reset your instance of Cryptpad and remove all the data that is being stored:
|
To reset your instance of Cryptpad and remove all the data that is being stored:
|
||||||
|
|
||||||
|
**WARNING: This will reset your Cryptpad instance and remove all data**
|
||||||
```
|
```
|
||||||
# change into your cryptpad directory
|
# change into your cryptpad directory
|
||||||
cd /your/cryptpad/instance/location;
|
cd /your/cryptpad/instance/location;
|
||||||
|
|||||||
339
rpc.js
339
rpc.js
@@ -2,12 +2,44 @@
|
|||||||
/* Use Nacl for checking signatures of messages */
|
/* Use Nacl for checking signatures of messages */
|
||||||
var Nacl = require("tweetnacl");
|
var Nacl = require("tweetnacl");
|
||||||
|
|
||||||
|
/* globals Buffer*/
|
||||||
|
/* globals process */
|
||||||
|
|
||||||
|
var Fs = require("fs");
|
||||||
|
var Path = require("path");
|
||||||
|
|
||||||
var RPC = module.exports;
|
var RPC = module.exports;
|
||||||
|
|
||||||
var Store = require("./storage/file");
|
var Store = require("./storage/file");
|
||||||
|
|
||||||
var isValidChannel = function (chan) {
|
var isValidChannel = function (chan) {
|
||||||
return /^[a-fA-F0-9]/.test(chan);
|
return /^[a-fA-F0-9]/.test(chan) ||
|
||||||
|
[32, 48].indexOf(chan.length) !== -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
var uint8ArrayToHex = function (a) {
|
||||||
|
// call slice so Uint8Arrays work as expected
|
||||||
|
return Array.prototype.slice.call(a).map(function (e) {
|
||||||
|
var n = Number(e & 0xff).toString(16);
|
||||||
|
if (n === 'NaN') {
|
||||||
|
throw new Error('invalid input resulted in NaN');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (n.length) {
|
||||||
|
case 0: return '00'; // just being careful, shouldn't happen
|
||||||
|
case 1: return '0' + n;
|
||||||
|
case 2: return n;
|
||||||
|
default: throw new Error('unexpected value');
|
||||||
|
}
|
||||||
|
}).join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
var createFileId = function () {
|
||||||
|
var id = uint8ArrayToHex(Nacl.randomBytes(24));
|
||||||
|
if (id.length !== 48 || /[^a-f0-9]/.test(id)) {
|
||||||
|
throw new Error('file ids must consist of 48 hex characters');
|
||||||
|
}
|
||||||
|
return id;
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeToken = function () {
|
var makeToken = function () {
|
||||||
@@ -21,7 +53,7 @@ var makeCookie = function (token) {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
time,
|
time,
|
||||||
process.pid, // jshint ignore:line
|
process.pid,
|
||||||
token
|
token
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -59,7 +91,11 @@ var isTooOld = function (time, now) {
|
|||||||
var expireSessions = function (Sessions) {
|
var expireSessions = function (Sessions) {
|
||||||
var now = +new Date();
|
var now = +new Date();
|
||||||
Object.keys(Sessions).forEach(function (key) {
|
Object.keys(Sessions).forEach(function (key) {
|
||||||
|
var session = Sessions[key];
|
||||||
if (isTooOld(Sessions[key].atime, now)) {
|
if (isTooOld(Sessions[key].atime, now)) {
|
||||||
|
if (session.blobstage) {
|
||||||
|
session.blobstage.close();
|
||||||
|
}
|
||||||
delete Sessions[key];
|
delete Sessions[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -86,7 +122,7 @@ var isValidCookie = function (Sessions, publicKey, cookie) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// different process. try harder
|
// different process. try harder
|
||||||
if (process.pid !== parsed.pid) { // jshint ignore:line
|
if (process.pid !== parsed.pid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +132,6 @@ var isValidCookie = function (Sessions, publicKey, cookie) {
|
|||||||
var idx = user.tokens.indexOf(parsed.seq);
|
var idx = user.tokens.indexOf(parsed.seq);
|
||||||
if (idx === -1) { return false; }
|
if (idx === -1) { return false; }
|
||||||
|
|
||||||
var next;
|
|
||||||
if (idx > 0) {
|
if (idx > 0) {
|
||||||
// make a new token
|
// make a new token
|
||||||
addTokenForKey(Sessions, publicKey, makeToken());
|
addTokenForKey(Sessions, publicKey, makeToken());
|
||||||
@@ -211,14 +246,32 @@ var getChannelList = function (store, Sessions, publicKey, cb) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var getUploadSize = function (store, channel, cb) {
|
||||||
|
var path = '';
|
||||||
|
|
||||||
|
Fs.stat(path, function (err, stats) {
|
||||||
|
if (err) { return void cb(err); }
|
||||||
|
cb(void 0, stats.size);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var getFileSize = function (store, channel, cb) {
|
var getFileSize = function (store, channel, cb) {
|
||||||
if (!isValidChannel(channel)) { return void cb('INVALID_CHAN'); }
|
if (!isValidChannel(channel)) { return void cb('INVALID_CHAN'); }
|
||||||
if (typeof(store.getChannelSize) !== 'function') {
|
|
||||||
return cb('GET_CHANNEL_SIZE_UNSUPPORTED');
|
if (channel.length === 32) {
|
||||||
|
if (typeof(store.getChannelSize) !== 'function') {
|
||||||
|
return cb('GET_CHANNEL_SIZE_UNSUPPORTED');
|
||||||
|
}
|
||||||
|
|
||||||
|
return void store.getChannelSize(channel, function (e, size) {
|
||||||
|
if (e) { return void cb(e.code); }
|
||||||
|
cb(void 0, size);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return void store.getChannelSize(channel, function (e, size) {
|
// 'channel' refers to a file, so you need anoter API
|
||||||
if (e) { return void cb(e.code); }
|
getUploadSize(null, channel, function (e, size) {
|
||||||
|
if (e) { return void cb(e); }
|
||||||
cb(void 0, size);
|
cb(void 0, size);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -294,10 +347,11 @@ var getHash = function (store, Sessions, publicKey, cb) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var storeMessage = function (store, publicKey, msg, cb) {
|
/* var storeMessage = function (store, publicKey, msg, cb) {
|
||||||
store.message(publicKey, JSON.stringify(msg), cb);
|
store.message(publicKey, JSON.stringify(msg), cb);
|
||||||
};
|
}; */
|
||||||
|
|
||||||
|
// TODO check if new pinned size exceeds user quota
|
||||||
var pinChannel = function (store, Sessions, publicKey, channels, cb) {
|
var pinChannel = function (store, Sessions, publicKey, channels, cb) {
|
||||||
if (!channels && channels.filter) {
|
if (!channels && channels.filter) {
|
||||||
// expected array
|
// expected array
|
||||||
@@ -349,8 +403,7 @@ var unpinChannel = function (store, Sessions, publicKey, channels, cb) {
|
|||||||
function (e) {
|
function (e) {
|
||||||
if (e) { return void cb(e); }
|
if (e) { return void cb(e); }
|
||||||
toStore.forEach(function (channel) {
|
toStore.forEach(function (channel) {
|
||||||
// TODO actually delete
|
delete session.channels[channel];
|
||||||
session.channels[channel] = false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
getHash(store, Sessions, publicKey, cb);
|
getHash(store, Sessions, publicKey, cb);
|
||||||
@@ -358,6 +411,7 @@ var unpinChannel = function (store, Sessions, publicKey, channels, cb) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO check if new pinned size exceeds user quota
|
||||||
var resetUserPins = function (store, Sessions, publicKey, channelList, cb) {
|
var resetUserPins = function (store, Sessions, publicKey, channelList, cb) {
|
||||||
var session = beginSession(Sessions, publicKey);
|
var session = beginSession(Sessions, publicKey);
|
||||||
|
|
||||||
@@ -376,14 +430,191 @@ var resetUserPins = function (store, Sessions, publicKey, channelList, cb) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var getPrivilegedUserList = function (cb) {
|
||||||
|
Fs.readFile('./privileged.conf', 'utf8', function (e, body) {
|
||||||
|
if (e) {
|
||||||
|
if (e.code === 'ENOENT') {
|
||||||
|
return void cb(void 0, []);
|
||||||
|
}
|
||||||
|
return void (e.code);
|
||||||
|
}
|
||||||
|
var list = body.split(/\n/)
|
||||||
|
.map(function (line) {
|
||||||
|
return line.replace(/#.*$/, '').trim();
|
||||||
|
})
|
||||||
|
.filter(function (x) { return x; });
|
||||||
|
cb(void 0, list);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var isPrivilegedUser = function (publicKey, cb) {
|
||||||
|
getPrivilegedUserList(function (e, list) {
|
||||||
|
if (e) { return void cb(false); }
|
||||||
|
cb(list.indexOf(publicKey) !== -1);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var getLimit = function (cb) {
|
||||||
|
cb = cb; // TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
var safeMkdir = function (path, cb) {
|
||||||
|
Fs.mkdir(path, function (e) {
|
||||||
|
if (!e || e.code === 'EEXIST') { return void cb(); }
|
||||||
|
cb(e);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeFilePath = function (root, id) {
|
||||||
|
if (typeof(id) !== 'string' || id.length <= 2) { return null; }
|
||||||
|
return Path.join(root, id.slice(0, 2), id);
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeFileStream = function (root, id, cb) {
|
||||||
|
var stub = id.slice(0, 2);
|
||||||
|
var full = makeFilePath(root, id);
|
||||||
|
safeMkdir(Path.join(root, stub), function (e) {
|
||||||
|
if (e) { return void cb(e); }
|
||||||
|
|
||||||
|
try {
|
||||||
|
var stream = Fs.createWriteStream(full, {
|
||||||
|
flags: 'a',
|
||||||
|
encoding: 'binary',
|
||||||
|
});
|
||||||
|
stream.on('open', function () {
|
||||||
|
cb(void 0, stream);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
cb('BAD_STREAM');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var upload = function (paths, Sessions, publicKey, content, cb) {
|
||||||
|
var dec = new Buffer(Nacl.util.decodeBase64(content)); // jshint ignore:line
|
||||||
|
|
||||||
|
var session = Sessions[publicKey];
|
||||||
|
session.atime = +new Date();
|
||||||
|
if (!session.blobstage) {
|
||||||
|
makeFileStream(paths.staging, publicKey, function (e, stream) {
|
||||||
|
if (e) { return void cb(e); }
|
||||||
|
|
||||||
|
var blobstage = session.blobstage = stream;
|
||||||
|
blobstage.write(dec);
|
||||||
|
cb(void 0, dec.length);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
session.blobstage.write(dec);
|
||||||
|
cb(void 0, dec.length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var upload_cancel = function (paths, Sessions, publicKey, cb) {
|
||||||
|
var path = makeFilePath(paths.staging, publicKey);
|
||||||
|
if (!path) {
|
||||||
|
console.log(paths.staging, publicKey);
|
||||||
|
console.log(path);
|
||||||
|
return void cb('NO_FILE');
|
||||||
|
}
|
||||||
|
|
||||||
|
Fs.unlink(path, function (e) {
|
||||||
|
if (e) { return void cb('E_UNLINK'); }
|
||||||
|
cb(void 0);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var isFile = function (filePath, cb) {
|
||||||
|
Fs.stat(filePath, function (e, stats) {
|
||||||
|
if (e) {
|
||||||
|
if (e.code === 'ENOENT') { return void cb(void 0, false); }
|
||||||
|
return void cb(e.message);
|
||||||
|
}
|
||||||
|
return void cb(void 0, stats.isFile());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
change channel IDs to a different length so that when we pin, we will be able
|
||||||
|
to tell that it is not a channel, but a file, just by its length.
|
||||||
|
|
||||||
|
also, when your upload is complete, pin the resulting file.
|
||||||
|
*/
|
||||||
|
var upload_complete = function (paths, Sessions, publicKey, cb) {
|
||||||
|
var session = Sessions[publicKey];
|
||||||
|
|
||||||
|
if (session.blobstage && session.blobstage.close) {
|
||||||
|
session.blobstage.close();
|
||||||
|
delete session.blobstage;
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldPath = makeFilePath(paths.staging, publicKey);
|
||||||
|
|
||||||
|
var tryRandomLocation = function (cb) {
|
||||||
|
var id = createFileId();
|
||||||
|
var prefix = id.slice(0, 2);
|
||||||
|
var newPath = makeFilePath(paths.blob, id);
|
||||||
|
|
||||||
|
safeMkdir(Path.join(paths.blob, prefix), function (e) {
|
||||||
|
if (e) {
|
||||||
|
console.error(e);
|
||||||
|
return void cb('RENAME_ERR');
|
||||||
|
}
|
||||||
|
isFile(newPath, function (e, yes) {
|
||||||
|
if (e) {
|
||||||
|
console.error(e);
|
||||||
|
return void cb(e);
|
||||||
|
}
|
||||||
|
if (yes) {
|
||||||
|
return void tryRandomLocation(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(void 0, newPath, id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
tryRandomLocation(function (e, newPath, id) {
|
||||||
|
Fs.rename(oldPath, newPath, function (e) {
|
||||||
|
if (e) {
|
||||||
|
console.error(e);
|
||||||
|
return cb(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(void 0, id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
when asking about your upload status, also send some information about how big
|
||||||
|
your upload is going to be. if that would exceed your limit, return TOO_LARGE
|
||||||
|
error.
|
||||||
|
|
||||||
|
*/
|
||||||
|
var upload_status = function (paths, Sessions, publicKey, cb) {
|
||||||
|
var filePath = makeFilePath(paths.staging, publicKey);
|
||||||
|
if (!filePath) { return void cb('E_INVALID_PATH'); }
|
||||||
|
isFile(filePath, function (e, yes) {
|
||||||
|
cb(e, yes);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/*::const ConfigType = require('./config.example.js');*/
|
/*::const ConfigType = require('./config.example.js');*/
|
||||||
RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)=>void*/) {
|
RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)=>void*/) {
|
||||||
// load pin-store...
|
// load pin-store...
|
||||||
|
|
||||||
console.log('loading rpc module...');
|
console.log('loading rpc module...');
|
||||||
|
|
||||||
var Sessions = {};
|
var Sessions = {};
|
||||||
|
|
||||||
|
var keyOrDefaultString = function (key, def) {
|
||||||
|
return typeof(config[key]) === 'string'? config[key]: def;
|
||||||
|
};
|
||||||
|
|
||||||
|
var paths = {};
|
||||||
|
var pinPath = paths.pin = keyOrDefaultString('pinPath', './pins');
|
||||||
|
var blobPath = paths.blob = keyOrDefaultString('blobPath', './blob');
|
||||||
|
var blobStagingPath = paths.staging = keyOrDefaultString('blobStagingPath', './blobstage');
|
||||||
|
|
||||||
var store;
|
var store;
|
||||||
|
|
||||||
var rpc = function (
|
var rpc = function (
|
||||||
@@ -428,7 +659,6 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
|
|||||||
return void respond('INVALID_MESSAGE_OR_PUBLIC_KEY');
|
return void respond('INVALID_MESSAGE_OR_PUBLIC_KEY');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (checkSignature(serialized, signature, publicKey) !== true) {
|
if (checkSignature(serialized, signature, publicKey) !== true) {
|
||||||
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
|
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
|
||||||
}
|
}
|
||||||
@@ -446,20 +676,26 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
|
|||||||
var Respond = function (e, msg) {
|
var Respond = function (e, msg) {
|
||||||
var token = Sessions[publicKey].tokens.slice(-1)[0];
|
var token = Sessions[publicKey].tokens.slice(-1)[0];
|
||||||
var cookie = makeCookie(token).join('|');
|
var cookie = makeCookie(token).join('|');
|
||||||
respond(e, [cookie].concat(msg||[]));
|
respond(e, [cookie].concat(typeof(msg) !== 'undefined' ?msg: []));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof(msg) !== 'object' || !msg.length) {
|
if (typeof(msg) !== 'object' || !msg.length) {
|
||||||
return void Respond('INVALID_MSG');
|
return void Respond('INVALID_MSG');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var deny = function () {
|
||||||
|
Respond('E_ACCESS_DENIED');
|
||||||
|
};
|
||||||
|
|
||||||
|
var handleMessage = function (privileged) {
|
||||||
switch (msg[0]) {
|
switch (msg[0]) {
|
||||||
case 'COOKIE': return void Respond(void 0);
|
case 'COOKIE': return void Respond(void 0);
|
||||||
case 'RESET':
|
case 'RESET':
|
||||||
return resetUserPins(store, Sessions, safeKey, msg[1], function (e, hash) {
|
return resetUserPins(store, Sessions, safeKey, msg[1], function (e, hash) {
|
||||||
return void Respond(e, hash);
|
return void Respond(e, hash);
|
||||||
});
|
});
|
||||||
case 'PIN':
|
case 'PIN': // TODO don't pin if over the limit
|
||||||
|
// if over, send error E_OVER_LIMIT
|
||||||
return pinChannel(store, Sessions, safeKey, msg[1], function (e, hash) {
|
return pinChannel(store, Sessions, safeKey, msg[1], function (e, hash) {
|
||||||
Respond(e, hash);
|
Respond(e, hash);
|
||||||
});
|
});
|
||||||
@@ -471,32 +707,89 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
|
|||||||
return void getHash(store, Sessions, safeKey, function (e, hash) {
|
return void getHash(store, Sessions, safeKey, function (e, hash) {
|
||||||
Respond(e, hash);
|
Respond(e, hash);
|
||||||
});
|
});
|
||||||
case 'GET_TOTAL_SIZE':
|
case 'GET_TOTAL_SIZE': // TODO cache this, since it will get called quite a bit
|
||||||
return getTotalSize(store, ctx.store, Sessions, safeKey, function (e, size) {
|
return getTotalSize(store, ctx.store, Sessions, safeKey, function (e, size) {
|
||||||
if (e) { return void Respond(e); }
|
if (e) { return void Respond(e); }
|
||||||
Respond(e, size);
|
Respond(e, size);
|
||||||
});
|
});
|
||||||
case 'GET_FILE_SIZE':
|
case 'GET_FILE_SIZE':
|
||||||
return void getFileSize(ctx.store, msg[1], Respond);
|
return void getFileSize(ctx.store, msg[1], Respond);
|
||||||
|
case 'GET_LIMIT': // TODO implement this and cache it per-user
|
||||||
|
return void getLimit(function (e, limit) {
|
||||||
|
limit = limit;
|
||||||
|
Respond('NOT_IMPLEMENTED');
|
||||||
|
});
|
||||||
case 'GET_MULTIPLE_FILE_SIZE':
|
case 'GET_MULTIPLE_FILE_SIZE':
|
||||||
return void getMultipleFileSize(ctx.store, msg[1], function (e, dict) {
|
return void getMultipleFileSize(ctx.store, msg[1], function (e, dict) {
|
||||||
if (e) { return void Respond(e); }
|
if (e) { return void Respond(e); }
|
||||||
Respond(void 0, dict);
|
Respond(void 0, dict);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// restricted to privileged users...
|
||||||
|
case 'UPLOAD':
|
||||||
|
if (!privileged) { return deny(); }
|
||||||
|
return void upload(paths, Sessions, safeKey, msg[1], function (e, len) {
|
||||||
|
Respond(e, len);
|
||||||
|
});
|
||||||
|
case 'UPLOAD_STATUS':
|
||||||
|
if (!privileged) { return deny(); }
|
||||||
|
return void upload_status(paths, Sessions, safeKey, function (e, stat) {
|
||||||
|
Respond(e, stat);
|
||||||
|
});
|
||||||
|
case 'UPLOAD_COMPLETE':
|
||||||
|
if (!privileged) { return deny(); }
|
||||||
|
return void upload_complete(paths, Sessions, safeKey, function (e, hash) {
|
||||||
|
Respond(e, hash);
|
||||||
|
});
|
||||||
|
case 'UPLOAD_CANCEL':
|
||||||
|
if (!privileged) { return deny(); }
|
||||||
|
return void upload_cancel(paths, Sessions, safeKey, function (e) {
|
||||||
|
Respond(e);
|
||||||
|
});
|
||||||
default:
|
default:
|
||||||
return void Respond('UNSUPPORTED_RPC_CALL', msg);
|
return void Respond('UNSUPPORTED_RPC_CALL', msg);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// reject uploads unless explicitly enabled
|
||||||
|
if (config.enableUploads !== true) {
|
||||||
|
return void handleMessage(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// restrict upload capability unless explicitly disabled
|
||||||
|
if (config.restrictUploads === false) {
|
||||||
|
return void handleMessage(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if session has not been authenticated, do so
|
||||||
|
var session = Sessions[publicKey];
|
||||||
|
if (typeof(session.privilege) !== 'boolean') {
|
||||||
|
return void isPrivilegedUser(publicKey, function (yes) {
|
||||||
|
session.privilege = yes;
|
||||||
|
handleMessage(yes);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if authenticated, proceed
|
||||||
|
handleMessage(session.privilege);
|
||||||
};
|
};
|
||||||
|
|
||||||
Store.create({
|
Store.create({
|
||||||
filePath: './pins'
|
filePath: pinPath,
|
||||||
}, function (s) {
|
}, function (s) {
|
||||||
store = s;
|
store = s;
|
||||||
cb(void 0, rpc);
|
|
||||||
|
|
||||||
// expire old sessions once per minute
|
safeMkdir(blobPath, function (e) {
|
||||||
setInterval(function () {
|
if (e) { throw e; }
|
||||||
expireSessions(Sessions);
|
safeMkdir(blobStagingPath, function (e) {
|
||||||
}, 60000);
|
if (e) { throw e; }
|
||||||
|
cb(void 0, rpc);
|
||||||
|
// expire old sessions once per minute
|
||||||
|
setInterval(function () {
|
||||||
|
expireSessions(Sessions);
|
||||||
|
}, 60000);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ var mainPages = config.mainPages || ['index', 'privacy', 'terms', 'about', 'cont
|
|||||||
var mainPagePattern = new RegExp('^\/(' + mainPages.join('|') + ').html$');
|
var mainPagePattern = new RegExp('^\/(' + mainPages.join('|') + ').html$');
|
||||||
app.get(mainPagePattern, Express.static(__dirname + '/customize.dist'));
|
app.get(mainPagePattern, Express.static(__dirname + '/customize.dist'));
|
||||||
|
|
||||||
|
app.use("/blob", Express.static(__dirname + '/blob'));
|
||||||
|
|
||||||
app.use("/customize", Express.static(__dirname + '/customize'));
|
app.use("/customize", Express.static(__dirname + '/customize'));
|
||||||
app.use("/customize", Express.static(__dirname + '/customize.dist'));
|
app.use("/customize", Express.static(__dirname + '/customize.dist'));
|
||||||
app.use(/^\/[^\/]*$/, Express.static('customize'));
|
app.use(/^\/[^\/]*$/, Express.static('customize'));
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ var readMessages = function (path, msgHandler, cb) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var checkPath = function (path, callback) {
|
var checkPath = function (path, callback) {
|
||||||
Fs.stat(path, function (err, stats) {
|
// TODO check if we actually need to use stat at all
|
||||||
|
Fs.stat(path, function (err) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
callback(undefined, true);
|
callback(undefined, true);
|
||||||
return;
|
return;
|
||||||
@@ -166,7 +167,7 @@ var getChannel = function (env, id, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}).nThen(function (waitFor) {
|
}).nThen(function () {
|
||||||
if (errorState) { return; }
|
if (errorState) { return; }
|
||||||
complete();
|
complete();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,31 +15,43 @@ define([
|
|||||||
var failMessages = [];
|
var failMessages = [];
|
||||||
|
|
||||||
var ASSERTS = [];
|
var ASSERTS = [];
|
||||||
var runASSERTS = function () {
|
var runASSERTS = function (cb) {
|
||||||
|
var count = ASSERTS.length;
|
||||||
|
var successes = 0;
|
||||||
|
|
||||||
|
var done = function (err) {
|
||||||
|
count--;
|
||||||
|
if (err) { failMessages.push(err); }
|
||||||
|
else { successes++; }
|
||||||
|
if (count === 0) { cb(); }
|
||||||
|
};
|
||||||
|
|
||||||
ASSERTS.forEach(function (f, index) {
|
ASSERTS.forEach(function (f, index) {
|
||||||
f(index);
|
f(function (err) {
|
||||||
|
done(err, index);
|
||||||
|
}, index);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var assert = function (test, msg) {
|
var assert = function (test, msg) {
|
||||||
ASSERTS.push(function (i) {
|
ASSERTS.push(function (cb, i) {
|
||||||
var returned = test();
|
test(function (result) {
|
||||||
if (returned === true) {
|
if (result === true) {
|
||||||
assertions++;
|
assertions++;
|
||||||
} else {
|
cb();
|
||||||
failed = true;
|
} else {
|
||||||
failedOn = assertions;
|
failed = true;
|
||||||
failMessages.push({
|
failedOn = assertions;
|
||||||
test: i,
|
cb({
|
||||||
message: msg,
|
test: i,
|
||||||
output: returned,
|
message: msg,
|
||||||
});
|
output: result,
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var $body = $('body');
|
|
||||||
|
|
||||||
var HJSON_list = [
|
var HJSON_list = [
|
||||||
'["DIV",{"id":"target"},[["P",{"class":" alice bob charlie has.dot","id":"bang"},["pewpewpew"]]]]',
|
'["DIV",{"id":"target"},[["P",{"class":" alice bob charlie has.dot","id":"bang"},["pewpewpew"]]]]',
|
||||||
|
|
||||||
@@ -60,7 +72,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
var HJSON_equal = function (shjson) {
|
var HJSON_equal = function (shjson) {
|
||||||
assert(function () {
|
assert(function (cb) {
|
||||||
// parse your stringified Hyperjson
|
// parse your stringified Hyperjson
|
||||||
var hjson;
|
var hjson;
|
||||||
|
|
||||||
@@ -84,10 +96,10 @@ define([
|
|||||||
var diff = TextPatcher.format(shjson, op);
|
var diff = TextPatcher.format(shjson, op);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
return true;
|
return cb(true);
|
||||||
} else {
|
} else {
|
||||||
return '<br><br>insert: ' + diff.insert + '<br><br>' +
|
return cb('<br><br>insert: ' + diff.insert + '<br><br>' +
|
||||||
'remove: ' + diff.remove + '<br><br>';
|
'remove: ' + diff.remove + '<br><br>');
|
||||||
}
|
}
|
||||||
}, "expected hyperjson equality");
|
}, "expected hyperjson equality");
|
||||||
};
|
};
|
||||||
@@ -96,7 +108,7 @@ define([
|
|||||||
|
|
||||||
var roundTrip = function (sel) {
|
var roundTrip = function (sel) {
|
||||||
var target = $(sel)[0];
|
var target = $(sel)[0];
|
||||||
assert(function () {
|
assert(function (cb) {
|
||||||
var hjson = Hyperjson.fromDOM(target);
|
var hjson = Hyperjson.fromDOM(target);
|
||||||
var cloned = Hyperjson.toDOM(hjson);
|
var cloned = Hyperjson.toDOM(hjson);
|
||||||
var success = cloned.outerHTML === target.outerHTML;
|
var success = cloned.outerHTML === target.outerHTML;
|
||||||
@@ -113,7 +125,7 @@ define([
|
|||||||
TextPatcher.log(target.outerHTML, op);
|
TextPatcher.log(target.outerHTML, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return cb(success);
|
||||||
}, "Round trip serialization introduced artifacts.");
|
}, "Round trip serialization introduced artifacts.");
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -127,9 +139,9 @@ define([
|
|||||||
|
|
||||||
var strungJSON = function (orig) {
|
var strungJSON = function (orig) {
|
||||||
var result;
|
var result;
|
||||||
assert(function () {
|
assert(function (cb) {
|
||||||
result = JSON.stringify(JSON.parse(orig));
|
result = JSON.stringify(JSON.parse(orig));
|
||||||
return result === orig;
|
return cb(result === orig);
|
||||||
}, "expected result (" + result + ") to equal original (" + orig + ")");
|
}, "expected result (" + result + ") to equal original (" + orig + ")");
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -139,6 +151,59 @@ define([
|
|||||||
strungJSON(orig);
|
strungJSON(orig);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// check that old hashes parse correctly
|
||||||
|
assert(function (cb) {
|
||||||
|
var secret = Cryptpad.parseHash('67b8385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyHci7LNy');
|
||||||
|
return cb(secret.channel === "67b8385b07352be53e40746d2be6ccd7" &&
|
||||||
|
secret.key === "XAYSuJYYqa9NfmInyHci7LNy" &&
|
||||||
|
secret.version === 0);
|
||||||
|
}, "Old hash failed to parse");
|
||||||
|
|
||||||
|
// make sure version 1 hashes parse correctly
|
||||||
|
assert(function (cb) {
|
||||||
|
var secret = Cryptpad.parseHash('/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI');
|
||||||
|
return cb(secret.version === 1 &&
|
||||||
|
secret.mode === "edit" &&
|
||||||
|
secret.channel === "3Ujt4F2Sjnjbis6CoYWpoQ" &&
|
||||||
|
secret.key === "usn4+9CqVja8Q7RZOGTfRgqI" &&
|
||||||
|
!secret.present);
|
||||||
|
}, "version 1 hash failed to parse");
|
||||||
|
|
||||||
|
// test support for present mode in hashes
|
||||||
|
assert(function (cb) {
|
||||||
|
var secret = Cryptpad.parseHash('/1/edit/CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G/present');
|
||||||
|
return cb(secret.version === 1
|
||||||
|
&& secret.mode === "edit"
|
||||||
|
&& secret.channel === "CmN5+YJkrHFS3NSBg-P7Sg"
|
||||||
|
&& secret.key === "DNZ2wcG683GscU4fyOyqA87G"
|
||||||
|
&& secret.present);
|
||||||
|
}, "version 1 hash failed to parse");
|
||||||
|
|
||||||
|
// test support for present mode in hashes
|
||||||
|
assert(function (cb) {
|
||||||
|
var secret = Cryptpad.parseHash('/1/edit//CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G//present');
|
||||||
|
return cb(secret.version === 1
|
||||||
|
&& secret.mode === "edit"
|
||||||
|
&& secret.channel === "CmN5+YJkrHFS3NSBg-P7Sg"
|
||||||
|
&& secret.key === "DNZ2wcG683GscU4fyOyqA87G"
|
||||||
|
&& secret.present);
|
||||||
|
}, "Couldn't handle multiple successive slashes");
|
||||||
|
|
||||||
|
// test support for trailing slash
|
||||||
|
assert(function (cb) {
|
||||||
|
var secret = Cryptpad.parseHash('/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI/');
|
||||||
|
return cb(secret.version === 1 &&
|
||||||
|
secret.mode === "edit" &&
|
||||||
|
secret.channel === "3Ujt4F2Sjnjbis6CoYWpoQ" &&
|
||||||
|
secret.key === "usn4+9CqVja8Q7RZOGTfRgqI" &&
|
||||||
|
!secret.present);
|
||||||
|
}, "test support for trailing slashes in version 1 hash failed to parse");
|
||||||
|
|
||||||
|
assert(function (cb) {
|
||||||
|
// TODO
|
||||||
|
return cb(true);
|
||||||
|
}, "version 2 hash failed to parse correctly");
|
||||||
|
|
||||||
var swap = function (str, dict) {
|
var swap = function (str, dict) {
|
||||||
return str.replace(/\{\{(.*?)\}\}/g, function (all, key) {
|
return str.replace(/\{\{(.*?)\}\}/g, function (all, key) {
|
||||||
return typeof dict[key] !== 'undefined'? dict[key] : all;
|
return typeof dict[key] !== 'undefined'? dict[key] : all;
|
||||||
@@ -153,7 +218,7 @@ define([
|
|||||||
return str || '';
|
return str || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
var formatFailures = function () {
|
var formatFailures = function () {
|
||||||
var template = multiline(function () { /*
|
var template = multiline(function () { /*
|
||||||
<p class="error">
|
<p class="error">
|
||||||
Failed on test number {{test}} with error message:
|
Failed on test number {{test}} with error message:
|
||||||
@@ -174,16 +239,15 @@ The test returned:
|
|||||||
}).join("\n");
|
}).join("\n");
|
||||||
};
|
};
|
||||||
|
|
||||||
runASSERTS();
|
runASSERTS(function () {
|
||||||
|
$("body").html(function (i, val) {
|
||||||
$("body").html(function (i, val) {
|
var dict = {
|
||||||
var dict = {
|
previous: val,
|
||||||
previous: val,
|
totalAssertions: ASSERTS.length,
|
||||||
totalAssertions: ASSERTS.length,
|
passedAssertions: assertions,
|
||||||
passedAssertions: assertions,
|
plural: (assertions === 1? '' : 's'),
|
||||||
plural: (assertions === 1? '' : 's'),
|
failMessages: formatFailures()
|
||||||
failMessages: formatFailures()
|
};
|
||||||
};
|
|
||||||
|
|
||||||
var SUCCESS = swap(multiline(function(){/*
|
var SUCCESS = swap(multiline(function(){/*
|
||||||
<div class="report">{{passedAssertions}} / {{totalAssertions}} test{{plural}} passed.
|
<div class="report">{{passedAssertions}} / {{totalAssertions}} test{{plural}} passed.
|
||||||
@@ -196,12 +260,13 @@ The test returned:
|
|||||||
{{previous}}
|
{{previous}}
|
||||||
*/}), dict);
|
*/}), dict);
|
||||||
|
|
||||||
var report = SUCCESS;
|
var report = SUCCESS;
|
||||||
|
|
||||||
return report;
|
return report;
|
||||||
|
});
|
||||||
|
|
||||||
|
var $report = $('.report');
|
||||||
|
$report.addClass(failed?'failure':'success');
|
||||||
});
|
});
|
||||||
|
|
||||||
var $report = $('.report');
|
|
||||||
$report.addClass(failed?'failure':'success');
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
588
www/code/main.js
588
www/code/main.js
@@ -3,18 +3,12 @@ define([
|
|||||||
'/bower_components/chainpad-crypto/crypto.js',
|
'/bower_components/chainpad-crypto/crypto.js',
|
||||||
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
||||||
'/bower_components/textpatcher/TextPatcher.js',
|
'/bower_components/textpatcher/TextPatcher.js',
|
||||||
'/common/toolbar.js',
|
'/common/toolbar2.js',
|
||||||
'json.sortify',
|
'json.sortify',
|
||||||
'/bower_components/chainpad-json-validator/json-ot.js',
|
'/bower_components/chainpad-json-validator/json-ot.js',
|
||||||
'/common/cryptpad-common.js',
|
'/common/cryptpad-common.js',
|
||||||
'/common/cryptget.js',
|
'/common/cryptget.js',
|
||||||
'/common/modes.js',
|
], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Cryptget) {
|
||||||
'/common/themes.js',
|
|
||||||
'/common/visible.js',
|
|
||||||
'/common/notify.js',
|
|
||||||
'/bower_components/file-saver/FileSaver.min.js'
|
|
||||||
], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Cryptget, Modes, Themes, Visible, Notify) {
|
|
||||||
var saveAs = window.saveAs;
|
|
||||||
var Messages = Cryptpad.Messages;
|
var Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
var module = window.APP = {
|
var module = window.APP = {
|
||||||
@@ -30,6 +24,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
var toolbar;
|
var toolbar;
|
||||||
|
var editor;
|
||||||
|
|
||||||
var secret = Cryptpad.getSecrets();
|
var secret = Cryptpad.getSecrets();
|
||||||
var readOnly = secret.keys && !secret.keys.editKeyStr;
|
var readOnly = secret.keys && !secret.keys.editKeyStr;
|
||||||
@@ -37,117 +32,26 @@ define([
|
|||||||
secret.keys = secret.key;
|
secret.keys = secret.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
var onConnectError = function (info) {
|
var onConnectError = function () {
|
||||||
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
||||||
};
|
};
|
||||||
|
|
||||||
var andThen = function (CMeditor) {
|
var andThen = function (CMeditor) {
|
||||||
var CodeMirror = module.CodeMirror = CMeditor;
|
var CodeMirror = Cryptpad.createCodemirror(CMeditor, ifrw, Cryptpad);
|
||||||
CodeMirror.modeURL = "/bower_components/codemirror/mode/%N/%N.js";
|
editor = CodeMirror.editor;
|
||||||
var $pad = $('#pad-iframe');
|
|
||||||
var $textarea = $pad.contents().find('#editor1');
|
|
||||||
|
|
||||||
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
|
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
|
||||||
var parsedHash = Cryptpad.parsePadUrl(window.location.href);
|
|
||||||
var defaultName = Cryptpad.getDefaultName(parsedHash);
|
|
||||||
var initialState = Messages.codeInitialState;
|
|
||||||
|
|
||||||
var isHistoryMode = false;
|
var isHistoryMode = false;
|
||||||
|
|
||||||
var editor = module.editor = CMeditor.fromTextArea($textarea[0], {
|
|
||||||
lineNumbers: true,
|
|
||||||
lineWrapping: true,
|
|
||||||
autoCloseBrackets: true,
|
|
||||||
matchBrackets : true,
|
|
||||||
showTrailingSpace : true,
|
|
||||||
styleActiveLine : true,
|
|
||||||
search: true,
|
|
||||||
highlightSelectionMatches: {showToken: /\w+/},
|
|
||||||
extraKeys: {"Shift-Ctrl-R": undefined},
|
|
||||||
foldGutter: true,
|
|
||||||
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
|
|
||||||
mode: "javascript",
|
|
||||||
readOnly: true
|
|
||||||
});
|
|
||||||
editor.setValue(Messages.codeInitialState);
|
|
||||||
|
|
||||||
var setMode = module.setMode = function (mode, $select) {
|
|
||||||
module.highlightMode = mode;
|
|
||||||
if (mode === 'text') {
|
|
||||||
editor.setOption('mode', 'text');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CodeMirror.autoLoadMode(editor, mode);
|
|
||||||
editor.setOption('mode', mode);
|
|
||||||
if ($select) {
|
|
||||||
var name = $select.find('a[data-value="' + mode + '"]').text() || 'Mode';
|
|
||||||
$select.setValue(name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var setTheme = module.setTheme = (function () {
|
|
||||||
var path = '/common/theme/';
|
|
||||||
|
|
||||||
var $head = $(ifrw.document.head);
|
|
||||||
|
|
||||||
var themeLoaded = module.themeLoaded = function (theme) {
|
|
||||||
return $head.find('link[href*="'+theme+'"]').length;
|
|
||||||
};
|
|
||||||
|
|
||||||
var loadTheme = module.loadTheme = function (theme) {
|
|
||||||
$head.append($('<link />', {
|
|
||||||
rel: 'stylesheet',
|
|
||||||
href: path + theme + '.css',
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return function (theme, $select) {
|
|
||||||
if (!theme) {
|
|
||||||
editor.setOption('theme', 'default');
|
|
||||||
} else {
|
|
||||||
if (!themeLoaded(theme)) {
|
|
||||||
loadTheme(theme);
|
|
||||||
}
|
|
||||||
editor.setOption('theme', theme);
|
|
||||||
}
|
|
||||||
if ($select) {
|
|
||||||
$select.setValue(theme || 'Theme');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}());
|
|
||||||
|
|
||||||
var setEditable = module.setEditable = function (bool) {
|
var setEditable = module.setEditable = function (bool) {
|
||||||
if (readOnly && bool) { return; }
|
if (readOnly && bool) { return; }
|
||||||
editor.setOption('readOnly', !bool);
|
editor.setOption('readOnly', !bool);
|
||||||
};
|
};
|
||||||
|
|
||||||
var userData = module.userData = {}; // List of pretty name of all users (mapped with their server ID)
|
var Title;
|
||||||
var userList; // List of users still connected to the channel (server IDs)
|
var UserList;
|
||||||
var addToUserData = function(data) {
|
var Metadata;
|
||||||
var users = module.users;
|
|
||||||
for (var attrname in data) { userData[attrname] = data[attrname]; }
|
|
||||||
|
|
||||||
if (users && users.length) {
|
|
||||||
for (var userKey in userData) {
|
|
||||||
if (users.indexOf(userKey) === -1) {
|
|
||||||
delete userData[userKey];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(userList && typeof userList.onChange === "function") {
|
|
||||||
userList.onChange(userData);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var myData = {};
|
|
||||||
var myUserName = ''; // My "pretty name"
|
|
||||||
var myID; // My server ID
|
|
||||||
|
|
||||||
var setMyID = function(info) {
|
|
||||||
myID = info.myID || null;
|
|
||||||
myUserName = myID;
|
|
||||||
};
|
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
initialState: '{}',
|
initialState: '{}',
|
||||||
@@ -157,7 +61,6 @@ define([
|
|||||||
validateKey: secret.keys.validateKey || undefined,
|
validateKey: secret.keys.validateKey || undefined,
|
||||||
readOnly: readOnly,
|
readOnly: readOnly,
|
||||||
crypto: Crypto.createEncryptor(secret.keys),
|
crypto: Crypto.createEncryptor(secret.keys),
|
||||||
setMyID: setMyID,
|
|
||||||
network: Cryptpad.getNetwork(),
|
network: Cryptpad.getNetwork(),
|
||||||
transformFunction: JsonOT.validate,
|
transformFunction: JsonOT.validate,
|
||||||
};
|
};
|
||||||
@@ -172,26 +75,21 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var isDefaultTitle = function () {
|
|
||||||
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
|
||||||
return Cryptpad.isDefaultName(parsed, document.title);
|
|
||||||
};
|
|
||||||
|
|
||||||
var initializing = true;
|
var initializing = true;
|
||||||
|
|
||||||
var stringifyInner = function (textValue) {
|
var stringifyInner = function (textValue) {
|
||||||
var obj = {
|
var obj = {
|
||||||
content: textValue,
|
content: textValue,
|
||||||
metadata: {
|
metadata: {
|
||||||
users: userData,
|
users: UserList.userData,
|
||||||
defaultTitle: defaultName
|
defaultTitle: Title.defaultTitle
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (!initializing) {
|
if (!initializing) {
|
||||||
obj.metadata.title = document.title;
|
obj.metadata.title = Title.title;
|
||||||
}
|
}
|
||||||
// set mode too...
|
// set mode too...
|
||||||
obj.highlightMode = module.highlightMode;
|
obj.highlightMode = CodeMirror.highlightMode;
|
||||||
|
|
||||||
// stringify the json and send it into chainpad
|
// stringify the json and send it into chainpad
|
||||||
return stringify(obj);
|
return stringify(obj);
|
||||||
@@ -204,7 +102,7 @@ define([
|
|||||||
|
|
||||||
editor.save();
|
editor.save();
|
||||||
|
|
||||||
var textValue = canonicalize($textarea.val());
|
var textValue = canonicalize(CodeMirror.$textarea.val());
|
||||||
var shjson = stringifyInner(textValue);
|
var shjson = stringifyInner(textValue);
|
||||||
|
|
||||||
module.patchText(shjson);
|
module.patchText(shjson);
|
||||||
@@ -214,231 +112,55 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var setName = module.setName = function (newName) {
|
|
||||||
if (typeof(newName) !== 'string') { return; }
|
|
||||||
var myUserNameTemp = newName.trim();
|
|
||||||
if(newName.trim().length > 32) {
|
|
||||||
myUserNameTemp = myUserNameTemp.substr(0, 32);
|
|
||||||
}
|
|
||||||
myUserName = myUserNameTemp;
|
|
||||||
myData[myID] = {
|
|
||||||
name: myUserName,
|
|
||||||
uid: Cryptpad.getUid(),
|
|
||||||
};
|
|
||||||
addToUserData(myData);
|
|
||||||
Cryptpad.setAttribute('username', myUserName, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.log("Couldn't set username");
|
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onLocal();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var getHeadingText = function () {
|
|
||||||
var lines = editor.getValue().split(/\n/);
|
|
||||||
|
|
||||||
var text = '';
|
config.onInit = function (info) {
|
||||||
lines.some(function (line) {
|
UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad);
|
||||||
// lisps?
|
|
||||||
var lispy = /^\s*(;|#\|)(.*?)$/;
|
|
||||||
if (lispy.test(line)) {
|
|
||||||
line.replace(lispy, function (a, one, two) {
|
|
||||||
text = two;
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// lines beginning with a hash are potentially valuable
|
var titleCfg = { getHeadingText: CodeMirror.getHeadingText };
|
||||||
// works for markdown, python, bash, etc.
|
Title = Cryptpad.createTitle(titleCfg, config.onLocal, Cryptpad);
|
||||||
var hash = /^#(.*?)$/;
|
|
||||||
if (hash.test(line)) {
|
|
||||||
line.replace(hash, function (a, one) {
|
|
||||||
text = one;
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// lines including a c-style comment are also valuable
|
Metadata = Cryptpad.createMetadata(UserList, Title);
|
||||||
var clike = /^\s*(\/\*|\/\/)(.*)?(\*\/)*$/;
|
|
||||||
if (clike.test(line)) {
|
|
||||||
line.replace(clike, function (a, one, two) {
|
|
||||||
if (!(two && two.replace)) { return; }
|
|
||||||
text = two.replace(/\*\/\s*$/, '').trim();
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO make one more pass for multiline comments
|
|
||||||
});
|
|
||||||
|
|
||||||
return text.trim();
|
|
||||||
};
|
|
||||||
|
|
||||||
var suggestName = function (fallback) {
|
|
||||||
if (document.title === defaultName) {
|
|
||||||
return getHeadingText() || fallback || "";
|
|
||||||
} else {
|
|
||||||
return document.title || getHeadingText() || defaultName;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var exportText = module.exportText = function () {
|
|
||||||
var text = editor.getValue();
|
|
||||||
|
|
||||||
var ext = Modes.extensionOf(module.highlightMode);
|
|
||||||
|
|
||||||
var title = Cryptpad.fixFileName(suggestName('cryptpad')) + (ext || '.txt');
|
|
||||||
|
|
||||||
Cryptpad.prompt(Messages.exportPrompt, title, function (filename) {
|
|
||||||
if (filename === null) { return; }
|
|
||||||
var blob = new Blob([text], {
|
|
||||||
type: 'text/plain;charset=utf-8'
|
|
||||||
});
|
|
||||||
saveAs(blob, filename);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
var importText = function (content, file) {
|
|
||||||
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
|
|
||||||
var mode;
|
|
||||||
var mime = CodeMirror.findModeByMIME(file.type);
|
|
||||||
|
|
||||||
if (!mime) {
|
|
||||||
var ext = /.+\.([^.]+)$/.exec(file.name);
|
|
||||||
if (ext[1]) {
|
|
||||||
mode = CodeMirror.findModeByExtension(ext[1]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mode = mime && mime.mode || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode && Modes.list.some(function (o) { return o.mode === mode; })) {
|
|
||||||
setMode(mode);
|
|
||||||
$bar.find('#language-mode').val(mode);
|
|
||||||
} else {
|
|
||||||
console.log("Couldn't find a suitable highlighting mode: %s", mode);
|
|
||||||
setMode('text');
|
|
||||||
$bar.find('#language-mode').val('text');
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.setValue(content);
|
|
||||||
onLocal();
|
|
||||||
};
|
|
||||||
|
|
||||||
var renameCb = function (err, title) {
|
|
||||||
if (err) { return; }
|
|
||||||
document.title = title;
|
|
||||||
onLocal();
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateTitle = function (newTitle) {
|
|
||||||
if (newTitle === document.title) { return; }
|
|
||||||
// Change the title now, and set it back to the old value if there is an error
|
|
||||||
var oldTitle = document.title;
|
|
||||||
document.title = newTitle;
|
|
||||||
Cryptpad.renamePad(newTitle, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.log("Couldn't set pad title");
|
|
||||||
console.error(err);
|
|
||||||
document.title = oldTitle;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
document.title = data;
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('span.title').text(data);
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('input').val(data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateDefaultTitle = function (defaultTitle) {
|
|
||||||
defaultName = defaultTitle;
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('input').attr("placeholder", defaultName);
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateMetadata = function(shjson) {
|
|
||||||
// Extract the user list (metadata) from the hyperjson
|
|
||||||
var json = (shjson === "") ? "" : JSON.parse(shjson);
|
|
||||||
var titleUpdated = false;
|
|
||||||
if (json && json.metadata) {
|
|
||||||
if (json.metadata.users) {
|
|
||||||
var userData = json.metadata.users;
|
|
||||||
// Update the local user data
|
|
||||||
addToUserData(userData);
|
|
||||||
}
|
|
||||||
if (json.metadata.defaultTitle) {
|
|
||||||
updateDefaultTitle(json.metadata.defaultTitle);
|
|
||||||
}
|
|
||||||
if (typeof json.metadata.title !== "undefined") {
|
|
||||||
updateTitle(json.metadata.title || defaultName);
|
|
||||||
titleUpdated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!titleUpdated) {
|
|
||||||
updateTitle(defaultName);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var onInit = config.onInit = function (info) {
|
|
||||||
userList = info.userList;
|
|
||||||
|
|
||||||
var configTb = {
|
var configTb = {
|
||||||
displayed: ['useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad'],
|
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'],
|
||||||
userData: userData,
|
userList: UserList.getToolbarConfig(),
|
||||||
readOnly: readOnly,
|
|
||||||
ifrw: ifrw,
|
|
||||||
share: {
|
share: {
|
||||||
secret: secret,
|
secret: secret,
|
||||||
channel: info.channel
|
channel: info.channel
|
||||||
},
|
},
|
||||||
title: {
|
title: Title.getTitleConfig(),
|
||||||
onRename: renameCb,
|
common: Cryptpad,
|
||||||
defaultName: defaultName,
|
readOnly: readOnly,
|
||||||
suggestName: suggestName
|
ifrw: ifrw,
|
||||||
},
|
realtime: info.realtime,
|
||||||
common: Cryptpad
|
network: info.network,
|
||||||
|
$container: $bar
|
||||||
};
|
};
|
||||||
toolbar = module.toolbar = Toolbar.create($bar, info.myID, info.realtime, info.getLag, userList, configTb);
|
toolbar = module.toolbar = Toolbar.create(configTb);
|
||||||
|
|
||||||
var $rightside = $bar.find('.' + Toolbar.constants.rightside);
|
Title.setToolbar(toolbar);
|
||||||
var $userBlock = $bar.find('.' + Toolbar.constants.username);
|
CodeMirror.init(config.onLocal, Title, toolbar);
|
||||||
var $usernameButton = module.$userNameButton = $($bar.find('.' + Toolbar.constants.changeUsername));
|
|
||||||
|
var $rightside = toolbar.$rightside;
|
||||||
|
|
||||||
var editHash;
|
var editHash;
|
||||||
var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys);
|
|
||||||
|
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add a history button */
|
/* add a history button */
|
||||||
var histConfig = {};
|
var histConfig = {
|
||||||
histConfig.onRender = function (val) {
|
onLocal: config.onLocal(),
|
||||||
if (typeof val === "undefined") { return; }
|
onRemote: config.onRemote(),
|
||||||
try {
|
setHistory: setHistory,
|
||||||
var hjson = JSON.parse(val || '{}');
|
applyVal: function (val) {
|
||||||
var remoteDoc = hjson.content;
|
var remoteDoc = JSON.parse(val || '{}').content;
|
||||||
editor.setValue(remoteDoc || '');
|
editor.setValue(remoteDoc || '');
|
||||||
editor.save();
|
editor.save();
|
||||||
} catch (e) {
|
},
|
||||||
// Probably a parse error
|
$toolbar: $bar
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
histConfig.onClose = function () {
|
|
||||||
// Close button clicked
|
|
||||||
setHistory(false, true);
|
|
||||||
};
|
|
||||||
histConfig.onRevert = function () {
|
|
||||||
// Revert button clicked
|
|
||||||
setHistory(false, false);
|
|
||||||
config.onLocal();
|
|
||||||
config.onRemote();
|
|
||||||
};
|
|
||||||
histConfig.onReady = function () {
|
|
||||||
// Called when the history is loaded and the UI displayed
|
|
||||||
setHistory(true);
|
|
||||||
};
|
|
||||||
histConfig.$toolbar = $bar;
|
|
||||||
var $hist = Cryptpad.createButton('history', true, {histConfig: histConfig});
|
var $hist = Cryptpad.createButton('history', true, {histConfig: histConfig});
|
||||||
$rightside.append($hist);
|
$rightside.append($hist);
|
||||||
|
|
||||||
@@ -447,133 +169,42 @@ define([
|
|||||||
var templateObj = {
|
var templateObj = {
|
||||||
rt: info.realtime,
|
rt: info.realtime,
|
||||||
Crypt: Cryptget,
|
Crypt: Cryptget,
|
||||||
getTitle: function () { return document.title; }
|
getTitle: Title.getTitle
|
||||||
};
|
};
|
||||||
var $templateButton = Cryptpad.createButton('template', true, templateObj);
|
var $templateButton = Cryptpad.createButton('template', true, templateObj);
|
||||||
$rightside.append($templateButton);
|
$rightside.append($templateButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add an export button */
|
/* add an export button */
|
||||||
var $export = Cryptpad.createButton('export', true, {}, exportText);
|
var $export = Cryptpad.createButton('export', true, {}, CodeMirror.exportText);
|
||||||
$rightside.append($export);
|
$rightside.append($export);
|
||||||
|
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
/* add an import button */
|
/* add an import button */
|
||||||
var $import = Cryptpad.createButton('import', true, {}, importText);
|
var $import = Cryptpad.createButton('import', true, {}, CodeMirror.importText);
|
||||||
$rightside.append($import);
|
$rightside.append($import);
|
||||||
|
|
||||||
/* add a rename button */
|
|
||||||
//var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb);
|
|
||||||
//$rightside.append($setTitle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add a forget button */
|
/* add a forget button */
|
||||||
var forgetCb = function (err, title) {
|
var forgetCb = function (err) {
|
||||||
if (err) { return; }
|
if (err) { return; }
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
};
|
};
|
||||||
var $forgetPad = Cryptpad.createButton('forget', true, {}, forgetCb);
|
var $forgetPad = Cryptpad.createButton('forget', true, {}, forgetCb);
|
||||||
$rightside.append($forgetPad);
|
$rightside.append($forgetPad);
|
||||||
|
|
||||||
var configureLanguage = function (cb) {
|
|
||||||
// FIXME this is async so make it happen as early as possible
|
|
||||||
var options = [];
|
|
||||||
Modes.list.forEach(function (l) {
|
|
||||||
options.push({
|
|
||||||
tag: 'a',
|
|
||||||
attributes: {
|
|
||||||
'data-value': l.mode,
|
|
||||||
'href': '#',
|
|
||||||
},
|
|
||||||
content: l.language // Pretty name of the language value
|
|
||||||
});
|
|
||||||
});
|
|
||||||
var dropdownConfig = {
|
|
||||||
text: 'Mode', // Button initial text
|
|
||||||
options: options, // Entries displayed in the menu
|
|
||||||
left: true, // Open to the left of the button
|
|
||||||
isSelect: true,
|
|
||||||
};
|
|
||||||
var $block = module.$language = Cryptpad.createDropdown(dropdownConfig);
|
|
||||||
var $button = $block.find('.buttonTitle');
|
|
||||||
|
|
||||||
$block.find('a').click(function (e) {
|
|
||||||
setMode($(this).attr('data-value'), $block);
|
|
||||||
onLocal();
|
|
||||||
});
|
|
||||||
|
|
||||||
$rightside.append($block);
|
|
||||||
cb();
|
|
||||||
};
|
|
||||||
|
|
||||||
var configureTheme = function () {
|
|
||||||
/* Remember the user's last choice of theme using localStorage */
|
|
||||||
var themeKey = 'CRYPTPAD_CODE_THEME';
|
|
||||||
var lastTheme = localStorage.getItem(themeKey) || 'default';
|
|
||||||
|
|
||||||
var options = [];
|
|
||||||
Themes.forEach(function (l) {
|
|
||||||
options.push({
|
|
||||||
tag: 'a',
|
|
||||||
attributes: {
|
|
||||||
'data-value': l.name,
|
|
||||||
'href': '#',
|
|
||||||
},
|
|
||||||
content: l.name // Pretty name of the language value
|
|
||||||
});
|
|
||||||
});
|
|
||||||
var dropdownConfig = {
|
|
||||||
text: 'Theme', // Button initial text
|
|
||||||
options: options, // Entries displayed in the menu
|
|
||||||
left: true, // Open to the left of the button
|
|
||||||
isSelect: true,
|
|
||||||
initialValue: lastTheme
|
|
||||||
};
|
|
||||||
var $block = module.$theme = Cryptpad.createDropdown(dropdownConfig);
|
|
||||||
var $button = $block.find('.buttonTitle');
|
|
||||||
|
|
||||||
setTheme(lastTheme, $block);
|
|
||||||
|
|
||||||
$block.find('a').click(function (e) {
|
|
||||||
var theme = $(this).attr('data-value');
|
|
||||||
setTheme(theme, $block);
|
|
||||||
localStorage.setItem(themeKey, theme);
|
|
||||||
});
|
|
||||||
|
|
||||||
$rightside.append($block);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
configureLanguage(function () {
|
CodeMirror.configureLanguage(CodeMirror.configureTheme);
|
||||||
configureTheme();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
configureTheme();
|
CodeMirror.configureTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the hash
|
// set the hash
|
||||||
if (!readOnly) { Cryptpad.replaceHash(editHash); }
|
if (!readOnly) { Cryptpad.replaceHash(editHash); }
|
||||||
|
|
||||||
Cryptpad.onDisplayNameChanged(setName);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var unnotify = module.unnotify = function () {
|
config.onReady = function (info) {
|
||||||
if (module.tabNotification &&
|
|
||||||
typeof(module.tabNotification.cancel) === 'function') {
|
|
||||||
module.tabNotification.cancel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var notify = module.notify = function () {
|
|
||||||
if (Visible.isSupported() && !Visible.currently()) {
|
|
||||||
unnotify();
|
|
||||||
module.tabNotification = Notify.tab(1000, 10);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var onReady = config.onReady = function (info) {
|
|
||||||
module.users = info.userList.users;
|
|
||||||
if (module.realtime !== info.realtime) {
|
if (module.realtime !== info.realtime) {
|
||||||
var realtime = module.realtime = info.realtime;
|
var realtime = module.realtime = info.realtime;
|
||||||
module.patchText = TextPatcher.create({
|
module.patchText = TextPatcher.create({
|
||||||
@@ -600,135 +231,58 @@ define([
|
|||||||
newDoc = hjson.content;
|
newDoc = hjson.content;
|
||||||
|
|
||||||
if (hjson.highlightMode) {
|
if (hjson.highlightMode) {
|
||||||
setMode(hjson.highlightMode, module.$language);
|
CodeMirror.setMode(hjson.highlightMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!module.highlightMode) {
|
if (!CodeMirror.highlightMode) {
|
||||||
setMode('javascript', module.$language);
|
CodeMirror.setMode('javascript');
|
||||||
console.log("%s => %s", module.highlightMode, module.$language.val());
|
console.log("%s => %s", CodeMirror.highlightMode, CodeMirror.$language.val());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the user list (metadata) from the hyperjson
|
// Update the user list (metadata) from the hyperjson
|
||||||
updateMetadata(userDoc);
|
Metadata.update(userDoc);
|
||||||
|
|
||||||
if (newDoc) {
|
if (newDoc) {
|
||||||
editor.setValue(newDoc);
|
editor.setValue(newDoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Cryptpad.initialName && document.title === defaultName) {
|
if (Cryptpad.initialName && Title.isDefaultTitle()) {
|
||||||
updateTitle(Cryptpad.initialName);
|
Title.updateTitle(Cryptpad.initialName);
|
||||||
onLocal();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Visible.isSupported()) {
|
|
||||||
Visible.onChange(function (yes) {
|
|
||||||
if (yes) { unnotify(); }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cryptpad.removeLoadingScreen();
|
Cryptpad.removeLoadingScreen();
|
||||||
setEditable(true);
|
setEditable(true);
|
||||||
initializing = false;
|
initializing = false;
|
||||||
//Cryptpad.log("Your document is ready");
|
|
||||||
|
|
||||||
onLocal(); // push local state to avoid parse errors later.
|
onLocal(); // push local state to avoid parse errors later.
|
||||||
Cryptpad.getLastName(function (err, lastName) {
|
|
||||||
if (err) {
|
if (readOnly) { return; }
|
||||||
console.log("Could not get previous name");
|
UserList.getLastName(toolbar.$userNameButton, isNew);
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Update the toolbar list:
|
|
||||||
// Add the current user in the metadata if he has edit rights
|
|
||||||
if (readOnly) { return; }
|
|
||||||
if (typeof(lastName) === 'string') {
|
|
||||||
setName(lastName);
|
|
||||||
} else {
|
|
||||||
myData[myID] = {
|
|
||||||
name: "",
|
|
||||||
uid: Cryptpad.getUid(),
|
|
||||||
};
|
|
||||||
addToUserData(myData);
|
|
||||||
onLocal();
|
|
||||||
module.$userNameButton.click();
|
|
||||||
}
|
|
||||||
if (isNew) {
|
|
||||||
Cryptpad.selectTemplate('code', info.realtime, Cryptget);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var cursorToPos = function(cursor, oldText) {
|
config.onRemote = function () {
|
||||||
var cLine = cursor.line;
|
|
||||||
var cCh = cursor.ch;
|
|
||||||
var pos = 0;
|
|
||||||
var textLines = oldText.split("\n");
|
|
||||||
for (var line = 0; line <= cLine; line++) {
|
|
||||||
if(line < cLine) {
|
|
||||||
pos += textLines[line].length+1;
|
|
||||||
}
|
|
||||||
else if(line === cLine) {
|
|
||||||
pos += cCh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
};
|
|
||||||
|
|
||||||
var posToCursor = function(position, newText) {
|
|
||||||
var cursor = {
|
|
||||||
line: 0,
|
|
||||||
ch: 0
|
|
||||||
};
|
|
||||||
var textLines = newText.substr(0, position).split("\n");
|
|
||||||
cursor.line = textLines.length - 1;
|
|
||||||
cursor.ch = textLines[cursor.line].length;
|
|
||||||
return cursor;
|
|
||||||
};
|
|
||||||
|
|
||||||
var onRemote = config.onRemote = function () {
|
|
||||||
if (initializing) { return; }
|
if (initializing) { return; }
|
||||||
if (isHistoryMode) { return; }
|
if (isHistoryMode) { return; }
|
||||||
var scroll = editor.getScrollInfo();
|
|
||||||
|
|
||||||
var oldDoc = canonicalize($textarea.val());
|
var oldDoc = canonicalize(CodeMirror.$textarea.val());
|
||||||
var shjson = module.realtime.getUserDoc();
|
var shjson = module.realtime.getUserDoc();
|
||||||
|
|
||||||
// Update the user list (metadata) from the hyperjson
|
// Update the user list (metadata) from the hyperjson
|
||||||
updateMetadata(shjson);
|
Metadata.update(shjson);
|
||||||
|
|
||||||
var hjson = JSON.parse(shjson);
|
var hjson = JSON.parse(shjson);
|
||||||
var remoteDoc = hjson.content;
|
var remoteDoc = hjson.content;
|
||||||
|
|
||||||
var highlightMode = hjson.highlightMode;
|
var highlightMode = hjson.highlightMode;
|
||||||
if (highlightMode && highlightMode !== module.highlightMode) {
|
if (highlightMode && highlightMode !== module.highlightMode) {
|
||||||
setMode(highlightMode, module.$language);
|
CodeMirror.setMode(highlightMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
//get old cursor here
|
CodeMirror.setValueAndCursor(oldDoc, remoteDoc, TextPatcher);
|
||||||
var oldCursor = {};
|
|
||||||
oldCursor.selectionStart = cursorToPos(editor.getCursor('from'), oldDoc);
|
|
||||||
oldCursor.selectionEnd = cursorToPos(editor.getCursor('to'), oldDoc);
|
|
||||||
|
|
||||||
editor.setValue(remoteDoc);
|
|
||||||
editor.save();
|
|
||||||
|
|
||||||
var op = TextPatcher.diff(oldDoc, remoteDoc);
|
|
||||||
var selects = ['selectionStart', 'selectionEnd'].map(function (attr) {
|
|
||||||
return TextPatcher.transformCursor(oldCursor[attr], op);
|
|
||||||
});
|
|
||||||
|
|
||||||
if(selects[0] === selects[1]) {
|
|
||||||
editor.setCursor(posToCursor(selects[0], remoteDoc));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
editor.setSelection(posToCursor(selects[0], remoteDoc), posToCursor(selects[1], remoteDoc));
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.scrollTo(scroll.left, scroll.top);
|
|
||||||
|
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
var textValue = canonicalize($textarea.val());
|
var textValue = canonicalize(CodeMirror.$textarea.val());
|
||||||
var shjson2 = stringifyInner(textValue);
|
var shjson2 = stringifyInner(textValue);
|
||||||
if (shjson2 !== shjson) {
|
if (shjson2 !== shjson) {
|
||||||
console.error("shjson2 !== shjson");
|
console.error("shjson2 !== shjson");
|
||||||
@@ -736,19 +290,17 @@ define([
|
|||||||
module.patchText(shjson2);
|
module.patchText(shjson2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (oldDoc !== remoteDoc) {
|
if (oldDoc !== remoteDoc) { Cryptpad.notify(); }
|
||||||
notify();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var onAbort = config.onAbort = function (info) {
|
config.onAbort = function () {
|
||||||
// inform of network disconnect
|
// inform of network disconnect
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
toolbar.failed();
|
toolbar.failed();
|
||||||
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
var onConnectionChange = config.onConnectionChange = function (info) {
|
config.onConnectionChange = function (info) {
|
||||||
setEditable(info.state);
|
setEditable(info.state);
|
||||||
toolbar.failed();
|
toolbar.failed();
|
||||||
if (info.state) {
|
if (info.state) {
|
||||||
@@ -760,9 +312,9 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var onError = config.onError = onConnectError;
|
config.onError = onConnectError;
|
||||||
|
|
||||||
var realtime = module.realtime = Realtime.start(config);
|
module.realtime = Realtime.start(config);
|
||||||
|
|
||||||
editor.on('change', onLocal);
|
editor.on('change', onLocal);
|
||||||
|
|
||||||
@@ -772,7 +324,7 @@ define([
|
|||||||
var interval = 100;
|
var interval = 100;
|
||||||
|
|
||||||
var second = function (CM) {
|
var second = function (CM) {
|
||||||
Cryptpad.ready(function (err, env) {
|
Cryptpad.ready(function () {
|
||||||
andThen(CM);
|
andThen(CM);
|
||||||
Cryptpad.reportAppUsage();
|
Cryptpad.reportAppUsage();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ define(['jquery'], function ($) {
|
|||||||
|
|
||||||
// copy arbitrary text to the clipboard
|
// copy arbitrary text to the clipboard
|
||||||
// return boolean indicating success
|
// return boolean indicating success
|
||||||
var copy = Clipboard.copy = function (text) {
|
Clipboard.copy = function (text) {
|
||||||
var $ta = $('<input>', {
|
var $ta = $('<input>', {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
}).val(text);
|
}).val(text);
|
||||||
|
|||||||
299
www/common/common-codemirror.js
Normal file
299
www/common/common-codemirror.js
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
define([
|
||||||
|
'jquery',
|
||||||
|
'/common/modes.js',
|
||||||
|
'/common/themes.js',
|
||||||
|
'/bower_components/file-saver/FileSaver.min.js'
|
||||||
|
], function ($, Modes, Themes) {
|
||||||
|
var saveAs = window.saveAs;
|
||||||
|
var module = {};
|
||||||
|
|
||||||
|
module.create = function (CMeditor, ifrw, Cryptpad) {
|
||||||
|
var exp = {};
|
||||||
|
|
||||||
|
var Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
|
var CodeMirror = exp.CodeMirror = CMeditor;
|
||||||
|
CodeMirror.modeURL = "/bower_components/codemirror/mode/%N/%N.js";
|
||||||
|
|
||||||
|
var $pad = $('#pad-iframe');
|
||||||
|
var $textarea = exp.$textarea = $pad.contents().find('#editor1');
|
||||||
|
|
||||||
|
var Title;
|
||||||
|
var onLocal = function () {};
|
||||||
|
var $rightside;
|
||||||
|
exp.init = function (local, title, toolbar) {
|
||||||
|
if (typeof local === "function") {
|
||||||
|
onLocal = local;
|
||||||
|
}
|
||||||
|
Title = title;
|
||||||
|
$rightside = toolbar.$rightside;
|
||||||
|
};
|
||||||
|
|
||||||
|
var editor = exp.editor = CMeditor.fromTextArea($textarea[0], {
|
||||||
|
lineNumbers: true,
|
||||||
|
lineWrapping: true,
|
||||||
|
autoCloseBrackets: true,
|
||||||
|
matchBrackets : true,
|
||||||
|
showTrailingSpace : true,
|
||||||
|
styleActiveLine : true,
|
||||||
|
search: true,
|
||||||
|
highlightSelectionMatches: {showToken: /\w+/},
|
||||||
|
extraKeys: {"Shift-Ctrl-R": undefined},
|
||||||
|
foldGutter: true,
|
||||||
|
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
|
||||||
|
mode: "javascript",
|
||||||
|
readOnly: true
|
||||||
|
});
|
||||||
|
editor.setValue(Messages.codeInitialState);
|
||||||
|
|
||||||
|
var setMode = exp.setMode = function (mode) {
|
||||||
|
exp.highlightMode = mode;
|
||||||
|
if (mode === 'text') {
|
||||||
|
editor.setOption('mode', 'text');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CMeditor.autoLoadMode(editor, mode);
|
||||||
|
editor.setOption('mode', mode);
|
||||||
|
if (exp.$language) {
|
||||||
|
var name = exp.$language.find('a[data-value="' + mode + '"]').text() || 'Mode';
|
||||||
|
exp.$language.setValue(name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var setTheme = exp.setTheme = (function () {
|
||||||
|
var path = '/common/theme/';
|
||||||
|
|
||||||
|
var $head = $(ifrw.document.head);
|
||||||
|
|
||||||
|
var themeLoaded = exp.themeLoaded = function (theme) {
|
||||||
|
return $head.find('link[href*="'+theme+'"]').length;
|
||||||
|
};
|
||||||
|
|
||||||
|
var loadTheme = exp.loadTheme = function (theme) {
|
||||||
|
$head.append($('<link />', {
|
||||||
|
rel: 'stylesheet',
|
||||||
|
href: path + theme + '.css',
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return function (theme, $select) {
|
||||||
|
if (!theme) {
|
||||||
|
editor.setOption('theme', 'default');
|
||||||
|
} else {
|
||||||
|
if (!themeLoaded(theme)) {
|
||||||
|
loadTheme(theme);
|
||||||
|
}
|
||||||
|
editor.setOption('theme', theme);
|
||||||
|
}
|
||||||
|
if ($select) {
|
||||||
|
$select.setValue(theme || 'Theme');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}());
|
||||||
|
|
||||||
|
exp.getHeadingText = function () {
|
||||||
|
var lines = editor.getValue().split(/\n/);
|
||||||
|
|
||||||
|
var text = '';
|
||||||
|
lines.some(function (line) {
|
||||||
|
// lisps?
|
||||||
|
var lispy = /^\s*(;|#\|)(.*?)$/;
|
||||||
|
if (lispy.test(line)) {
|
||||||
|
line.replace(lispy, function (a, one, two) {
|
||||||
|
text = two;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lines beginning with a hash are potentially valuable
|
||||||
|
// works for markdown, python, bash, etc.
|
||||||
|
var hash = /^#(.*?)$/;
|
||||||
|
if (hash.test(line)) {
|
||||||
|
line.replace(hash, function (a, one) {
|
||||||
|
text = one;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lines including a c-style comment are also valuable
|
||||||
|
var clike = /^\s*(\/\*|\/\/)(.*)?(\*\/)*$/;
|
||||||
|
if (clike.test(line)) {
|
||||||
|
line.replace(clike, function (a, one, two) {
|
||||||
|
if (!(two && two.replace)) { return; }
|
||||||
|
text = two.replace(/\*\/\s*$/, '').trim();
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO make one more pass for multiline comments
|
||||||
|
});
|
||||||
|
|
||||||
|
return text.trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
exp.configureLanguage = function (cb) {
|
||||||
|
var options = [];
|
||||||
|
Modes.list.forEach(function (l) {
|
||||||
|
options.push({
|
||||||
|
tag: 'a',
|
||||||
|
attributes: {
|
||||||
|
'data-value': l.mode,
|
||||||
|
'href': '#',
|
||||||
|
},
|
||||||
|
content: l.language // Pretty name of the language value
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var dropdownConfig = {
|
||||||
|
text: 'Mode', // Button initial text
|
||||||
|
options: options, // Entries displayed in the menu
|
||||||
|
left: true, // Open to the left of the button
|
||||||
|
isSelect: true,
|
||||||
|
};
|
||||||
|
console.log('here');
|
||||||
|
var $block = exp.$language = Cryptpad.createDropdown(dropdownConfig);
|
||||||
|
console.log(exp);
|
||||||
|
$block.find('a').click(function () {
|
||||||
|
setMode($(this).attr('data-value'), $block);
|
||||||
|
onLocal();
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($rightside) { $rightside.append($block); }
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
exp.configureTheme = function () {
|
||||||
|
/* Remember the user's last choice of theme using localStorage */
|
||||||
|
var themeKey = 'CRYPTPAD_CODE_THEME';
|
||||||
|
var lastTheme = localStorage.getItem(themeKey) || 'default';
|
||||||
|
|
||||||
|
var options = [];
|
||||||
|
Themes.forEach(function (l) {
|
||||||
|
options.push({
|
||||||
|
tag: 'a',
|
||||||
|
attributes: {
|
||||||
|
'data-value': l.name,
|
||||||
|
'href': '#',
|
||||||
|
},
|
||||||
|
content: l.name // Pretty name of the language value
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var dropdownConfig = {
|
||||||
|
text: 'Theme', // Button initial text
|
||||||
|
options: options, // Entries displayed in the menu
|
||||||
|
left: true, // Open to the left of the button
|
||||||
|
isSelect: true,
|
||||||
|
initialValue: lastTheme
|
||||||
|
};
|
||||||
|
var $block = exp.$theme = Cryptpad.createDropdown(dropdownConfig);
|
||||||
|
|
||||||
|
setTheme(lastTheme, $block);
|
||||||
|
|
||||||
|
$block.find('a').click(function () {
|
||||||
|
var theme = $(this).attr('data-value');
|
||||||
|
setTheme(theme, $block);
|
||||||
|
localStorage.setItem(themeKey, theme);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($rightside) { $rightside.append($block); }
|
||||||
|
};
|
||||||
|
|
||||||
|
exp.exportText = function () {
|
||||||
|
var text = editor.getValue();
|
||||||
|
|
||||||
|
var ext = Modes.extensionOf(exp.highlightMode);
|
||||||
|
|
||||||
|
var title = Cryptpad.fixFileName(Title ? Title.suggestTitle('cryptpad') : "?") + (ext || '.txt');
|
||||||
|
|
||||||
|
Cryptpad.prompt(Messages.exportPrompt, title, function (filename) {
|
||||||
|
if (filename === null) { return; }
|
||||||
|
var blob = new Blob([text], {
|
||||||
|
type: 'text/plain;charset=utf-8'
|
||||||
|
});
|
||||||
|
saveAs(blob, filename);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
exp.importText = function (content, file) {
|
||||||
|
var $bar = ifrw.$('#cme_toolbox');
|
||||||
|
var mode;
|
||||||
|
var mime = CodeMirror.findModeByMIME(file.type);
|
||||||
|
|
||||||
|
if (!mime) {
|
||||||
|
var ext = /.+\.([^.]+)$/.exec(file.name);
|
||||||
|
if (ext[1]) {
|
||||||
|
mode = CMeditor.findModeByExtension(ext[1]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mode = mime && mime.mode || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode && Modes.list.some(function (o) { return o.mode === mode; })) {
|
||||||
|
setMode(mode);
|
||||||
|
$bar.find('#language-mode').val(mode);
|
||||||
|
} else {
|
||||||
|
console.log("Couldn't find a suitable highlighting mode: %s", mode);
|
||||||
|
setMode('text');
|
||||||
|
$bar.find('#language-mode').val('text');
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.setValue(content);
|
||||||
|
onLocal();
|
||||||
|
};
|
||||||
|
|
||||||
|
var cursorToPos = function(cursor, oldText) {
|
||||||
|
var cLine = cursor.line;
|
||||||
|
var cCh = cursor.ch;
|
||||||
|
var pos = 0;
|
||||||
|
var textLines = oldText.split("\n");
|
||||||
|
for (var line = 0; line <= cLine; line++) {
|
||||||
|
if(line < cLine) {
|
||||||
|
pos += textLines[line].length+1;
|
||||||
|
}
|
||||||
|
else if(line === cLine) {
|
||||||
|
pos += cCh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
var posToCursor = function(position, newText) {
|
||||||
|
var cursor = {
|
||||||
|
line: 0,
|
||||||
|
ch: 0
|
||||||
|
};
|
||||||
|
var textLines = newText.substr(0, position).split("\n");
|
||||||
|
cursor.line = textLines.length - 1;
|
||||||
|
cursor.ch = textLines[cursor.line].length;
|
||||||
|
return cursor;
|
||||||
|
};
|
||||||
|
|
||||||
|
exp.setValueAndCursor = function (oldDoc, remoteDoc, TextPatcher) {
|
||||||
|
var scroll = editor.getScrollInfo();
|
||||||
|
//get old cursor here
|
||||||
|
var oldCursor = {};
|
||||||
|
oldCursor.selectionStart = cursorToPos(editor.getCursor('from'), oldDoc);
|
||||||
|
oldCursor.selectionEnd = cursorToPos(editor.getCursor('to'), oldDoc);
|
||||||
|
|
||||||
|
editor.setValue(remoteDoc);
|
||||||
|
editor.save();
|
||||||
|
|
||||||
|
var op = TextPatcher.diff(oldDoc, remoteDoc);
|
||||||
|
var selects = ['selectionStart', 'selectionEnd'].map(function (attr) {
|
||||||
|
return TextPatcher.transformCursor(oldCursor[attr], op);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(selects[0] === selects[1]) {
|
||||||
|
editor.setCursor(posToCursor(selects[0], remoteDoc));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
editor.setSelection(posToCursor(selects[0], remoteDoc), posToCursor(selects[1], remoteDoc));
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.scrollTo(scroll.left, scroll.top);
|
||||||
|
};
|
||||||
|
|
||||||
|
return exp;
|
||||||
|
};
|
||||||
|
|
||||||
|
return module;
|
||||||
|
});
|
||||||
|
|
||||||
@@ -23,13 +23,16 @@ define([
|
|||||||
return chanKey + keys;
|
return chanKey + keys;
|
||||||
}
|
}
|
||||||
if (!keys.editKeyStr) { return; }
|
if (!keys.editKeyStr) { return; }
|
||||||
return '/1/edit/' + hexToBase64(chanKey) + '/' + Crypto.b64RemoveSlashes(keys.editKeyStr);
|
return '/1/edit/' + hexToBase64(chanKey) + '/'+Crypto.b64RemoveSlashes(keys.editKeyStr)+'/';
|
||||||
};
|
};
|
||||||
var getViewHashFromKeys = Hash.getViewHashFromKeys = function (chanKey, keys) {
|
var getViewHashFromKeys = Hash.getViewHashFromKeys = function (chanKey, keys) {
|
||||||
if (typeof keys === 'string') {
|
if (typeof keys === 'string') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return '/1/view/' + hexToBase64(chanKey) + '/' + Crypto.b64RemoveSlashes(keys.viewKeyStr);
|
return '/1/view/' + hexToBase64(chanKey) + '/'+Crypto.b64RemoveSlashes(keys.viewKeyStr)+'/';
|
||||||
|
};
|
||||||
|
var getFileHashFromKeys = Hash.getFileHashFromKeys = function (fileKey, cryptKey) {
|
||||||
|
return '/2/' + hexToBase64(fileKey) + '/' + Crypto.b64RemoveSlashes(cryptKey) + '/';
|
||||||
};
|
};
|
||||||
|
|
||||||
var parsePadUrl = Hash.parsePadUrl = function (href) {
|
var parsePadUrl = Hash.parsePadUrl = function (href) {
|
||||||
@@ -38,6 +41,7 @@ define([
|
|||||||
var ret = {};
|
var ret = {};
|
||||||
|
|
||||||
if (!href) { return ret; }
|
if (!href) { return ret; }
|
||||||
|
if (href.slice(-1) !== '/') { href += '/'; }
|
||||||
|
|
||||||
if (!/^https*:\/\//.test(href)) {
|
if (!/^https*:\/\//.test(href)) {
|
||||||
var idx = href.indexOf('/#');
|
var idx = href.indexOf('/#');
|
||||||
@@ -46,7 +50,7 @@ define([
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hash = href.replace(patt, function (a, domain, type, hash) {
|
var hash = href.replace(patt, function (a, domain, type) {
|
||||||
ret.domain = domain;
|
ret.domain = domain;
|
||||||
ret.type = type;
|
ret.type = type;
|
||||||
return '';
|
return '';
|
||||||
@@ -62,12 +66,16 @@ define([
|
|||||||
return '/' + parsed.type + '/#' + parsed.hash;
|
return '/' + parsed.type + '/#' + parsed.hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var fixDuplicateSlashes = function (s) {
|
||||||
|
return s.replace(/\/+/g, '/');
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns all needed keys for a realtime channel
|
* Returns all needed keys for a realtime channel
|
||||||
* - no argument: use the URL hash or create one if it doesn't exist
|
* - no argument: use the URL hash or create one if it doesn't exist
|
||||||
* - secretHash provided: use secretHash to find the keys
|
* - secretHash provided: use secretHash to find the keys
|
||||||
*/
|
*/
|
||||||
var getSecrets = Hash.getSecrets = function (secretHash) {
|
Hash.getSecrets = function (secretHash) {
|
||||||
var secret = {};
|
var secret = {};
|
||||||
var generate = function () {
|
var generate = function () {
|
||||||
secret.keys = Crypto.createEditCryptor();
|
secret.keys = Crypto.createEditCryptor();
|
||||||
@@ -91,7 +99,7 @@ define([
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// New hash
|
// New hash
|
||||||
var hashArray = hash.split('/');
|
var hashArray = fixDuplicateSlashes(hash).split('/');
|
||||||
if (hashArray.length < 4) {
|
if (hashArray.length < 4) {
|
||||||
Hash.alert("Unable to parse the key");
|
Hash.alert("Unable to parse the key");
|
||||||
throw new Error("Unable to parse the key");
|
throw new Error("Unable to parse the key");
|
||||||
@@ -119,14 +127,15 @@ define([
|
|||||||
}
|
}
|
||||||
} else if (version === "2") {
|
} else if (version === "2") {
|
||||||
// version 2 hashes are to be used for encrypted blobs
|
// version 2 hashes are to be used for encrypted blobs
|
||||||
// TODO
|
secret.channel = hashArray[2].replace(/-/g, '/');
|
||||||
|
secret.keys = { fileKeyStr: hashArray[3].replace(/-/g, '/') };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return secret;
|
return secret;
|
||||||
};
|
};
|
||||||
|
|
||||||
var getHashes = Hash.getHashes = function (channel, secret) {
|
Hash.getHashes = function (channel, secret) {
|
||||||
var hashes = {};
|
var hashes = {};
|
||||||
if (secret.keys.editKeyStr) {
|
if (secret.keys.editKeyStr) {
|
||||||
hashes.editHash = getEditHashFromKeys(channel, secret.keys);
|
hashes.editHash = getEditHashFromKeys(channel, secret.keys);
|
||||||
@@ -134,6 +143,9 @@ define([
|
|||||||
if (secret.keys.viewKeyStr) {
|
if (secret.keys.viewKeyStr) {
|
||||||
hashes.viewHash = getViewHashFromKeys(channel, secret.keys);
|
hashes.viewHash = getViewHashFromKeys(channel, secret.keys);
|
||||||
}
|
}
|
||||||
|
if (secret.keys.fileKeyStr) {
|
||||||
|
hashes.fileHash = getFileHashFromKeys(channel, secret.keys.fileKeyStr);
|
||||||
|
}
|
||||||
return hashes;
|
return hashes;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -145,12 +157,12 @@ define([
|
|||||||
return id;
|
return id;
|
||||||
};
|
};
|
||||||
|
|
||||||
var createRandomHash = Hash.createRandomHash = function () {
|
Hash.createRandomHash = function () {
|
||||||
// 16 byte channel Id
|
// 16 byte channel Id
|
||||||
var channelId = Util.hexToBase64(createChannelId());
|
var channelId = Util.hexToBase64(createChannelId());
|
||||||
// 18 byte encryption key
|
// 18 byte encryption key
|
||||||
var key = Crypto.b64RemoveSlashes(Crypto.rand64(18));
|
var key = Crypto.b64RemoveSlashes(Crypto.rand64(18));
|
||||||
return '/1/edit/' + [channelId, key].join('/');
|
return '/1/edit/' + [channelId, key].join('/') + '/';
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -159,8 +171,8 @@ Version 0
|
|||||||
Version 1
|
Version 1
|
||||||
/code/#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI
|
/code/#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI
|
||||||
Version 2
|
Version 2
|
||||||
/file/<fileId>/#/2/<cryptKey>/<contentType>
|
/file/#/2/<fileId>/<cryptKey>/<contentType>
|
||||||
/file/<fileId>/#/2/ajExFODrFH4lVBwxxsrOKw/pdf
|
/file/#/2/K6xWU-LT9BJHCQcDCT-DcQ/ajExFODrFH4lVBwxxsrOKw/image-png
|
||||||
*/
|
*/
|
||||||
var parseHash = Hash.parseHash = function (hash) {
|
var parseHash = Hash.parseHash = function (hash) {
|
||||||
var parsed = {};
|
var parsed = {};
|
||||||
@@ -171,20 +183,26 @@ Version 2
|
|||||||
parsed.version = 0;
|
parsed.version = 0;
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
var hashArr = hash.split('/');
|
var hashArr = fixDuplicateSlashes(hash).split('/');
|
||||||
if (hashArr[1] && hashArr[1] === '1') {
|
if (hashArr[1] && hashArr[1] === '1') {
|
||||||
parsed.version = 1;
|
parsed.version = 1;
|
||||||
parsed.mode = hashArr[2];
|
parsed.mode = hashArr[2];
|
||||||
parsed.channel = hashArr[3];
|
parsed.channel = hashArr[3];
|
||||||
parsed.key = hashArr[4];
|
parsed.key = hashArr[4];
|
||||||
parsed.present = hashArr[5] && hashArr[5] === 'present';
|
parsed.present = typeof(hashArr[5]) === "string" && hashArr[5] === 'present';
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
if (hashArr[1] && hashArr[1] === '2') {
|
||||||
|
parsed.version = 2;
|
||||||
|
parsed.channel = hashArr[2].replace(/-/g, '/');
|
||||||
|
parsed.key = hashArr[3].replace(/-/g, '/');
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// STORAGE
|
// STORAGE
|
||||||
var findWeaker = Hash.findWeaker = function (href, recents) {
|
Hash.findWeaker = function (href, recents) {
|
||||||
var rHref = href || getRelativeHref(window.location.href);
|
var rHref = href || getRelativeHref(window.location.href);
|
||||||
var parsed = parsePadUrl(rHref);
|
var parsed = parsePadUrl(rHref);
|
||||||
if (!parsed.hash) { return false; }
|
if (!parsed.hash) { return false; }
|
||||||
@@ -228,11 +246,11 @@ Version 2
|
|||||||
});
|
});
|
||||||
return stronger;
|
return stronger;
|
||||||
};
|
};
|
||||||
var isNotStrongestStored = Hash.isNotStrongestStored = function (href, recents) {
|
Hash.isNotStrongestStored = function (href, recents) {
|
||||||
return findStronger(href, recents);
|
return findStronger(href, recents);
|
||||||
};
|
};
|
||||||
|
|
||||||
var hrefToHexChannelId = Hash.hrefToHexChannelId = function (href) {
|
Hash.hrefToHexChannelId = function (href) {
|
||||||
var parsed = Hash.parsePadUrl(href);
|
var parsed = Hash.parsePadUrl(href);
|
||||||
if (!parsed || !parsed.hash) { return; }
|
if (!parsed || !parsed.hash) { return; }
|
||||||
|
|
||||||
@@ -240,7 +258,7 @@ Version 2
|
|||||||
|
|
||||||
if (parsed.version === 0) {
|
if (parsed.version === 0) {
|
||||||
return parsed.channel;
|
return parsed.channel;
|
||||||
} else if (parsed.version !== 1) {
|
} else if (parsed.version !== 1 && parsed.version !== 2) {
|
||||||
console.error("parsed href had no version");
|
console.error("parsed href had no version");
|
||||||
console.error(parsed);
|
console.error(parsed);
|
||||||
return;
|
return;
|
||||||
@@ -253,5 +271,14 @@ Version 2
|
|||||||
return hex;
|
return hex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Hash.getBlobPathFromHex = function (id) {
|
||||||
|
return '/blob/' + id.slice(0,2) + '/' + id;
|
||||||
|
};
|
||||||
|
|
||||||
|
Hash.serializeHash = function (hash) {
|
||||||
|
if (hash && hash.slice(-1) !== "/") { hash += "/"; }
|
||||||
|
return hash;
|
||||||
|
};
|
||||||
|
|
||||||
return Hash;
|
return Hash;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ define([
|
|||||||
return states;
|
return states;
|
||||||
};
|
};
|
||||||
|
|
||||||
var loadHistory = function (common, cb) {
|
var loadHistory = function (config, common, cb) {
|
||||||
var network = common.getNetwork();
|
var network = common.getNetwork();
|
||||||
var hkn = network.historyKeeper;
|
var hkn = network.historyKeeper;
|
||||||
|
|
||||||
var wcId = common.hrefToHexChannelId(window.location.href);
|
var wcId = common.hrefToHexChannelId(config.href || window.location.href);
|
||||||
|
|
||||||
var createRealtime = function(chan) {
|
var createRealtime = function () {
|
||||||
return ChainPad.create({
|
return ChainPad.create({
|
||||||
userName: 'history',
|
userName: 'history',
|
||||||
initialState: '',
|
initialState: '',
|
||||||
@@ -35,7 +35,8 @@ define([
|
|||||||
};
|
};
|
||||||
var realtime = createRealtime();
|
var realtime = createRealtime();
|
||||||
|
|
||||||
var secret = common.getSecrets();
|
var hash = config.href ? common.parsePadUrl(config.href).hash : undefined;
|
||||||
|
var secret = common.getSecrets(hash);
|
||||||
var crypto = Crypto.createEncryptor(secret.keys);
|
var crypto = Crypto.createEncryptor(secret.keys);
|
||||||
|
|
||||||
var to = window.setTimeout(function () {
|
var to = window.setTimeout(function () {
|
||||||
@@ -66,21 +67,44 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
network.on('message', function (msg, sender) {
|
network.on('message', function (msg) {
|
||||||
onMsg(msg);
|
onMsg(msg);
|
||||||
});
|
});
|
||||||
|
|
||||||
network.sendto(hkn, JSON.stringify(['GET_FULL_HISTORY', wcId, secret.keys.validateKey]));
|
network.sendto(hkn, JSON.stringify(['GET_FULL_HISTORY', wcId, secret.keys.validateKey]));
|
||||||
};
|
};
|
||||||
|
|
||||||
var create = History.create = function (common, config) {
|
History.create = function (common, config) {
|
||||||
if (!config.$toolbar) { return void console.error("config.$toolbar is undefined");}
|
if (!config.$toolbar) { return void console.error("config.$toolbar is undefined");}
|
||||||
|
if (History.loading) { return void console.error("History is already being loaded..."); }
|
||||||
|
History.loading = true;
|
||||||
var $toolbar = config.$toolbar;
|
var $toolbar = config.$toolbar;
|
||||||
var noFunc = function () {};
|
|
||||||
var render = config.onRender || noFunc;
|
if (!config.applyVal || !config.setHistory || !config.onLocal || !config.onRemote) {
|
||||||
var onClose = config.onClose || noFunc;
|
throw new Error("Missing config element: applyVal, onLocal, onRemote, setHistory");
|
||||||
var onRevert = config.onRevert || noFunc;
|
}
|
||||||
var onReady = config.onReady || noFunc;
|
|
||||||
|
// config.setHistory(bool, bool)
|
||||||
|
// - bool1: history value
|
||||||
|
// - bool2: reset old content?
|
||||||
|
var render = function (val) {
|
||||||
|
if (typeof val === "undefined") { return; }
|
||||||
|
try {
|
||||||
|
config.applyVal(val);
|
||||||
|
} catch (e) {
|
||||||
|
// Probably a parse error
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var onClose = function () { config.setHistory(false, true); };
|
||||||
|
var onRevert = function () {
|
||||||
|
config.setHistory(false, false);
|
||||||
|
config.onLocal();
|
||||||
|
config.onRemote();
|
||||||
|
};
|
||||||
|
var onReady = function () {
|
||||||
|
config.setHistory(true);
|
||||||
|
};
|
||||||
|
|
||||||
var Messages = common.Messages;
|
var Messages = common.Messages;
|
||||||
|
|
||||||
@@ -112,9 +136,9 @@ define([
|
|||||||
var val = states[i].getContent().doc;
|
var val = states[i].getContent().doc;
|
||||||
c = i;
|
c = i;
|
||||||
if (typeof onUpdate === "function") { onUpdate(); }
|
if (typeof onUpdate === "function") { onUpdate(); }
|
||||||
$hist.find('.next, .previous').show();
|
$hist.find('.next, .previous').css('visibility', '');
|
||||||
if (c === states.length - 1) { $hist.find('.next').hide(); }
|
if (c === states.length - 1) { $hist.find('.next').css('visibility', 'hidden'); }
|
||||||
if (c === 0) { $hist.find('.previous').hide(); }
|
if (c === 0) { $hist.find('.previous').css('visibility', 'hidden'); }
|
||||||
return val || '';
|
return val || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -132,15 +156,16 @@ define([
|
|||||||
$right.hide();
|
$right.hide();
|
||||||
$cke.hide();
|
$cke.hide();
|
||||||
var $prev =$('<button>', {
|
var $prev =$('<button>', {
|
||||||
'class': 'previous fa fa-step-backward',
|
'class': 'previous fa fa-step-backward buttonPrimary',
|
||||||
title: Messages.history_prev
|
title: Messages.history_prev
|
||||||
}).appendTo($hist);
|
}).appendTo($hist);
|
||||||
|
var $nav = $('<div>', {'class': 'goto'}).appendTo($hist);
|
||||||
var $next = $('<button>', {
|
var $next = $('<button>', {
|
||||||
'class': 'next fa fa-step-forward',
|
'class': 'next fa fa-step-forward buttonPrimary',
|
||||||
title: Messages.history_next
|
title: Messages.history_next
|
||||||
}).appendTo($hist);
|
}).appendTo($hist);
|
||||||
|
|
||||||
var $nav = $('<div>', {'class': 'goto'}).appendTo($hist);
|
$('<label>').text(Messages.history_version).appendTo($nav);
|
||||||
var $cur = $('<input>', {
|
var $cur = $('<input>', {
|
||||||
'class' : 'gotoInput',
|
'class' : 'gotoInput',
|
||||||
'type' : 'number',
|
'type' : 'number',
|
||||||
@@ -150,25 +175,21 @@ define([
|
|||||||
// stopPropagation because the event would be cancelled by the dropdown menus
|
// stopPropagation because the event would be cancelled by the dropdown menus
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
});
|
});
|
||||||
var $label = $('<label>').text(' / '+ states.length).appendTo($nav);
|
var $label2 = $('<label>').text(' / '+ states.length).appendTo($nav);
|
||||||
var $goTo = $('<button>', {
|
|
||||||
'class': 'fa fa-check',
|
|
||||||
'title': Messages.history_goTo
|
|
||||||
}).appendTo($nav);
|
|
||||||
$('<br>').appendTo($nav);
|
$('<br>').appendTo($nav);
|
||||||
var $rev = $('<button>', {
|
|
||||||
'class':'revertHistory',
|
|
||||||
title: Messages.history_restoreTitle
|
|
||||||
}).text(Messages.history_restore).appendTo($nav);
|
|
||||||
var $close = $('<button>', {
|
var $close = $('<button>', {
|
||||||
'class':'closeHistory',
|
'class':'closeHistory',
|
||||||
title: Messages.history_closeTitle
|
title: Messages.history_closeTitle
|
||||||
}).text(Messages.history_close).appendTo($nav);
|
}).text(Messages.history_close).appendTo($nav);
|
||||||
|
var $rev = $('<button>', {
|
||||||
|
'class':'revertHistory buttonSuccess',
|
||||||
|
title: Messages.history_restoreTitle
|
||||||
|
}).text(Messages.history_restore).appendTo($nav);
|
||||||
|
|
||||||
onUpdate = function () {
|
onUpdate = function () {
|
||||||
$cur.attr('max', states.length);
|
$cur.attr('max', states.length);
|
||||||
$cur.val(c+1);
|
$cur.val(c+1);
|
||||||
$label.text(' / ' + states.length);
|
$label2.text(' / ' + states.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
var close = function () {
|
var close = function () {
|
||||||
@@ -181,7 +202,6 @@ define([
|
|||||||
// Buttons actions
|
// Buttons actions
|
||||||
$prev.click(function () { render(getPrevious()); });
|
$prev.click(function () { render(getPrevious()); });
|
||||||
$next.click(function () { render(getNext()); });
|
$next.click(function () { render(getNext()); });
|
||||||
$goTo.click(function () { render( get($cur.val() - 1) ); });
|
|
||||||
$cur.keydown(function (e) {
|
$cur.keydown(function (e) {
|
||||||
var p = function () { e.preventDefault(); };
|
var p = function () { e.preventDefault(); };
|
||||||
if (e.which === 13) { p(); return render( get($cur.val() - 1) ); } // Enter
|
if (e.which === 13) { p(); return render( get($cur.val() - 1) ); } // Enter
|
||||||
@@ -192,7 +212,7 @@ define([
|
|||||||
if (e.which === 27) { p(); $close.click(); }
|
if (e.which === 27) { p(); $close.click(); }
|
||||||
}).focus();
|
}).focus();
|
||||||
$cur.on('change', function () {
|
$cur.on('change', function () {
|
||||||
$goTo.click();
|
render( get($cur.val() - 1) );
|
||||||
});
|
});
|
||||||
$close.click(function () {
|
$close.click(function () {
|
||||||
states = [];
|
states = [];
|
||||||
@@ -213,7 +233,8 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Load all the history messages into a new chainpad object
|
// Load all the history messages into a new chainpad object
|
||||||
loadHistory(common, function (err, newRt) {
|
loadHistory(config, common, function (err, newRt) {
|
||||||
|
History.loading = false;
|
||||||
if (err) { throw new Error(err); }
|
if (err) { throw new Error(err); }
|
||||||
realtime = newRt;
|
realtime = newRt;
|
||||||
update();
|
update();
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ define([
|
|||||||
'/customize/messages.js',
|
'/customize/messages.js',
|
||||||
'/common/common-util.js',
|
'/common/common-util.js',
|
||||||
'/customize/application_config.js',
|
'/customize/application_config.js',
|
||||||
'/bower_components/alertifyjs/dist/js/alertify.js'
|
'/bower_components/alertifyjs/dist/js/alertify.js',
|
||||||
], function ($, Messages, Util, AppConfig, Alertify) {
|
'/common/notify.js',
|
||||||
|
'/common/visible.js'
|
||||||
|
], function ($, Messages, Util, AppConfig, Alertify, Notify, Visible) {
|
||||||
|
|
||||||
var UI = {};
|
var UI = {};
|
||||||
|
|
||||||
@@ -48,7 +50,7 @@ define([
|
|||||||
UI.alert = function (msg, cb, force) {
|
UI.alert = function (msg, cb, force) {
|
||||||
cb = cb || function () {};
|
cb = cb || function () {};
|
||||||
if (force !== true) { msg = Util.fixHTML(msg); }
|
if (force !== true) { msg = Util.fixHTML(msg); }
|
||||||
var close = function (e) {
|
var close = function () {
|
||||||
findOKButton().click();
|
findOKButton().click();
|
||||||
};
|
};
|
||||||
var keyHandler = listenForKeys(close, close);
|
var keyHandler = listenForKeys(close, close);
|
||||||
@@ -66,9 +68,9 @@ define([
|
|||||||
cb = cb || function () {};
|
cb = cb || function () {};
|
||||||
if (force !== true) { msg = Util.fixHTML(msg); }
|
if (force !== true) { msg = Util.fixHTML(msg); }
|
||||||
|
|
||||||
var keyHandler = listenForKeys(function (e) { // yes
|
var keyHandler = listenForKeys(function () { // yes
|
||||||
findOKButton().click();
|
findOKButton().click();
|
||||||
}, function (e) { // no
|
}, function () { // no
|
||||||
findCancelButton().click();
|
findCancelButton().click();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -90,9 +92,9 @@ define([
|
|||||||
cb = cb || function () {};
|
cb = cb || function () {};
|
||||||
if (force !== true) { msg = Util.fixHTML(msg); }
|
if (force !== true) { msg = Util.fixHTML(msg); }
|
||||||
|
|
||||||
var keyHandler = listenForKeys(function (e) {
|
var keyHandler = listenForKeys(function () {
|
||||||
findOKButton().click();
|
findOKButton().click();
|
||||||
}, function (e) {
|
}, function () {
|
||||||
findCancelButton().click();
|
findCancelButton().click();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -141,7 +143,7 @@ define([
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
show: function () {
|
show: function () {
|
||||||
$target.show();
|
$target.css('display', 'inline');
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
hide: function () {
|
hide: function () {
|
||||||
@@ -183,7 +185,7 @@ define([
|
|||||||
}
|
}
|
||||||
if (Messages.tips && !hideTips) {
|
if (Messages.tips && !hideTips) {
|
||||||
var $loadingTip = $('<div>', {'id': 'loadingTip'});
|
var $loadingTip = $('<div>', {'id': 'loadingTip'});
|
||||||
var $tip = $('<span>', {'class': 'tips'}).text(getRandomTip()).appendTo($loadingTip);
|
$('<span>', {'class': 'tips'}).text(getRandomTip()).appendTo($loadingTip);
|
||||||
$loadingTip.css({
|
$loadingTip.css({
|
||||||
'top': $('body').height()/2 + $container.height()/2 + 20 + 'px'
|
'top': $('body').height()/2 + $container.height()/2 + 20 + 'px'
|
||||||
});
|
});
|
||||||
@@ -204,7 +206,29 @@ define([
|
|||||||
$('#' + LOADING).find('p').html(error || Messages.error);
|
$('#' + LOADING).find('p').html(error || Messages.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
var importContent = UI.importContent = function (type, f) {
|
// Notify
|
||||||
|
var notify = {};
|
||||||
|
UI.unnotify = function () {
|
||||||
|
if (notify.tabNotification &&
|
||||||
|
typeof(notify.tabNotification.cancel) === 'function') {
|
||||||
|
notify.tabNotification.cancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
UI.notify = function () {
|
||||||
|
if (Visible.isSupported() && !Visible.currently()) {
|
||||||
|
UI.unnotify();
|
||||||
|
notify.tabNotification = Notify.tab(1000, 10);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Visible.isSupported()) {
|
||||||
|
Visible.onChange(function (yes) {
|
||||||
|
if (yes) { UI.unnotify(); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
UI.importContent = function (type, f) {
|
||||||
return function () {
|
return function () {
|
||||||
var $files = $('<input type="file">').click();
|
var $files = $('<input type="file">').click();
|
||||||
$files.on('change', function (e) {
|
$files.on('change', function (e) {
|
||||||
|
|||||||
51
www/common/common-metadata.js
Normal file
51
www/common/common-metadata.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
define(function () {
|
||||||
|
var module = {};
|
||||||
|
|
||||||
|
module.create = function (UserList, Title, cfg) {
|
||||||
|
var exp = {};
|
||||||
|
|
||||||
|
exp.update = function (shjson) {
|
||||||
|
// Extract the user list (metadata) from the hyperjson
|
||||||
|
var json = (!shjson || typeof shjson !== "string") ? "" : JSON.parse(shjson);
|
||||||
|
var titleUpdated = false;
|
||||||
|
var metadata;
|
||||||
|
if (Array.isArray(json)) {
|
||||||
|
metadata = json[3] && json[3].metadata;
|
||||||
|
} else {
|
||||||
|
metadata = json.metadata;
|
||||||
|
}
|
||||||
|
if (typeof metadata === "object") {
|
||||||
|
if (metadata.users) {
|
||||||
|
var userData = metadata.users;
|
||||||
|
// Update the local user data
|
||||||
|
UserList.addToUserData(userData);
|
||||||
|
}
|
||||||
|
if (metadata.defaultTitle) {
|
||||||
|
Title.updateDefaultTitle(metadata.defaultTitle);
|
||||||
|
}
|
||||||
|
if (typeof metadata.title !== "undefined") {
|
||||||
|
Title.updateTitle(metadata.title || Title.defaultTitle);
|
||||||
|
titleUpdated = true;
|
||||||
|
}
|
||||||
|
if (metadata.slideOptions && cfg.slideOptions) {
|
||||||
|
cfg.slideOptions(metadata.slideOptions);
|
||||||
|
}
|
||||||
|
if (metadata.color && cfg.slideColors) {
|
||||||
|
cfg.slideColors(metadata.color, metadata.backColor);
|
||||||
|
}
|
||||||
|
if (typeof(metadata.palette) !== 'undefined' && cfg.updatePalette) {
|
||||||
|
cfg.updatePalette(metadata.palette);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!titleUpdated) {
|
||||||
|
Title.updateTitle(Title.defaultTitle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return exp;
|
||||||
|
};
|
||||||
|
|
||||||
|
return module;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
84
www/common/common-title.js
Normal file
84
www/common/common-title.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
define(function () {
|
||||||
|
var module = {};
|
||||||
|
|
||||||
|
module.create = function (cfg, onLocal, Cryptpad) {
|
||||||
|
var exp = {};
|
||||||
|
|
||||||
|
var parsed = exp.parsedHref = Cryptpad.parsePadUrl(window.location.href);
|
||||||
|
exp.defaultTitle = Cryptpad.getDefaultName(parsed);
|
||||||
|
|
||||||
|
exp.title = document.title; // TOOD slides
|
||||||
|
|
||||||
|
cfg = cfg || {};
|
||||||
|
|
||||||
|
var getHeadingText = cfg.getHeadingText || function () { return; };
|
||||||
|
var updateLocalTitle = function (newTitle) {
|
||||||
|
exp.title = newTitle;
|
||||||
|
if (typeof cfg.updateLocalTitle === "function") {
|
||||||
|
cfg.updateLocalTitle(newTitle);
|
||||||
|
} else {
|
||||||
|
document.title = newTitle;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var $title;
|
||||||
|
exp.setToolbar = function (toolbar) {
|
||||||
|
$title = toolbar && toolbar.title;
|
||||||
|
};
|
||||||
|
|
||||||
|
exp.getTitle = function () { return exp.title; };
|
||||||
|
var isDefaultTitle = exp.isDefaultTitle = function (){return exp.title === exp.defaultTitle;};
|
||||||
|
|
||||||
|
var suggestTitle = exp.suggestTitle = function (fallback) {
|
||||||
|
if (isDefaultTitle()) {
|
||||||
|
return getHeadingText() || fallback || "";
|
||||||
|
} else {
|
||||||
|
return exp.title || getHeadingText() || exp.defaultTitle;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var renameCb = function (err, newTitle) {
|
||||||
|
if (err) { return; }
|
||||||
|
updateLocalTitle(newTitle);
|
||||||
|
console.log('here');
|
||||||
|
onLocal();
|
||||||
|
};
|
||||||
|
|
||||||
|
exp.updateTitle = function (newTitle) {
|
||||||
|
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) {
|
||||||
|
if (err) {
|
||||||
|
console.log("Couldn't set pad title");
|
||||||
|
console.error(err);
|
||||||
|
updateLocalTitle(oldTitle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateLocalTitle(data);
|
||||||
|
if (!$title) { return; }
|
||||||
|
$title.find('span.title').text(data);
|
||||||
|
$title.find('input').val(data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exp.updateDefaultTitle = function (newDefaultTitle) {
|
||||||
|
exp.defaultTitle = newDefaultTitle;
|
||||||
|
if (!$title) { return; }
|
||||||
|
$title.find('input').attr("placeholder", exp.defaultTitle);
|
||||||
|
};
|
||||||
|
|
||||||
|
exp.getTitleConfig = function () {
|
||||||
|
return {
|
||||||
|
onRename: renameCb,
|
||||||
|
suggestName: suggestTitle,
|
||||||
|
defaultName: exp.defaultTitle
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return exp;
|
||||||
|
};
|
||||||
|
|
||||||
|
return module;
|
||||||
|
});
|
||||||
|
|
||||||
105
www/common/common-userlist.js
Normal file
105
www/common/common-userlist.js
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
define(function () {
|
||||||
|
var module = {};
|
||||||
|
|
||||||
|
module.create = function (info, onLocal, Cryptget, Cryptpad) {
|
||||||
|
var exp = {};
|
||||||
|
|
||||||
|
var userData = exp.userData = {};
|
||||||
|
var userList = exp.userList = info.userList;
|
||||||
|
var myData = exp.myData = {};
|
||||||
|
exp.myUserName = info.myID;
|
||||||
|
exp.myNetfluxId = info.myID;
|
||||||
|
|
||||||
|
var network = Cryptpad.getNetwork();
|
||||||
|
|
||||||
|
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
||||||
|
var appType = parsed ? parsed.type : undefined;
|
||||||
|
|
||||||
|
var addToUserData = exp.addToUserData = function(data) {
|
||||||
|
var users = userList.users;
|
||||||
|
for (var attrname in data) { userData[attrname] = data[attrname]; }
|
||||||
|
|
||||||
|
if (users && users.length) {
|
||||||
|
for (var userKey in userData) {
|
||||||
|
if (users.indexOf(userKey) === -1) {
|
||||||
|
delete userData[userKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(userList && typeof userList.onChange === "function") {
|
||||||
|
userList.onChange(userData);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exp.getToolbarConfig = function () {
|
||||||
|
return {
|
||||||
|
data: userData,
|
||||||
|
list: userList,
|
||||||
|
userNetfluxId: exp.myNetfluxId
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var setName = exp.setName = function (newName, cb) {
|
||||||
|
if (typeof(newName) !== 'string') { return; }
|
||||||
|
var myUserNameTemp = newName.trim();
|
||||||
|
if(myUserNameTemp.length > 32) {
|
||||||
|
myUserNameTemp = myUserNameTemp.substr(0, 32);
|
||||||
|
}
|
||||||
|
exp.myUserName = myUserNameTemp;
|
||||||
|
myData = {};
|
||||||
|
myData[exp.myNetfluxId] = {
|
||||||
|
name: exp.myUserName,
|
||||||
|
uid: Cryptpad.getUid(),
|
||||||
|
};
|
||||||
|
addToUserData(myData);
|
||||||
|
Cryptpad.setAttribute('username', exp.myUserName, function (err) {
|
||||||
|
if (err) {
|
||||||
|
console.log("Couldn't set username");
|
||||||
|
console.error(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof cb === "function") { cb(); }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exp.getLastName = function ($changeNameButton, isNew) {
|
||||||
|
Cryptpad.getLastName(function (err, lastName) {
|
||||||
|
if (err) {
|
||||||
|
console.log("Could not get previous name");
|
||||||
|
console.error(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Update the toolbar list:
|
||||||
|
// Add the current user in the metadata
|
||||||
|
if (typeof(lastName) === 'string') {
|
||||||
|
setName(lastName, onLocal);
|
||||||
|
} else {
|
||||||
|
myData[exp.myNetfluxId] = {
|
||||||
|
name: "",
|
||||||
|
uid: Cryptpad.getUid(),
|
||||||
|
};
|
||||||
|
addToUserData(myData);
|
||||||
|
onLocal();
|
||||||
|
$changeNameButton.click();
|
||||||
|
}
|
||||||
|
if (isNew && appType) {
|
||||||
|
Cryptpad.selectTemplate(appType, info.realtime, Cryptget);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Cryptpad.onDisplayNameChanged(function (newName) {
|
||||||
|
setName(newName, onLocal);
|
||||||
|
});
|
||||||
|
|
||||||
|
network.on('reconnect', function (uid) {
|
||||||
|
exp.myNetfluxId = uid;
|
||||||
|
exp.setName(exp.myUserName);
|
||||||
|
});
|
||||||
|
|
||||||
|
return exp;
|
||||||
|
};
|
||||||
|
|
||||||
|
return module;
|
||||||
|
});
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
define([], function () {
|
define([], function () {
|
||||||
var Util = {};
|
var Util = {};
|
||||||
|
|
||||||
var find = Util.find = function (map, path) {
|
Util.find = function (map, path) {
|
||||||
return (map && path.reduce(function (p, n) {
|
return (map && path.reduce(function (p, n) {
|
||||||
return typeof(p[n]) !== 'undefined' && p[n];
|
return typeof(p[n]) !== 'undefined' && p[n];
|
||||||
}, map));
|
}, map));
|
||||||
};
|
};
|
||||||
|
|
||||||
var fixHTML = Util.fixHTML = function (str) {
|
Util.fixHTML = function (str) {
|
||||||
if (!str) { return ''; }
|
if (!str) { return ''; }
|
||||||
return str.replace(/[<>&"']/g, function (x) {
|
return str.replace(/[<>&"']/g, function (x) {
|
||||||
return ({ "<": "<", ">": ">", "&": "&", '"': """, "'": "'" })[x];
|
return ({ "<": "<", ">": ">", "&": "&", '"': """, "'": "'" })[x];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var hexToBase64 = Util.hexToBase64 = function (hex) {
|
Util.hexToBase64 = function (hex) {
|
||||||
var hexArray = hex
|
var hexArray = hex
|
||||||
.replace(/\r|\n/g, "")
|
.replace(/\r|\n/g, "")
|
||||||
.replace(/([\da-fA-F]{2}) ?/g, "0x$1 ")
|
.replace(/([\da-fA-F]{2}) ?/g, "0x$1 ")
|
||||||
@@ -24,7 +24,7 @@ define([], function () {
|
|||||||
return window.btoa(byteString).replace(/\//g, '-').slice(0,-2);
|
return window.btoa(byteString).replace(/\//g, '-').slice(0,-2);
|
||||||
};
|
};
|
||||||
|
|
||||||
var base64ToHex = Util.base64ToHex = function (b64String) {
|
Util.base64ToHex = function (b64String) {
|
||||||
var hexArray = [];
|
var hexArray = [];
|
||||||
atob(b64String.replace(/-/g, '/')).split("").forEach(function(e){
|
atob(b64String.replace(/-/g, '/')).split("").forEach(function(e){
|
||||||
var h = e.charCodeAt(0).toString(16);
|
var h = e.charCodeAt(0).toString(16);
|
||||||
@@ -34,9 +34,9 @@ define([], function () {
|
|||||||
return hexArray.join("");
|
return hexArray.join("");
|
||||||
};
|
};
|
||||||
|
|
||||||
var uint8ArrayToHex = Util.uint8ArrayToHex = function (a) {
|
Util.uint8ArrayToHex = function (a) {
|
||||||
// call slice so Uint8Arrays work as expected
|
// call slice so Uint8Arrays work as expected
|
||||||
return Array.prototype.slice.call(a).map(function (e, i) {
|
return Array.prototype.slice.call(a).map(function (e) {
|
||||||
var n = Number(e & 0xff).toString(16);
|
var n = Number(e & 0xff).toString(16);
|
||||||
if (n === 'NaN') {
|
if (n === 'NaN') {
|
||||||
throw new Error('invalid input resulted in NaN');
|
throw new Error('invalid input resulted in NaN');
|
||||||
@@ -51,7 +51,7 @@ define([], function () {
|
|||||||
}).join('');
|
}).join('');
|
||||||
};
|
};
|
||||||
|
|
||||||
var deduplicateString = Util.deduplicateString = function (array) {
|
Util.deduplicateString = function (array) {
|
||||||
var a = array.slice();
|
var a = array.slice();
|
||||||
for(var i=0; i<a.length; i++) {
|
for(var i=0; i<a.length; i++) {
|
||||||
for(var j=i+1; j<a.length; j++) {
|
for(var j=i+1; j<a.length; j++) {
|
||||||
@@ -61,11 +61,11 @@ define([], function () {
|
|||||||
return a;
|
return a;
|
||||||
};
|
};
|
||||||
|
|
||||||
var getHash = Util.getHash = function () {
|
Util.getHash = function () {
|
||||||
return window.location.hash.slice(1);
|
return window.location.hash.slice(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
var replaceHash = Util.replaceHash = function (hash) {
|
Util.replaceHash = function (hash) {
|
||||||
if (window.history && window.history.replaceState) {
|
if (window.history && window.history.replaceState) {
|
||||||
if (!/^#/.test(hash)) { hash = '#' + hash; }
|
if (!/^#/.test(hash)) { hash = '#' + hash; }
|
||||||
return void window.history.replaceState({}, window.document.title, hash);
|
return void window.history.replaceState({}, window.document.title, hash);
|
||||||
@@ -76,18 +76,28 @@ define([], function () {
|
|||||||
/*
|
/*
|
||||||
* Saving files
|
* Saving files
|
||||||
*/
|
*/
|
||||||
var fixFileName = Util.fixFileName = function (filename) {
|
Util.fixFileName = function (filename) {
|
||||||
return filename.replace(/ /g, '-').replace(/[\/\?]/g, '_')
|
return filename.replace(/ /g, '-').replace(/[\/\?]/g, '_')
|
||||||
.replace(/_+/g, '_');
|
.replace(/_+/g, '_');
|
||||||
};
|
};
|
||||||
|
|
||||||
var bytesToMegabytes = Util.bytesToMegabytes = function (bytes) {
|
Util.bytesToMegabytes = function (bytes) {
|
||||||
return Math.floor((bytes / (1024 * 1024) * 100)) / 100;
|
return Math.floor((bytes / (1024 * 1024) * 100)) / 100;
|
||||||
};
|
};
|
||||||
|
|
||||||
var bytesToKilobytes = Util.bytesToKilobytes = function (bytes) {
|
Util.bytesToKilobytes = function (bytes) {
|
||||||
return Math.floor(bytes / 1024 * 100) / 100;
|
return Math.floor(bytes / 1024 * 100) / 100;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Util.fetch = function (src, cb) {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", src, true);
|
||||||
|
xhr.responseType = "arraybuffer";
|
||||||
|
xhr.onload = function () {
|
||||||
|
return void cb(void 0, new Uint8Array(xhr.response));
|
||||||
|
};
|
||||||
|
xhr.send(null);
|
||||||
|
};
|
||||||
|
|
||||||
return Util;
|
return Util;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
define([
|
define([
|
||||||
|
'/customize/application_config.js',
|
||||||
'/bower_components/scrypt-async/scrypt-async.min.js',
|
'/bower_components/scrypt-async/scrypt-async.min.js',
|
||||||
], function () {
|
], function (AppConfig) {
|
||||||
var Cred = {};
|
var Cred = {};
|
||||||
var Scrypt = window.scrypt;
|
var Scrypt = window.scrypt;
|
||||||
|
|
||||||
@@ -8,22 +9,26 @@ define([
|
|||||||
return typeof(x) === 'string';
|
return typeof(x) === 'string';
|
||||||
};
|
};
|
||||||
|
|
||||||
var isValidUsername = Cred.isValidUsername = function (name) {
|
Cred.isValidUsername = function (name) {
|
||||||
return !!(name && isString(name));
|
return !!(name && isString(name));
|
||||||
};
|
};
|
||||||
|
|
||||||
var isValidPassword = Cred.isValidPassword = function (passwd) {
|
Cred.isValidPassword = function (passwd) {
|
||||||
return !!(passwd && isString(passwd));
|
return !!(passwd && isString(passwd));
|
||||||
};
|
};
|
||||||
|
|
||||||
var passwordsMatch = Cred.passwordsMatch = function (a, b) {
|
Cred.passwordsMatch = function (a, b) {
|
||||||
return isString(a) && isString(b) && a === b;
|
return isString(a) && isString(b) && a === b;
|
||||||
};
|
};
|
||||||
|
|
||||||
var deriveFromPassphrase = Cred.deriveFromPassphrase = function
|
Cred.customSalt = function () {
|
||||||
(username, password, len, cb) {
|
return typeof(AppConfig.loginSalt) === 'string'?
|
||||||
|
AppConfig.loginSalt: '';
|
||||||
|
};
|
||||||
|
|
||||||
|
Cred.deriveFromPassphrase = function (username, password, len, cb) {
|
||||||
Scrypt(password,
|
Scrypt(password,
|
||||||
username,
|
username + Cred.customSalt(), // salt
|
||||||
8, // memoryCost (n)
|
8, // memoryCost (n)
|
||||||
1024, // block size parameter (r)
|
1024, // block size parameter (r)
|
||||||
len || 128, // dkLen
|
len || 128, // dkLen
|
||||||
@@ -32,7 +37,7 @@ define([
|
|||||||
undefined); // format, could be 'base64'
|
undefined); // format, could be 'base64'
|
||||||
};
|
};
|
||||||
|
|
||||||
var dispenser = Cred.dispenser = function (bytes) {
|
Cred.dispenser = function (bytes) {
|
||||||
var entropy = {
|
var entropy = {
|
||||||
used: 0,
|
used: 0,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ define([
|
|||||||
'/common/cryptpad-common.js',
|
'/common/cryptpad-common.js',
|
||||||
'/bower_components/textpatcher/TextPatcher.js'
|
'/bower_components/textpatcher/TextPatcher.js'
|
||||||
], function ($, Crypto, Realtime, Cryptpad, TextPatcher) {
|
], function ($, Crypto, Realtime, Cryptpad, TextPatcher) {
|
||||||
var Messages = Cryptpad.Messages;
|
//var Messages = Cryptpad.Messages;
|
||||||
var noop = function () {};
|
//var noop = function () {};
|
||||||
var finish = function (S, err, doc) {
|
var finish = function (S, err, doc) {
|
||||||
if (S.done) { return; }
|
if (S.done) { return; }
|
||||||
S.cb(err, doc);
|
S.cb(err, doc);
|
||||||
@@ -50,14 +50,14 @@ define([
|
|||||||
var Session = { cb: cb, };
|
var Session = { cb: cb, };
|
||||||
var config = makeConfig(hash);
|
var config = makeConfig(hash);
|
||||||
|
|
||||||
var onReady = config.onReady = function (info) {
|
config.onReady = function (info) {
|
||||||
var rt = Session.session = info.realtime;
|
var rt = Session.session = info.realtime;
|
||||||
Session.network = info.network;
|
Session.network = info.network;
|
||||||
finish(Session, void 0, rt.getUserDoc());
|
finish(Session, void 0, rt.getUserDoc());
|
||||||
};
|
};
|
||||||
overwrite(config, opt);
|
overwrite(config, opt);
|
||||||
|
|
||||||
var realtime = Session.realtime = Realtime.start(config);
|
Session.realtime = Realtime.start(config);
|
||||||
};
|
};
|
||||||
|
|
||||||
var put = function (hash, doc, cb, opt) {
|
var put = function (hash, doc, cb, opt) {
|
||||||
@@ -87,7 +87,7 @@ define([
|
|||||||
};
|
};
|
||||||
overwrite(config, opt);
|
overwrite(config, opt);
|
||||||
|
|
||||||
var realtime = Session.session = Realtime.start(config);
|
Session.session = Realtime.start(config);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -7,11 +7,15 @@ define([
|
|||||||
'/common/common-hash.js',
|
'/common/common-hash.js',
|
||||||
'/common/common-interface.js',
|
'/common/common-interface.js',
|
||||||
'/common/common-history.js',
|
'/common/common-history.js',
|
||||||
|
'/common/common-userlist.js',
|
||||||
|
'/common/common-title.js',
|
||||||
|
'/common/common-metadata.js',
|
||||||
|
'/common/common-codemirror.js',
|
||||||
|
|
||||||
'/common/clipboard.js',
|
'/common/clipboard.js',
|
||||||
'/common/pinpad.js',
|
'/common/pinpad.js',
|
||||||
'/customize/application_config.js'
|
'/customize/application_config.js'
|
||||||
], function ($, Config, Messages, Store, Util, Hash, UI, History, Clipboard, Pinpad, AppConfig) {
|
], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata, CodeMirror, Clipboard, Pinpad, AppConfig) {
|
||||||
|
|
||||||
/* This file exposes functionality which is specific to Cryptpad, but not to
|
/* This file exposes functionality which is specific to Cryptpad, but not to
|
||||||
any particular pad type. This includes functions for committing metadata
|
any particular pad type. This includes functions for committing metadata
|
||||||
@@ -29,7 +33,7 @@ define([
|
|||||||
var userHashKey = common.userHashKey = 'User_hash';
|
var userHashKey = common.userHashKey = 'User_hash';
|
||||||
var userNameKey = common.userNameKey = 'User_name';
|
var userNameKey = common.userNameKey = 'User_name';
|
||||||
var fileHashKey = common.fileHashKey = 'FS_hash';
|
var fileHashKey = common.fileHashKey = 'FS_hash';
|
||||||
var displayNameKey = common.displayNameKey = 'cryptpad.username';
|
common.displayNameKey = 'cryptpad.username';
|
||||||
var newPadNameKey = common.newPadNameKey = "newPadName";
|
var newPadNameKey = common.newPadNameKey = "newPadName";
|
||||||
var newPadPathKey = common.newPadPathKey = "newPadPath";
|
var newPadPathKey = common.newPadPathKey = "newPadPath";
|
||||||
var storageKey = common.storageKey = 'CryptPad_RECENTPADS';
|
var storageKey = common.storageKey = 'CryptPad_RECENTPADS';
|
||||||
@@ -39,10 +43,10 @@ define([
|
|||||||
var rpc;
|
var rpc;
|
||||||
|
|
||||||
// import UI elements
|
// import UI elements
|
||||||
var findCancelButton = common.findCancelButton = UI.findCancelButton;
|
common.findCancelButton = UI.findCancelButton;
|
||||||
var findOKButton = common.findOKButton = UI.findOKButton;
|
common.findOKButton = UI.findOKButton;
|
||||||
var listenForKeys = common.listenForKeys = UI.listenForKeys;
|
common.listenForKeys = UI.listenForKeys;
|
||||||
var stopListening = common.stopListening = UI.stopListening;
|
common.stopListening = UI.stopListening;
|
||||||
common.prompt = UI.prompt;
|
common.prompt = UI.prompt;
|
||||||
common.confirm = UI.confirm;
|
common.confirm = UI.confirm;
|
||||||
common.alert = UI.alert;
|
common.alert = UI.alert;
|
||||||
@@ -52,19 +56,22 @@ define([
|
|||||||
common.addLoadingScreen = UI.addLoadingScreen;
|
common.addLoadingScreen = UI.addLoadingScreen;
|
||||||
common.removeLoadingScreen = UI.removeLoadingScreen;
|
common.removeLoadingScreen = UI.removeLoadingScreen;
|
||||||
common.errorLoadingScreen = UI.errorLoadingScreen;
|
common.errorLoadingScreen = UI.errorLoadingScreen;
|
||||||
|
common.notify = UI.notify;
|
||||||
|
common.unnotify = UI.unnotify;
|
||||||
|
|
||||||
// import common utilities for export
|
// import common utilities for export
|
||||||
var find = common.find = Util.find;
|
common.find = Util.find;
|
||||||
var fixHTML = common.fixHTML = Util.fixHTML;
|
var fixHTML = common.fixHTML = Util.fixHTML;
|
||||||
var hexToBase64 = common.hexToBase64 = Util.hexToBase64;
|
common.hexToBase64 = Util.hexToBase64;
|
||||||
var base64ToHex = common.base64ToHex = Util.base64ToHex;
|
common.base64ToHex = Util.base64ToHex;
|
||||||
var deduplicateString = common.deduplicateString = Util.deduplicateString;
|
var deduplicateString = common.deduplicateString = Util.deduplicateString;
|
||||||
var uint8ArrayToHex = common.uint8ArrayToHex = Util.uint8ArrayToHex;
|
common.uint8ArrayToHex = Util.uint8ArrayToHex;
|
||||||
var replaceHash = common.replaceHash = Util.replaceHash;
|
common.replaceHash = Util.replaceHash;
|
||||||
var getHash = common.getHash = Util.getHash;
|
var getHash = common.getHash = Util.getHash;
|
||||||
var fixFileName = common.fixFileName = Util.fixFileName;
|
common.fixFileName = Util.fixFileName;
|
||||||
common.bytesToMegabytes = Util.bytesToMegabytes;
|
common.bytesToMegabytes = Util.bytesToMegabytes;
|
||||||
common.bytesToKilobytes = Util.bytesToKilobytes;
|
common.bytesToKilobytes = Util.bytesToKilobytes;
|
||||||
|
common.fetch = Util.fetch;
|
||||||
|
|
||||||
// import hash utilities for export
|
// import hash utilities for export
|
||||||
var createRandomHash = common.createRandomHash = Hash.createRandomHash;
|
var createRandomHash = common.createRandomHash = Hash.createRandomHash;
|
||||||
@@ -73,14 +80,29 @@ define([
|
|||||||
var hrefToHexChannelId = common.hrefToHexChannelId = Hash.hrefToHexChannelId;
|
var hrefToHexChannelId = common.hrefToHexChannelId = Hash.hrefToHexChannelId;
|
||||||
var parseHash = common.parseHash = Hash.parseHash;
|
var parseHash = common.parseHash = Hash.parseHash;
|
||||||
var getRelativeHref = common.getRelativeHref = Hash.getRelativeHref;
|
var getRelativeHref = common.getRelativeHref = Hash.getRelativeHref;
|
||||||
|
common.getBlobPathFromHex = Hash.getBlobPathFromHex;
|
||||||
|
|
||||||
common.getEditHashFromKeys = Hash.getEditHashFromKeys;
|
common.getEditHashFromKeys = Hash.getEditHashFromKeys;
|
||||||
common.getViewHashFromKeys = Hash.getViewHashFromKeys;
|
common.getViewHashFromKeys = Hash.getViewHashFromKeys;
|
||||||
|
common.getFileHashFromKeys = Hash.getFileHashFromKeys;
|
||||||
common.getSecrets = Hash.getSecrets;
|
common.getSecrets = Hash.getSecrets;
|
||||||
common.getHashes = Hash.getHashes;
|
common.getHashes = Hash.getHashes;
|
||||||
common.createChannelId = Hash.createChannelId;
|
common.createChannelId = Hash.createChannelId;
|
||||||
common.findWeaker = Hash.findWeaker;
|
common.findWeaker = Hash.findWeaker;
|
||||||
common.findStronger = Hash.findStronger;
|
common.findStronger = Hash.findStronger;
|
||||||
|
common.serializeHash = Hash.serializeHash;
|
||||||
|
|
||||||
|
// Userlist
|
||||||
|
common.createUserList = UserList.create;
|
||||||
|
|
||||||
|
// Title
|
||||||
|
common.createTitle = Title.create;
|
||||||
|
|
||||||
|
// Metadata
|
||||||
|
common.createMetadata = Metadata.create;
|
||||||
|
|
||||||
|
// CodeMirror
|
||||||
|
common.createCodemirror = CodeMirror.create;
|
||||||
|
|
||||||
// History
|
// History
|
||||||
common.getHistory = function (config) { return History.create(common, config); };
|
common.getHistory = function (config) { return History.create(common, config); };
|
||||||
@@ -119,13 +141,13 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var reportAppUsage = common.reportAppUsage = function () {
|
common.reportAppUsage = function () {
|
||||||
var pattern = window.location.pathname.split('/')
|
var pattern = window.location.pathname.split('/')
|
||||||
.filter(function (x) { return x; }).join('.');
|
.filter(function (x) { return x; }).join('.');
|
||||||
feedback(pattern);
|
feedback(pattern);
|
||||||
};
|
};
|
||||||
|
|
||||||
var getUid = common.getUid = function () {
|
common.getUid = function () {
|
||||||
if (store && store.getProxy() && store.getProxy().proxy) {
|
if (store && store.getProxy() && store.getProxy().proxy) {
|
||||||
return store.getProxy().proxy.uid;
|
return store.getProxy().proxy.uid;
|
||||||
}
|
}
|
||||||
@@ -150,7 +172,7 @@ define([
|
|||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
var getWebsocketURL = common.getWebsocketURL = function () {
|
common.getWebsocketURL = function () {
|
||||||
if (!Config.websocketPath) { return Config.websocketURL; }
|
if (!Config.websocketPath) { return Config.websocketURL; }
|
||||||
var path = Config.websocketPath;
|
var path = Config.websocketPath;
|
||||||
if (/^ws{1,2}:\/\//.test(path)) { return path; }
|
if (/^ws{1,2}:\/\//.test(path)) { return path; }
|
||||||
@@ -162,9 +184,10 @@ define([
|
|||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
var login = common.login = function (hash, name, cb) {
|
common.login = function (hash, name, cb) {
|
||||||
if (!hash) { throw new Error('expected a user hash'); }
|
if (!hash) { throw new Error('expected a user hash'); }
|
||||||
if (!name) { throw new Error('expected a user name'); }
|
if (!name) { throw new Error('expected a user name'); }
|
||||||
|
hash = common.serializeHash(hash);
|
||||||
localStorage.setItem(userHashKey, hash);
|
localStorage.setItem(userHashKey, hash);
|
||||||
localStorage.setItem(userNameKey, name);
|
localStorage.setItem(userNameKey, name);
|
||||||
if (cb) { cb(); }
|
if (cb) { cb(); }
|
||||||
@@ -185,10 +208,11 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
var logoutHandlers = [];
|
var logoutHandlers = [];
|
||||||
var logout = common.logout = function (cb) {
|
common.logout = function (cb) {
|
||||||
[
|
[
|
||||||
userNameKey,
|
userNameKey,
|
||||||
userHashKey,
|
userHashKey,
|
||||||
|
'loginToken',
|
||||||
].forEach(function (k) {
|
].forEach(function (k) {
|
||||||
sessionStorage.removeItem(k);
|
sessionStorage.removeItem(k);
|
||||||
localStorage.removeItem(k);
|
localStorage.removeItem(k);
|
||||||
@@ -208,18 +232,19 @@ define([
|
|||||||
|
|
||||||
if (cb) { cb(); }
|
if (cb) { cb(); }
|
||||||
};
|
};
|
||||||
var onLogout = common.onLogout = function (h) {
|
common.onLogout = function (h) {
|
||||||
if (typeof (h) !== "function") { return; }
|
if (typeof (h) !== "function") { return; }
|
||||||
if (logoutHandlers.indexOf(h) !== -1) { return; }
|
if (logoutHandlers.indexOf(h) !== -1) { return; }
|
||||||
logoutHandlers.push(h);
|
logoutHandlers.push(h);
|
||||||
};
|
};
|
||||||
|
|
||||||
var getUserHash = common.getUserHash = function () {
|
var getUserHash = common.getUserHash = function () {
|
||||||
var hash;
|
var hash = localStorage[userHashKey];
|
||||||
[sessionStorage, localStorage].some(function (s) {
|
|
||||||
var h = s[userHashKey];
|
if (hash) {
|
||||||
if (h) { return (hash = h); }
|
var sHash = common.serializeHash(hash);
|
||||||
});
|
if (sHash !== hash) { localStorage[userHashKey] = sHash; }
|
||||||
|
}
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
};
|
};
|
||||||
@@ -228,7 +253,7 @@ define([
|
|||||||
return typeof getUserHash() === "string";
|
return typeof getUserHash() === "string";
|
||||||
};
|
};
|
||||||
|
|
||||||
var hasSigningKeys = common.hasSigningKeys = function (proxy) {
|
common.hasSigningKeys = function (proxy) {
|
||||||
return typeof(proxy) === 'object' &&
|
return typeof(proxy) === 'object' &&
|
||||||
typeof(proxy.edPrivate) === 'string' &&
|
typeof(proxy.edPrivate) === 'string' &&
|
||||||
typeof(proxy.edPublic) === 'string';
|
typeof(proxy.edPublic) === 'string';
|
||||||
@@ -295,16 +320,20 @@ define([
|
|||||||
pads.forEach(function (pad, i) {
|
pads.forEach(function (pad, i) {
|
||||||
if (pad && typeof(pad) === 'object') {
|
if (pad && typeof(pad) === 'object') {
|
||||||
var hash = checkObjectData(pad);
|
var hash = checkObjectData(pad);
|
||||||
if (!hash || !common.parseHash(hash)) { return; }
|
if (!hash || !common.parseHash(hash)) {
|
||||||
|
console.error("[Cryptpad.checkRecentPads] pad had unexpected value", pad);
|
||||||
|
getStore().removeData(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
return pad;
|
return pad;
|
||||||
}
|
}
|
||||||
console.error("[Cryptpad.migrateRecentPads] pad had unexpected value");
|
console.error("[Cryptpad.checkRecentPads] pad had unexpected value", pad);
|
||||||
getStore().removeData(i);
|
getStore().removeData(i);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the pads from localStorage to migrate them to the object store
|
// Get the pads from localStorage to migrate them to the object store
|
||||||
var getLegacyPads = common.getLegacyPads = function (cb) {
|
common.getLegacyPads = function (cb) {
|
||||||
require(['/customize/store.js'], function(Legacy) { // TODO DEPRECATE_F
|
require(['/customize/store.js'], function(Legacy) { // TODO DEPRECATE_F
|
||||||
Legacy.ready(function (err, legacy) {
|
Legacy.ready(function (err, legacy) {
|
||||||
if (err) { cb(err, null); return; }
|
if (err) { cb(err, null); return; }
|
||||||
@@ -324,7 +353,6 @@ define([
|
|||||||
// Create untitled documents when no name is given
|
// Create untitled documents when no name is given
|
||||||
var getDefaultName = common.getDefaultName = function (parsed) {
|
var getDefaultName = common.getDefaultName = function (parsed) {
|
||||||
var type = parsed.type;
|
var type = parsed.type;
|
||||||
var untitledIndex = 1;
|
|
||||||
var name = (Messages.type)[type] + ' - ' + new Date().toString().split(' ').slice(0,4).join(' ');
|
var name = (Messages.type)[type] + ' - ' + new Date().toString().split(' ').slice(0,4).join(' ');
|
||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
@@ -344,37 +372,37 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Sort pads according to how recently they were accessed */
|
/* Sort pads according to how recently they were accessed */
|
||||||
var mostRecent = common.mostRecent = function (a, b) {
|
common.mostRecent = function (a, b) {
|
||||||
return new Date(b.atime).getTime() - new Date(a.atime).getTime();
|
return new Date(b.atime).getTime() - new Date(a.atime).getTime();
|
||||||
};
|
};
|
||||||
|
|
||||||
// STORAGE
|
// STORAGE
|
||||||
var setPadAttribute = common.setPadAttribute = function (attr, value, cb) {
|
common.setPadAttribute = function (attr, value, cb) {
|
||||||
getStore().setDrive([getHash(), attr].join('.'), value, function (err, data) {
|
getStore().setDrive([getHash(), attr].join('.'), value, function (err, data) {
|
||||||
cb(err, data);
|
cb(err, data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var setAttribute = common.setAttribute = function (attr, value, cb) {
|
common.setAttribute = function (attr, value, cb) {
|
||||||
getStore().set(["cryptpad", attr].join('.'), value, function (err, data) {
|
getStore().set(["cryptpad", attr].join('.'), value, function (err, data) {
|
||||||
cb(err, data);
|
cb(err, data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var setLSAttribute = common.setLSAttribute = function (attr, value) {
|
common.setLSAttribute = function (attr, value) {
|
||||||
localStorage[attr] = value;
|
localStorage[attr] = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
// STORAGE
|
// STORAGE
|
||||||
var getPadAttribute = common.getPadAttribute = function (attr, cb) {
|
common.getPadAttribute = function (attr, cb) {
|
||||||
getStore().getDrive([getHash(), attr].join('.'), function (err, data) {
|
getStore().getDrive([getHash(), attr].join('.'), function (err, data) {
|
||||||
cb(err, data);
|
cb(err, data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var getAttribute = common.getAttribute = function (attr, cb) {
|
common.getAttribute = function (attr, cb) {
|
||||||
getStore().get(["cryptpad", attr].join('.'), function (err, data) {
|
getStore().get(["cryptpad", attr].join('.'), function (err, data) {
|
||||||
cb(err, data);
|
cb(err, data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var getLSAttribute = common.getLSAttribute = function (attr) {
|
common.getLSAttribute = function (attr) {
|
||||||
return localStorage[attr];
|
return localStorage[attr];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -389,19 +417,19 @@ define([
|
|||||||
});
|
});
|
||||||
return templates;
|
return templates;
|
||||||
};
|
};
|
||||||
var addTemplate = common.addTemplate = function (data) {
|
common.addTemplate = function (data) {
|
||||||
getStore().pushData(data);
|
getStore().pushData(data);
|
||||||
getStore().addPad(data.href, ['template']);
|
getStore().addPad(data.href, ['template']);
|
||||||
};
|
};
|
||||||
|
|
||||||
var isTemplate = common.isTemplate = function (href) {
|
common.isTemplate = function (href) {
|
||||||
var rhref = getRelativeHref(href);
|
var rhref = getRelativeHref(href);
|
||||||
var templates = listTemplates();
|
var templates = listTemplates();
|
||||||
return templates.some(function (t) {
|
return templates.some(function (t) {
|
||||||
return t.href === rhref;
|
return t.href === rhref;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var selectTemplate = common.selectTemplate = function (type, rt, Crypt) {
|
common.selectTemplate = function (type, rt, Crypt) {
|
||||||
if (!AppConfig.enableTemplates) { return; }
|
if (!AppConfig.enableTemplates) { return; }
|
||||||
var temps = listTemplates(type);
|
var temps = listTemplates(type);
|
||||||
if (temps.length === 0) { return; }
|
if (temps.length === 0) { return; }
|
||||||
@@ -419,7 +447,7 @@ define([
|
|||||||
Crypt.get(parsed.hash, function (err, val) {
|
Crypt.get(parsed.hash, function (err, val) {
|
||||||
if (err) { throw new Error(err); }
|
if (err) { throw new Error(err); }
|
||||||
var p = parsePadUrl(window.location.href);
|
var p = parsePadUrl(window.location.href);
|
||||||
Crypt.put(p.hash, val, function (e) {
|
Crypt.put(p.hash, val, function () {
|
||||||
common.findOKButton().click();
|
common.findOKButton().click();
|
||||||
common.removeLoadingScreen();
|
common.removeLoadingScreen();
|
||||||
});
|
});
|
||||||
@@ -444,28 +472,28 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
// STORAGE: Display Name
|
// STORAGE: Display Name
|
||||||
var getLastName = common.getLastName = function (cb) {
|
common.getLastName = function (cb) {
|
||||||
common.getAttribute('username', function (err, userName) {
|
common.getAttribute('username', function (err, userName) {
|
||||||
cb(err, userName);
|
cb(err, userName);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var _onDisplayNameChanged = [];
|
var _onDisplayNameChanged = [];
|
||||||
var onDisplayNameChanged = common.onDisplayNameChanged = function (h) {
|
common.onDisplayNameChanged = function (h) {
|
||||||
if (typeof(h) !== "function") { return; }
|
if (typeof(h) !== "function") { return; }
|
||||||
if (_onDisplayNameChanged.indexOf(h) !== -1) { return; }
|
if (_onDisplayNameChanged.indexOf(h) !== -1) { return; }
|
||||||
_onDisplayNameChanged.push(h);
|
_onDisplayNameChanged.push(h);
|
||||||
};
|
};
|
||||||
var changeDisplayName = common.changeDisplayName = function (newName) {
|
common.changeDisplayName = function (newName) {
|
||||||
_onDisplayNameChanged.forEach(function (h) {
|
_onDisplayNameChanged.forEach(function (h) {
|
||||||
h(newName);
|
h(newName);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// STORAGE
|
// STORAGE
|
||||||
var forgetPad = common.forgetPad = function (href, cb) {
|
common.forgetPad = function (href, cb) {
|
||||||
var parsed = parsePadUrl(href);
|
var parsed = parsePadUrl(href);
|
||||||
|
|
||||||
var callback = function (err, data) {
|
var callback = function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
return;
|
return;
|
||||||
@@ -495,7 +523,19 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var setPadTitle = common.setPadTitle = function (name, cb) {
|
var updateFileName = function (href, oldName, newName) {
|
||||||
|
var fo = getStore().getProxy().fo;
|
||||||
|
var paths = fo.findFileInRoot(href);
|
||||||
|
paths.forEach(function (path) {
|
||||||
|
if (path.length !== 2) { return; }
|
||||||
|
var name = path[1].split('_')[0];
|
||||||
|
var parsed = parsePadUrl(href);
|
||||||
|
if (path.length === 2 && name === oldName && isDefaultName(parsed, name)) {
|
||||||
|
fo.rename(path, newName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
common.setPadTitle = function (name, cb) {
|
||||||
var href = window.location.href;
|
var href = window.location.href;
|
||||||
var parsed = parsePadUrl(href);
|
var parsed = parsePadUrl(href);
|
||||||
href = getRelativeHref(href);
|
href = getRelativeHref(href);
|
||||||
@@ -509,12 +549,12 @@ define([
|
|||||||
|
|
||||||
var updateWeaker = [];
|
var updateWeaker = [];
|
||||||
var contains;
|
var contains;
|
||||||
var renamed = recent.map(function (pad) {
|
recent.forEach(function (pad) {
|
||||||
var p = parsePadUrl(pad.href);
|
var p = parsePadUrl(pad.href);
|
||||||
|
|
||||||
if (p.type !== parsed.type) { return pad; }
|
if (p.type !== parsed.type) { return pad; }
|
||||||
|
|
||||||
var shouldUpdate = p.hash === parsed.hash;
|
var shouldUpdate = p.hash.replace(/\/$/, '') === parsed.hash.replace(/\/$/, '');
|
||||||
|
|
||||||
// Version 1 : we have up to 4 differents hash for 1 pad, keep the strongest :
|
// Version 1 : we have up to 4 differents hash for 1 pad, keep the strongest :
|
||||||
// Edit > Edit (present) > View > View (present)
|
// Edit > Edit (present) > View > View (present)
|
||||||
@@ -540,6 +580,7 @@ define([
|
|||||||
pad.atime = +new Date();
|
pad.atime = +new Date();
|
||||||
|
|
||||||
// set the name
|
// set the name
|
||||||
|
var old = pad.title;
|
||||||
pad.title = name;
|
pad.title = name;
|
||||||
|
|
||||||
// If we now have a stronger version of a stored href, replace the weaker one by the strong one
|
// If we now have a stronger version of a stored href, replace the weaker one by the strong one
|
||||||
@@ -550,14 +591,23 @@ define([
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
pad.href = href;
|
pad.href = href;
|
||||||
|
updateFileName(href, old, name);
|
||||||
}
|
}
|
||||||
return pad;
|
return pad;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!contains) {
|
if (!contains && href) {
|
||||||
var data = makePad(href, name);
|
var data = makePad(href, name);
|
||||||
getStore().pushData(data);
|
getStore().pushData(data, function (e) {
|
||||||
getStore().addPad(href, common.initialPath, common.initialName || name);
|
if (e) {
|
||||||
|
if (e === 'E_OVER_LIMIT' && AppConfig.enablePinLimit) {
|
||||||
|
common.alert(Messages.pinLimitNotPinned, null, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else { throw new Error("Cannot push this pad to CryptDrive", e); }
|
||||||
|
}
|
||||||
|
getStore().addPad(data, common.initialPath);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (updateWeaker.length > 0) {
|
if (updateWeaker.length > 0) {
|
||||||
updateWeaker.forEach(function (obj) {
|
updateWeaker.forEach(function (obj) {
|
||||||
@@ -584,7 +634,7 @@ define([
|
|||||||
/*
|
/*
|
||||||
* Buttons
|
* Buttons
|
||||||
*/
|
*/
|
||||||
var renamePad = common.renamePad = function (title, callback) {
|
common.renamePad = function (title, callback) {
|
||||||
if (title === null) { return; }
|
if (title === null) { return; }
|
||||||
|
|
||||||
if (title.trim() === "") {
|
if (title.trim() === "") {
|
||||||
@@ -592,7 +642,7 @@ define([
|
|||||||
title = getDefaultName(parsed);
|
title = getDefaultName(parsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
common.setPadTitle(title, function (err, data) {
|
common.setPadTitle(title, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log("unable to set pad title");
|
console.log("unable to set pad title");
|
||||||
console.log(err);
|
console.log(err);
|
||||||
@@ -642,7 +692,7 @@ define([
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var arePinsSynced = common.arePinsSynced = function (cb) {
|
common.arePinsSynced = function (cb) {
|
||||||
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
|
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
|
||||||
|
|
||||||
var list = getCanonicalChannelList();
|
var list = getCanonicalChannelList();
|
||||||
@@ -653,7 +703,7 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var resetPins = common.resetPins = function (cb) {
|
common.resetPins = function (cb) {
|
||||||
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
|
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
|
||||||
|
|
||||||
var list = getCanonicalChannelList();
|
var list = getCanonicalChannelList();
|
||||||
@@ -663,7 +713,7 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var pinPads = common.pinPads = function (pads, cb) {
|
common.pinPads = function (pads, cb) {
|
||||||
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
|
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
|
||||||
|
|
||||||
rpc.pin(pads, function (e, hash) {
|
rpc.pin(pads, function (e, hash) {
|
||||||
@@ -672,7 +722,7 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var unpinPads = common.unpinPads = function (pads, cb) {
|
common.unpinPads = function (pads, cb) {
|
||||||
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
|
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
|
||||||
|
|
||||||
rpc.unpin(pads, function (e, hash) {
|
rpc.unpin(pads, function (e, hash) {
|
||||||
@@ -681,12 +731,12 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var getPinnedUsage = common.getPinnedUsage = function (cb) {
|
common.getPinnedUsage = function (cb) {
|
||||||
if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); }
|
if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); }
|
||||||
rpc.getFileListSize(cb);
|
rpc.getFileListSize(cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
var getFileSize = common.getFileSize = function (href, cb) {
|
common.getFileSize = function (href, cb) {
|
||||||
var channelId = Hash.hrefToHexChannelId(href);
|
var channelId = Hash.hrefToHexChannelId(href);
|
||||||
rpc.getFileSize(channelId, function (e, bytes) {
|
rpc.getFileSize(channelId, function (e, bytes) {
|
||||||
if (e) { return void cb(e); }
|
if (e) { return void cb(e); }
|
||||||
@@ -694,7 +744,30 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var createButton = common.createButton = function (type, rightside, data, callback) {
|
common.getPinLimit = function (cb) {
|
||||||
|
cb(void 0, typeof(AppConfig.pinLimit) === 'number'? AppConfig.pinLimit: 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
common.isOverPinLimit = function (cb) {
|
||||||
|
if (!common.isLoggedIn() || !AppConfig.enablePinLimit) { return void cb(null, false); }
|
||||||
|
var usage;
|
||||||
|
var andThen = function (e, limit) {
|
||||||
|
if (e) { return void cb(e); }
|
||||||
|
var data = {usage: usage, limit: limit};
|
||||||
|
if (usage > limit) {
|
||||||
|
return void cb (null, true, data);
|
||||||
|
}
|
||||||
|
return void cb (null, false, data);
|
||||||
|
};
|
||||||
|
var todo = function (e, used) {
|
||||||
|
usage = common.bytesToMegabytes(used);
|
||||||
|
if (e) { return void cb(e); }
|
||||||
|
common.getPinLimit(andThen);
|
||||||
|
};
|
||||||
|
common.getPinnedUsage(todo);
|
||||||
|
};
|
||||||
|
|
||||||
|
common.createButton = function (type, rightside, data, callback) {
|
||||||
var button;
|
var button;
|
||||||
var size = "17px";
|
var size = "17px";
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -783,7 +856,7 @@ define([
|
|||||||
var href = window.location.href;
|
var href = window.location.href;
|
||||||
common.confirm(Messages.forgetPrompt, function (yes) {
|
common.confirm(Messages.forgetPrompt, function (yes) {
|
||||||
if (!yes) { return; }
|
if (!yes) { return; }
|
||||||
common.forgetPad(href, function (err, data) {
|
common.forgetPad(href, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log("unable to forget pad");
|
console.log("unable to forget pad");
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -1002,7 +1075,7 @@ define([
|
|||||||
|
|
||||||
// Provide $container if you want to put the generated block in another element
|
// Provide $container if you want to put the generated block in another element
|
||||||
// Provide $initBlock if you already have the menu block and you want the content inserted in it
|
// Provide $initBlock if you already have the menu block and you want the content inserted in it
|
||||||
var createLanguageSelector = common.createLanguageSelector = function ($container, $initBlock) {
|
common.createLanguageSelector = function ($container, $initBlock) {
|
||||||
var options = [];
|
var options = [];
|
||||||
var languages = Messages._languages;
|
var languages = Messages._languages;
|
||||||
var keys = Object.keys(languages).sort();
|
var keys = Object.keys(languages).sort();
|
||||||
@@ -1036,7 +1109,7 @@ define([
|
|||||||
return $block;
|
return $block;
|
||||||
};
|
};
|
||||||
|
|
||||||
var createUserAdminMenu = common.createUserAdminMenu = function (config) {
|
common.createUserAdminMenu = function (config) {
|
||||||
var $displayedName = $('<span>', {'class': config.displayNameCls || 'displayName'});
|
var $displayedName = $('<span>', {'class': config.displayNameCls || 'displayName'});
|
||||||
var accountName = localStorage[common.userNameKey];
|
var accountName = localStorage[common.userNameKey];
|
||||||
var account = isLoggedIn();
|
var account = isLoggedIn();
|
||||||
@@ -1123,24 +1196,24 @@ define([
|
|||||||
};
|
};
|
||||||
var $userAdmin = createDropdown(dropdownConfigUser);
|
var $userAdmin = createDropdown(dropdownConfigUser);
|
||||||
|
|
||||||
$userAdmin.find('a.logout').click(function (e) {
|
$userAdmin.find('a.logout').click(function () {
|
||||||
common.logout();
|
common.logout();
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
});
|
});
|
||||||
$userAdmin.find('a.settings').click(function (e) {
|
$userAdmin.find('a.settings').click(function () {
|
||||||
if (parsed && parsed.type) {
|
if (parsed && parsed.type) {
|
||||||
window.open('/settings/');
|
window.open('/settings/');
|
||||||
} else {
|
} else {
|
||||||
window.location.href = '/settings/';
|
window.location.href = '/settings/';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$userAdmin.find('a.login').click(function (e) {
|
$userAdmin.find('a.login').click(function () {
|
||||||
if (window.location.pathname !== "/") {
|
if (window.location.pathname !== "/") {
|
||||||
sessionStorage.redirectTo = window.location.href;
|
sessionStorage.redirectTo = window.location.href;
|
||||||
}
|
}
|
||||||
window.location.href = '/login/';
|
window.location.href = '/login/';
|
||||||
});
|
});
|
||||||
$userAdmin.find('a.register').click(function (e) {
|
$userAdmin.find('a.register').click(function () {
|
||||||
if (window.location.pathname !== "/") {
|
if (window.location.pathname !== "/") {
|
||||||
sessionStorage.redirectTo = window.location.href;
|
sessionStorage.redirectTo = window.location.href;
|
||||||
}
|
}
|
||||||
@@ -1189,6 +1262,11 @@ define([
|
|||||||
if (typeof(window.Proxy) === 'undefined') {
|
if (typeof(window.Proxy) === 'undefined') {
|
||||||
feedback("NO_PROXIES");
|
feedback("NO_PROXIES");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof(Array.isArray) !== 'function') {
|
||||||
|
feedback("NO_ISARRAY");
|
||||||
|
}
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
// Race condition : if document.body is undefined when alertify.js is loaded, Alertify
|
// Race condition : if document.body is undefined when alertify.js is loaded, Alertify
|
||||||
// won't work. We have to reset it now to make sure it uses a correct "body"
|
// won't work. We have to reset it now to make sure it uses a correct "body"
|
||||||
@@ -1227,7 +1305,8 @@ define([
|
|||||||
|
|
||||||
common.arePinsSynced(function (err, yes) {
|
common.arePinsSynced(function (err, yes) {
|
||||||
if (!yes) {
|
if (!yes) {
|
||||||
common.resetPins(function (err, hash) {
|
common.resetPins(function (err) {
|
||||||
|
if (err) { console.error(err); }
|
||||||
console.log('RESET DONE');
|
console.log('RESET DONE');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
define([
|
define([
|
||||||
'/common/treesome.js',
|
'/common/treesome.js',
|
||||||
'/bower_components/rangy/rangy-core.min.js'
|
'/bower_components/rangy/rangy-core.min.js'
|
||||||
], function (Tree, Rangy, saveRestore) {
|
], function (Tree, Rangy) {
|
||||||
var log = function (x) { console.log(x); };
|
|
||||||
var error = function (x) { console.log(x); };
|
|
||||||
var verbose = function (x) { if (window.verboseMode) { console.log(x); } };
|
var verbose = function (x) { if (window.verboseMode) { console.log(x); } };
|
||||||
|
|
||||||
/* accepts the document used by the editor */
|
/* accepts the document used by the editor */
|
||||||
@@ -45,7 +43,7 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var exists = cursor.exists = function () {
|
cursor.exists = function () {
|
||||||
return (Range.start.el?1:0) | (Range.end.el?2:0);
|
return (Range.start.el?1:0) | (Range.end.el?2:0);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,7 +53,7 @@ define([
|
|||||||
2 if end
|
2 if end
|
||||||
3 if start and end
|
3 if start and end
|
||||||
*/
|
*/
|
||||||
var inNode = cursor.inNode = function (el) {
|
cursor.inNode = function (el) {
|
||||||
var state = ['start', 'end'].map(function (pos, i) {
|
var state = ['start', 'end'].map(function (pos, i) {
|
||||||
return Tree.contains(el, Range[pos].el)? i +1: 0;
|
return Tree.contains(el, Range[pos].el)? i +1: 0;
|
||||||
});
|
});
|
||||||
@@ -122,7 +120,7 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var pushDelta = cursor.pushDelta = function (oldVal, newVal, offset) {
|
cursor.pushDelta = function (oldVal, newVal) {
|
||||||
if (oldVal === newVal) { return; }
|
if (oldVal === newVal) { return; }
|
||||||
var commonStart = 0;
|
var commonStart = 0;
|
||||||
while (oldVal.charAt(commonStart) === newVal.charAt(commonStart)) {
|
while (oldVal.charAt(commonStart) === newVal.charAt(commonStart)) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
define([], function () {
|
define([], function () {
|
||||||
var exports = {};
|
var exports = {};
|
||||||
|
|
||||||
var hexToUint8Array = exports.hexToUint8Array = function (s) {
|
exports.hexToUint8Array = function (s) {
|
||||||
// if not hex or odd number of characters
|
// if not hex or odd number of characters
|
||||||
if (!/[a-fA-F0-9]+/.test(s) || s.length % 2) { throw new Error("string is not hex"); }
|
if (!/[a-fA-F0-9]+/.test(s) || s.length % 2) { throw new Error("string is not hex"); }
|
||||||
return s.split(/([0-9a-fA-F]{2})/)
|
return s.split(/([0-9a-fA-F]{2})/)
|
||||||
@@ -9,7 +9,7 @@ define([], function () {
|
|||||||
.map(function (x) { return Number('0x' + x); });
|
.map(function (x) { return Number('0x' + x); });
|
||||||
};
|
};
|
||||||
|
|
||||||
var uint8ArrayToHex = exports.uint8ArrayToHex = function (a) {
|
exports.uint8ArrayToHex = function (a) {
|
||||||
return a.reduce(function(memo, i) {
|
return a.reduce(function(memo, i) {
|
||||||
return memo + ((i < 16) ? '0' : '') + i.toString(16);
|
return memo + ((i < 16) ? '0' : '') + i.toString(16);
|
||||||
}, '');
|
}, '');
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ define([
|
|||||||
var TRASH = module.TRASH = "trash";
|
var TRASH = module.TRASH = "trash";
|
||||||
var TEMPLATE = module.TEMPLATE = "template";
|
var TEMPLATE = module.TEMPLATE = "template";
|
||||||
|
|
||||||
var init = module.init = function (files, config) {
|
module.init = function (files, config) {
|
||||||
var Cryptpad = config.Cryptpad;
|
var Cryptpad = config.Cryptpad;
|
||||||
Messages = Cryptpad.Messages;
|
Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ define([
|
|||||||
var NEW_FOLDER_NAME = Messages.fm_newFolder;
|
var NEW_FOLDER_NAME = Messages.fm_newFolder;
|
||||||
var NEW_FILE_NAME = Messages.fm_newFile;
|
var NEW_FILE_NAME = Messages.fm_newFile;
|
||||||
|
|
||||||
var DEBUG = config.DEBUG || false;
|
//var DEBUG = config.DEBUG || false;
|
||||||
var logging = function () {
|
var logging = function () {
|
||||||
console.log.apply(console, arguments);
|
console.log.apply(console, arguments);
|
||||||
};
|
};
|
||||||
@@ -34,7 +34,7 @@ define([
|
|||||||
console.error.apply(console, arguments);
|
console.error.apply(console, arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
var getStructure = exp.getStructure = function () {
|
exp.getStructure = function () {
|
||||||
var a = {};
|
var a = {};
|
||||||
a[ROOT] = {};
|
a[ROOT] = {};
|
||||||
a[UNSORTED] = [];
|
a[UNSORTED] = [];
|
||||||
@@ -92,7 +92,7 @@ define([
|
|||||||
return path[0] === TRASH && path.length === 4;
|
return path[0] === TRASH && path.length === 4;
|
||||||
};
|
};
|
||||||
|
|
||||||
var isPathInFilesData = exp.isPathInFilesData = function (path) {
|
exp.isPathInFilesData = function (path) {
|
||||||
return path[0] && path[0] === FILES_DATA;
|
return path[0] && path[0] === FILES_DATA;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ define([
|
|||||||
return typeof(element) === "string";
|
return typeof(element) === "string";
|
||||||
};
|
};
|
||||||
|
|
||||||
var isReadOnlyFile = exp.isReadOnlyFile = function (element) {
|
exp.isReadOnlyFile = function (element) {
|
||||||
if (!isFile(element)) { return false; }
|
if (!isFile(element)) { return false; }
|
||||||
var parsed = Cryptpad.parsePadUrl(element);
|
var parsed = Cryptpad.parsePadUrl(element);
|
||||||
if (!parsed) { return false; }
|
if (!parsed) { return false; }
|
||||||
@@ -114,15 +114,15 @@ define([
|
|||||||
return typeof(element) !== "string";
|
return typeof(element) !== "string";
|
||||||
};
|
};
|
||||||
|
|
||||||
var isFolderEmpty = exp.isFolderEmpty = function (element) {
|
exp.isFolderEmpty = function (element) {
|
||||||
if (typeof(element) !== "object") { return false; }
|
if (typeof(element) !== "object") { return false; }
|
||||||
return Object.keys(element).length === 0;
|
return Object.keys(element).length === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
var hasSubfolder = exp.hasSubfolder = function (element, trashRoot) {
|
exp.hasSubfolder = function (element, trashRoot) {
|
||||||
if (typeof(element) !== "object") { return false; }
|
if (typeof(element) !== "object") { return false; }
|
||||||
var subfolder = 0;
|
var subfolder = 0;
|
||||||
var addSubfolder = function (el, idx) {
|
var addSubfolder = function (el) {
|
||||||
subfolder += isFolder(el.element) ? 1 : 0;
|
subfolder += isFolder(el.element) ? 1 : 0;
|
||||||
};
|
};
|
||||||
for (var f in element) {
|
for (var f in element) {
|
||||||
@@ -137,10 +137,10 @@ define([
|
|||||||
return subfolder;
|
return subfolder;
|
||||||
};
|
};
|
||||||
|
|
||||||
var hasFile = exp.hasFile = function (element, trashRoot) {
|
exp.hasFile = function (element, trashRoot) {
|
||||||
if (typeof(element) !== "object") { return false; }
|
if (typeof(element) !== "object") { return false; }
|
||||||
var file = 0;
|
var file = 0;
|
||||||
var addFile = function (el, idx) {
|
var addFile = function (el) {
|
||||||
file += isFile(el.element) ? 1 : 0;
|
file += isFile(el.element) ? 1 : 0;
|
||||||
};
|
};
|
||||||
for (var f in element) {
|
for (var f in element) {
|
||||||
@@ -189,10 +189,10 @@ define([
|
|||||||
return inTree;
|
return inTree;
|
||||||
};
|
};
|
||||||
|
|
||||||
var isFileInTrash = function (file) {
|
/* var isFileInTrash = function (file) {
|
||||||
var inTrash = false;
|
var inTrash = false;
|
||||||
var root = files[TRASH];
|
var root = files[TRASH];
|
||||||
var filter = function (trashEl, idx) {
|
var filter = function (trashEl) {
|
||||||
inTrash = isFileInTree(file, trashEl.element);
|
inTrash = isFileInTree(file, trashEl.element);
|
||||||
return inTrash;
|
return inTrash;
|
||||||
};
|
};
|
||||||
@@ -205,11 +205,7 @@ define([
|
|||||||
if (inTrash) { break; }
|
if (inTrash) { break; }
|
||||||
}
|
}
|
||||||
return inTrash;
|
return inTrash;
|
||||||
};
|
};*/
|
||||||
|
|
||||||
var isFileInUnsorted = function (file) {
|
|
||||||
return files[UNSORTED].indexOf(file) !== -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
var getUnsortedFiles = exp.getUnsortedFiles = function () {
|
var getUnsortedFiles = exp.getUnsortedFiles = function () {
|
||||||
if (!files[UNSORTED]) {
|
if (!files[UNSORTED]) {
|
||||||
@@ -244,7 +240,7 @@ define([
|
|||||||
var getTrashFiles = exp.getTrashFiles = function () {
|
var getTrashFiles = exp.getTrashFiles = function () {
|
||||||
var root = files[TRASH];
|
var root = files[TRASH];
|
||||||
var ret = [];
|
var ret = [];
|
||||||
var addFiles = function (el, idx) {
|
var addFiles = function (el) {
|
||||||
if (isFile(el.element)) {
|
if (isFile(el.element)) {
|
||||||
if(ret.indexOf(el.element) === -1) { ret.push(el.element); }
|
if(ret.indexOf(el.element) === -1) { ret.push(el.element); }
|
||||||
} else {
|
} else {
|
||||||
@@ -261,7 +257,7 @@ define([
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
var getFilesDataFiles = exp.getFilesDataFiles = function () {
|
exp.getFilesDataFiles = function () {
|
||||||
var ret = [];
|
var ret = [];
|
||||||
files[FILES_DATA].forEach(function (el) {
|
files[FILES_DATA].forEach(function (el) {
|
||||||
if (el.href && ret.indexOf(el.href) === -1) {
|
if (el.href && ret.indexOf(el.href) === -1) {
|
||||||
@@ -351,7 +347,7 @@ define([
|
|||||||
return rootpaths.concat(unsortedpaths, templatepaths, trashpaths);
|
return rootpaths.concat(unsortedpaths, templatepaths, trashpaths);
|
||||||
};
|
};
|
||||||
|
|
||||||
var search = exp.search = function (value) {
|
exp.search = function (value) {
|
||||||
if (typeof(value) !== "string") { return []; }
|
if (typeof(value) !== "string") { return []; }
|
||||||
var res = [];
|
var res = [];
|
||||||
// Search in ROOT
|
// Search in ROOT
|
||||||
@@ -402,7 +398,7 @@ define([
|
|||||||
|
|
||||||
var ret = [];
|
var ret = [];
|
||||||
res.forEach(function (l) {
|
res.forEach(function (l) {
|
||||||
var paths = findFile(l);
|
//var paths = findFile(l);
|
||||||
ret.push({
|
ret.push({
|
||||||
paths: findFile(l),
|
paths: findFile(l),
|
||||||
data: exp.getFileData(l)
|
data: exp.getFileData(l)
|
||||||
@@ -509,7 +505,7 @@ define([
|
|||||||
files[TRASH][obj.name].splice(idx, 1);
|
files[TRASH][obj.name].splice(idx, 1);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var deleteMultiplePermanently = exp.deletePathsPermanently = function (paths) {
|
exp.deletePathsPermanently = function (paths) {
|
||||||
var hrefPaths = paths.filter(isPathInHrefArray);
|
var hrefPaths = paths.filter(isPathInHrefArray);
|
||||||
var rootPaths = paths.filter(isPathInRoot);
|
var rootPaths = paths.filter(isPathInRoot);
|
||||||
var trashPaths = paths.filter(isPathInTrash);
|
var trashPaths = paths.filter(isPathInTrash);
|
||||||
@@ -723,7 +719,7 @@ define([
|
|||||||
if (cb) { cb(); }
|
if (cb) { cb(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
var moveElements = exp.moveElements = function (paths, newParentPath, cb) {
|
exp.moveElements = function (paths, newParentPath, cb) {
|
||||||
var unsortedPaths = paths.filter(isPathInHrefArray);
|
var unsortedPaths = paths.filter(isPathInHrefArray);
|
||||||
moveHrefArrayElements(unsortedPaths, newParentPath);
|
moveHrefArrayElements(unsortedPaths, newParentPath);
|
||||||
// Copy the elements to their new location
|
// Copy the elements to their new location
|
||||||
@@ -735,7 +731,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Import elements in the file manager
|
// Import elements in the file manager
|
||||||
var importElements = exp.importElements = function (elements, path, cb) {
|
exp.importElements = function (elements, path, cb) {
|
||||||
if (!elements || elements.length === 0) { return; }
|
if (!elements || elements.length === 0) { return; }
|
||||||
var newParent = findElement(files, path);
|
var newParent = findElement(files, path);
|
||||||
if (!newParent) { debug("Trying to import elements into a non-existing folder"); return; }
|
if (!newParent) { debug("Trying to import elements into a non-existing folder"); return; }
|
||||||
@@ -748,7 +744,7 @@ define([
|
|||||||
if(cb) { cb(); }
|
if(cb) { cb(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
var createNewFolder = exp.createNewFolder = function (folderPath, name, cb) {
|
exp.createNewFolder = function (folderPath, name, cb) {
|
||||||
var parentEl = findElement(files, folderPath);
|
var parentEl = findElement(files, folderPath);
|
||||||
var folderName = getAvailableName(parentEl, name || NEW_FOLDER_NAME);
|
var folderName = getAvailableName(parentEl, name || NEW_FOLDER_NAME);
|
||||||
parentEl[folderName] = {};
|
parentEl[folderName] = {};
|
||||||
@@ -767,7 +763,7 @@ define([
|
|||||||
ctime: +new Date()
|
ctime: +new Date()
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var createNewFile = exp.createNewFile = function (filePath, name, type, cb) {
|
exp.createNewFile = function (filePath, name, type, cb) {
|
||||||
var parentEl = findElement(files, filePath);
|
var parentEl = findElement(files, filePath);
|
||||||
var fileName = getAvailableName(parentEl, name || NEW_FILE_NAME);
|
var fileName = getAvailableName(parentEl, name || NEW_FILE_NAME);
|
||||||
var href = '/' + type + '/#' + Cryptpad.createRandomHash();
|
var href = '/' + type + '/#' + Cryptpad.createRandomHash();
|
||||||
@@ -799,7 +795,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Restore an element (copy it elsewhere and remove from the trash root)
|
// Restore an element (copy it elsewhere and remove from the trash root)
|
||||||
var restoreTrash = exp.restoreTrash = function (path, cb) {
|
exp.restoreTrash = function (path, cb) {
|
||||||
if (!path || path.length !== 4 || path[0] !== TRASH) {
|
if (!path || path.length !== 4 || path[0] !== TRASH) {
|
||||||
debug("restoreTrash was called from an element not in the trash root: ", path);
|
debug("restoreTrash was called from an element not in the trash root: ", path);
|
||||||
return;
|
return;
|
||||||
@@ -838,7 +834,7 @@ define([
|
|||||||
// Remove the last element from the path to get the parent path and the element name
|
// Remove the last element from the path to get the parent path and the element name
|
||||||
var parentPath = path.slice();
|
var parentPath = path.slice();
|
||||||
var name;
|
var name;
|
||||||
var element = findElement(files, path);
|
//var element = findElement(files, path);
|
||||||
if (path.length === 4) { // Trash root
|
if (path.length === 4) { // Trash root
|
||||||
name = path[1];
|
name = path[1];
|
||||||
parentPath.pop();
|
parentPath.pop();
|
||||||
@@ -860,13 +856,13 @@ define([
|
|||||||
if(cb) { cb(); }
|
if(cb) { cb(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
var emptyTrash = exp.emptyTrash = function (cb) {
|
exp.emptyTrash = function (cb) {
|
||||||
files[TRASH] = {};
|
files[TRASH] = {};
|
||||||
checkDeletedFiles();
|
checkDeletedFiles();
|
||||||
if(cb) { cb(); }
|
if(cb) { cb(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
var deleteFileData = exp.deleteFileData = function (href, cb) {
|
exp.deleteFileData = function (href, cb) {
|
||||||
if (workgroup) { return; }
|
if (workgroup) { return; }
|
||||||
|
|
||||||
var toRemove = [];
|
var toRemove = [];
|
||||||
@@ -889,7 +885,7 @@ define([
|
|||||||
if(cb) { cb(); }
|
if(cb) { cb(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
var renameElement = exp.renameElement = function (path, newName, cb) {
|
exp.renameElement = function (path, newName, cb) {
|
||||||
if (path.length <= 1) {
|
if (path.length <= 1) {
|
||||||
logError('Renaming `root` is forbidden');
|
logError('Renaming `root` is forbidden');
|
||||||
return;
|
return;
|
||||||
@@ -914,7 +910,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var forgetPad = exp.forgetPad = function (href) {
|
exp.forgetPad = function (href) {
|
||||||
if (workgroup) { return; }
|
if (workgroup) { return; }
|
||||||
if (!href || !isFile(href)) { return; }
|
if (!href || !isFile(href)) { return; }
|
||||||
var path;
|
var path;
|
||||||
@@ -985,7 +981,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Replace a href by a stronger one everywhere in the drive (except FILES_DATA)
|
// Replace a href by a stronger one everywhere in the drive (except FILES_DATA)
|
||||||
var replaceHref = exp.replaceHref = function (o, n) {
|
exp.replaceHref = function (o, n) {
|
||||||
if (!isFile(o) || !isFile(n)) { return; }
|
if (!isFile(o) || !isFile(n)) { return; }
|
||||||
var paths = findFile(o);
|
var paths = findFile(o);
|
||||||
|
|
||||||
@@ -1012,7 +1008,7 @@ define([
|
|||||||
|
|
||||||
// addTemplate is called when we want to add a new pad, never visited, to the templates list
|
// addTemplate is called when we want to add a new pad, never visited, to the templates list
|
||||||
// first, we must add it to FILES_DATA, so the input has to be an fileDAta object
|
// first, we must add it to FILES_DATA, so the input has to be an fileDAta object
|
||||||
var addTemplate = exp.addTemplate = function (fileData) {
|
exp.addTemplate = function (fileData) {
|
||||||
if (workgroup) { return; }
|
if (workgroup) { return; }
|
||||||
if (typeof fileData !== "object" || !fileData.href || !fileData.title) {
|
if (typeof fileData !== "object" || !fileData.href || !fileData.title) {
|
||||||
console.error("filedata object expected to add a new template");
|
console.error("filedata object expected to add a new template");
|
||||||
@@ -1031,7 +1027,7 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var listTemplates = exp.listTemplates = function (type) {
|
exp.listTemplates = function () {
|
||||||
if (workgroup) { return; }
|
if (workgroup) { return; }
|
||||||
var templateFiles = getTemplateFiles();
|
var templateFiles = getTemplateFiles();
|
||||||
var res = [];
|
var res = [];
|
||||||
@@ -1049,7 +1045,7 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var fixFiles = exp.fixFiles = function () {
|
exp.fixFiles = function () {
|
||||||
// Explore the tree and check that everything is correct:
|
// Explore the tree and check that everything is correct:
|
||||||
// * 'root', 'trash', 'unsorted' and 'filesData' exist and are objects
|
// * 'root', 'trash', 'unsorted' and 'filesData' exist and are objects
|
||||||
// * ROOT: Folders are objects, files are href
|
// * ROOT: Folders are objects, files are href
|
||||||
@@ -1138,7 +1134,7 @@ define([
|
|||||||
var templateFiles = getTemplateFiles();
|
var templateFiles = getTemplateFiles();
|
||||||
var trashFiles = getTrashFiles();
|
var trashFiles = getTrashFiles();
|
||||||
var toClean = [];
|
var toClean = [];
|
||||||
fd.forEach(function (el, idx) {
|
fd.forEach(function (el) {
|
||||||
if (!el || typeof(el) !== "object") {
|
if (!el || typeof(el) !== "object") {
|
||||||
debug("An element in filesData was not an object.", el);
|
debug("An element in filesData was not an object.", el);
|
||||||
toClean.push(el);
|
toClean.push(el);
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ define([
|
|||||||
ret.removeData = filesOp.removeData;
|
ret.removeData = filesOp.removeData;
|
||||||
ret.pushData = filesOp.pushData;
|
ret.pushData = filesOp.pushData;
|
||||||
|
|
||||||
ret.addPad = function (href, path, name) {
|
ret.addPad = function (data, path) {
|
||||||
filesOp.add(href, path, name);
|
filesOp.add(data, path);
|
||||||
};
|
};
|
||||||
|
|
||||||
ret.forgetPad = function (href, cb) {
|
ret.forgetPad = function (href, cb) {
|
||||||
@@ -127,13 +127,21 @@ define([
|
|||||||
return filesOp.replace(o, n);
|
return filesOp.replace(o, n);
|
||||||
};
|
};
|
||||||
|
|
||||||
var changeHandlers = ret.changeHandlers = [];
|
ret.changeHandlers = [];
|
||||||
|
|
||||||
ret.change = function (f) {};
|
ret.change = function () {};
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var tryParsing = function (x) {
|
||||||
|
try { return JSON.parse(x); }
|
||||||
|
catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var onReady = function (f, proxy, Cryptpad, exp) {
|
var onReady = function (f, proxy, Cryptpad, exp) {
|
||||||
var fo = exp.fo = FO.init(proxy.drive, {
|
var fo = exp.fo = FO.init(proxy.drive, {
|
||||||
Cryptpad: Cryptpad
|
Cryptpad: Cryptpad
|
||||||
@@ -145,6 +153,37 @@ define([
|
|||||||
f(void 0, store);
|
f(void 0, store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var requestLogin = function () {
|
||||||
|
// log out so that you don't go into an endless loop...
|
||||||
|
Cryptpad.logout();
|
||||||
|
|
||||||
|
// redirect them to log in, and come back when they're done.
|
||||||
|
sessionStorage.redirectTo = window.location.href;
|
||||||
|
window.location.href = '/login/';
|
||||||
|
};
|
||||||
|
|
||||||
|
var tokenKey = 'loginToken';
|
||||||
|
if (Cryptpad.isLoggedIn()) {
|
||||||
|
/* This isn't truly secure, since anyone who can read the user's object can
|
||||||
|
set their local loginToken to match that in the object. However, it exposes
|
||||||
|
a UI that will work most of the time. */
|
||||||
|
|
||||||
|
// every user object should have a persistent, random number
|
||||||
|
if (typeof(proxy.loginToken) !== 'number') {
|
||||||
|
proxy[tokenKey] = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
|
||||||
|
}
|
||||||
|
|
||||||
|
var localToken = tryParsing(localStorage.getItem(tokenKey));
|
||||||
|
if (localToken === null) {
|
||||||
|
// if that number hasn't been set to localStorage, do so.
|
||||||
|
localStorage.setItem(tokenKey, proxy.loginToken);
|
||||||
|
} else if (localToken !== proxy[tokenKey]) {
|
||||||
|
// if it has been, and the local number doesn't match that in
|
||||||
|
// the user object, request that they reauthenticate.
|
||||||
|
return void requestLogin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof(proxy.allowUserFeedback) !== 'boolean') {
|
if (typeof(proxy.allowUserFeedback) !== 'boolean') {
|
||||||
proxy.allowUserFeedback = true;
|
proxy.allowUserFeedback = true;
|
||||||
}
|
}
|
||||||
@@ -157,19 +196,20 @@ define([
|
|||||||
|
|
||||||
// if the user is logged in, but does not have signing keys...
|
// if the user is logged in, but does not have signing keys...
|
||||||
if (Cryptpad.isLoggedIn() && !Cryptpad.hasSigningKeys(proxy)) {
|
if (Cryptpad.isLoggedIn() && !Cryptpad.hasSigningKeys(proxy)) {
|
||||||
// log out so that you don't go into an endless loop...
|
return void requestLogin();
|
||||||
Cryptpad.logout();
|
|
||||||
|
|
||||||
// redirect them to log in, and come back when they're done.
|
|
||||||
sessionStorage.redirectTo = window.location.href;
|
|
||||||
window.location.href = '/login/';
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy.on('change', [Cryptpad.displayNameKey], function (o, n, p) {
|
proxy.on('change', [Cryptpad.displayNameKey], function (o, n) {
|
||||||
if (typeof(n) !== "string") { return; }
|
if (typeof(n) !== "string") { return; }
|
||||||
Cryptpad.changeDisplayName(n);
|
Cryptpad.changeDisplayName(n);
|
||||||
});
|
});
|
||||||
|
proxy.on('change', [tokenKey], function () {
|
||||||
|
console.log('wut');
|
||||||
|
var localToken = tryParsing(localStorage.getItem(tokenKey));
|
||||||
|
if (localToken !== proxy[tokenKey]) {
|
||||||
|
return void requestLogin();
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var initialized = false;
|
var initialized = false;
|
||||||
@@ -197,7 +237,6 @@ define([
|
|||||||
var exp = {};
|
var exp = {};
|
||||||
|
|
||||||
window.addEventListener('storage', function (e) {
|
window.addEventListener('storage', function (e) {
|
||||||
var key = e.key;
|
|
||||||
if (e.key !== Cryptpad.userHashKey) { return; }
|
if (e.key !== Cryptpad.userHashKey) { return; }
|
||||||
var o = e.oldValue;
|
var o = e.oldValue;
|
||||||
var n = e.newValue;
|
var n = e.newValue;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ define([
|
|||||||
// 16 bytes for a deterministic channel key
|
// 16 bytes for a deterministic channel key
|
||||||
var channelSeed = dispense(16);
|
var channelSeed = dispense(16);
|
||||||
// 32 bytes for a curve key
|
// 32 bytes for a curve key
|
||||||
var curveSeed = opt.curveSeed = dispense(32);
|
opt.curveSeed = dispense(32);
|
||||||
// 32 more for a signing key
|
// 32 more for a signing key
|
||||||
var edSeed = opt.edSeed = dispense(32);
|
var edSeed = opt.edSeed = dispense(32);
|
||||||
|
|
||||||
@@ -43,9 +43,9 @@ define([
|
|||||||
// should never happen
|
// should never happen
|
||||||
if (channelHex.length !== 32) { throw new Error('invalid channel id'); }
|
if (channelHex.length !== 32) { throw new Error('invalid channel id'); }
|
||||||
|
|
||||||
var channel64 = opt.channel64 = Cryptpad.hexToBase64(channelHex);
|
opt.channel64 = Cryptpad.hexToBase64(channelHex);
|
||||||
|
|
||||||
var userHash = opt.userHash = '/1/edit/' + [opt.channel64, opt.keys.editKeyStr].join('/');
|
opt.userHash = '/1/edit/' + [opt.channel64, opt.keys.editKeyStr].join('/');
|
||||||
|
|
||||||
return opt;
|
return opt;
|
||||||
};
|
};
|
||||||
@@ -62,7 +62,7 @@ define([
|
|||||||
|
|
||||||
var rt = opt.rt = Listmap.create(config);
|
var rt = opt.rt = Listmap.create(config);
|
||||||
rt.proxy
|
rt.proxy
|
||||||
.on('ready', function (info) {
|
.on('ready', function () {
|
||||||
cb(void 0, rt);
|
cb(void 0, rt);
|
||||||
})
|
})
|
||||||
.on('disconnect', function (info) {
|
.on('disconnect', function (info) {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -46,7 +46,7 @@ define([
|
|||||||
var merge = function (obj1, obj2, keepOld) {
|
var merge = function (obj1, obj2, keepOld) {
|
||||||
if (typeof (obj1) !== "object" || typeof (obj2) !== "object") { return; }
|
if (typeof (obj1) !== "object" || typeof (obj2) !== "object") { return; }
|
||||||
Object.keys(obj2).forEach(function (k) {
|
Object.keys(obj2).forEach(function (k) {
|
||||||
var v = obj2[k];
|
//var v = obj2[k];
|
||||||
// If one of them is not an object or if we have a map and a array, don't override, create a new key
|
// If one of them is not an object or if we have a map and a array, don't override, create a new key
|
||||||
if (!obj1[k] || typeof(obj1[k]) !== "object" || typeof(obj2[k]) !== "object" ||
|
if (!obj1[k] || typeof(obj1[k]) !== "object" || typeof(obj2[k]) !== "object" ||
|
||||||
(getType(obj1[k]) !== getType(obj2[k]))) {
|
(getType(obj1[k]) !== getType(obj2[k]))) {
|
||||||
@@ -80,7 +80,7 @@ define([
|
|||||||
path.pop();
|
path.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
var p, next, nextRoot;
|
var next, nextRoot;
|
||||||
path.forEach(function (p, i) {
|
path.forEach(function (p, i) {
|
||||||
if (!root) { return; }
|
if (!root) { return; }
|
||||||
if (typeof(p) === "string") {
|
if (typeof(p) === "string") {
|
||||||
@@ -128,7 +128,7 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var mergeAnonDrive = exp.anonDriveIntoUser = function (proxy, cb) {
|
exp.anonDriveIntoUser = function (proxy, cb) {
|
||||||
// Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb
|
// Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb
|
||||||
if (!localStorage.FS_hash || !Cryptpad.isLoggedIn()) {
|
if (!localStorage.FS_hash || !Cryptpad.isLoggedIn()) {
|
||||||
if (typeof(cb) === "function") { cb(); }
|
if (typeof(cb) === "function") { cb(); }
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ define(function () {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
var extensionOf = Modes.extensionOf = function (mode) {
|
Modes.extensionOf = function (mode) {
|
||||||
var ext = '';
|
var ext = '';
|
||||||
list.some(function (o) {
|
list.some(function (o) {
|
||||||
if (o.mode !== mode) { return; }
|
if (o.mode !== mode) { return; }
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var create = Module.create = function (msg, title, icon) {
|
var create = Module.create = function (msg, title) {
|
||||||
return new Notification(title,{
|
return new Notification(title,{
|
||||||
// icon: icon,
|
// icon: icon,
|
||||||
body: msg,
|
body: msg,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var system = Module.system = function (msg, title, icon) {
|
Module.system = function (msg, title, icon) {
|
||||||
// Let's check if the browser supports notifications
|
// Let's check if the browser supports notifications
|
||||||
if (!isSupported()) { console.log("Notifications are not supported"); }
|
if (!isSupported()) { console.log("Notifications are not supported"); }
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var tab = Module.tab = function (frequency, count) {
|
Module.tab = function (frequency, count) {
|
||||||
var key = '_pendingTabNotification';
|
var key = '_pendingTabNotification';
|
||||||
|
|
||||||
var favicon = document.getElementById('favicon');
|
var favicon = document.getElementById('favicon');
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
define([
|
define([
|
||||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||||
], function () {
|
], function () {
|
||||||
var MAX_LAG_BEFORE_TIMEOUT = 30000;
|
|
||||||
var Nacl = window.nacl;
|
var Nacl = window.nacl;
|
||||||
|
|
||||||
var uid = function () {
|
var uid = function () {
|
||||||
@@ -102,9 +101,16 @@ types of messages:
|
|||||||
timeouts: {}, // timeouts
|
timeouts: {}, // timeouts
|
||||||
pending: {}, // callbacks
|
pending: {}, // callbacks
|
||||||
cookie: null,
|
cookie: null,
|
||||||
|
connected: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var send = function (type, msg, cb) {
|
var send = function (type, msg, cb) {
|
||||||
|
if (!ctx.connected && type !== 'COOKIE') {
|
||||||
|
return void window.setTimeout(function () {
|
||||||
|
cb('DISCONNECTED');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// construct a signed message...
|
// construct a signed message...
|
||||||
|
|
||||||
var data = [type, msg];
|
var data = [type, msg];
|
||||||
@@ -123,11 +129,22 @@ types of messages:
|
|||||||
return sendMsg(ctx, data, cb);
|
return sendMsg(ctx, data, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
network.on('message', function (msg, sender) {
|
network.on('message', function (msg) {
|
||||||
onMsg(ctx, msg);
|
onMsg(ctx, msg);
|
||||||
});
|
});
|
||||||
|
|
||||||
send('COOKIE', "", function (e, msg) {
|
network.on('disconnect', function () {
|
||||||
|
ctx.connected = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
network.on('reconnect', function () {
|
||||||
|
send('COOKIE', "", function (e) {
|
||||||
|
if (e) { return void cb(e); }
|
||||||
|
ctx.connected = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
send('COOKIE', "", function (e) {
|
||||||
if (e) { return void cb(e); }
|
if (e) { return void cb(e); }
|
||||||
// callback to provide 'send' method to whatever needs it
|
// callback to provide 'send' method to whatever needs it
|
||||||
cb(void 0, { send: send, });
|
cb(void 0, { send: send, });
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ define([
|
|||||||
/** Id of the div containing the lag info. */
|
/** Id of the div containing the lag info. */
|
||||||
var LAG_ELEM_CLS = Bar.constants.lag = 'cryptpad-lag';
|
var LAG_ELEM_CLS = Bar.constants.lag = 'cryptpad-lag';
|
||||||
|
|
||||||
|
var LIMIT_ELEM_CLS = Bar.constants.lag = 'cryptpad-limit';
|
||||||
|
|
||||||
/** The toolbar class which contains the user list, debug link and lag. */
|
/** The toolbar class which contains the user list, debug link and lag. */
|
||||||
var TOOLBAR_CLS = Bar.constants.toolbar = 'cryptpad-toolbar';
|
var TOOLBAR_CLS = Bar.constants.toolbar = 'cryptpad-toolbar';
|
||||||
|
|
||||||
@@ -94,14 +96,14 @@ define([
|
|||||||
|
|
||||||
var createSpinner = function ($container, config) {
|
var createSpinner = function ($container, config) {
|
||||||
if (config.displayed.indexOf('spinner') !== -1) {
|
if (config.displayed.indexOf('spinner') !== -1) {
|
||||||
var $spin = $('<span>');
|
var $spin = $('<span>', {'class':SPINNER_CLS});
|
||||||
var $spinner = $('<span>', {
|
var $spinner = $('<span>', {
|
||||||
id: uid(),
|
id: uid(),
|
||||||
'class': SPINNER_CLS + ' spin fa fa-spinner fa-pulse',
|
'class': 'spin fa fa-spinner fa-pulse',
|
||||||
}).appendTo($spin).hide();
|
}).appendTo($spin).hide();
|
||||||
$('<span>', {
|
$('<span>', {
|
||||||
id: uid(),
|
id: uid(),
|
||||||
'class': SPINNER_CLS + ' synced fa fa-check',
|
'class': 'synced fa fa-check',
|
||||||
title: Messages.synced
|
title: Messages.synced
|
||||||
}).appendTo($spin);
|
}).appendTo($spin);
|
||||||
$container.prepend($spin);
|
$container.prepend($spin);
|
||||||
@@ -205,6 +207,13 @@ define([
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (hashes.fileHash) {
|
||||||
|
options.push({
|
||||||
|
tag: 'a',
|
||||||
|
attributes: {title: Messages.viewShareTitle, 'class': 'fileShare'},
|
||||||
|
content: '<span class="fa fa-eye"></span> ' + Messages.viewShare
|
||||||
|
});
|
||||||
|
}
|
||||||
var dropdownConfigShare = {
|
var dropdownConfigShare = {
|
||||||
text: $('<div>').append($shareIcon).append($span).html(),
|
text: $('<div>').append($shareIcon).append($span).html(),
|
||||||
options: options
|
options: options
|
||||||
@@ -223,7 +232,14 @@ define([
|
|||||||
}
|
}
|
||||||
if (hashes.viewHash) {
|
if (hashes.viewHash) {
|
||||||
$shareBlock.find('a.viewShare').click(function () {
|
$shareBlock.find('a.viewShare').click(function () {
|
||||||
var url = window.location.origin + window.location.pathname + '#' + hashes.viewHash;
|
var url = window.location.origin + window.location.pathname + '#' + hashes.viewHash ;
|
||||||
|
var success = Cryptpad.Clipboard.copy(url);
|
||||||
|
if (success) { Cryptpad.log(Messages.shareSuccess); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (hashes.fileHash) {
|
||||||
|
$shareBlock.find('a.fileShare').click(function () {
|
||||||
|
var url = window.location.origin + window.location.pathname + '#' + hashes.fileHash ;
|
||||||
var success = Cryptpad.Clipboard.copy(url);
|
var success = Cryptpad.Clipboard.copy(url);
|
||||||
if (success) { Cryptpad.log(Messages.shareSuccess); }
|
if (success) { Cryptpad.log(Messages.shareSuccess); }
|
||||||
});
|
});
|
||||||
@@ -372,7 +388,7 @@ define([
|
|||||||
'class': LAG_ELEM_CLS,
|
'class': LAG_ELEM_CLS,
|
||||||
id: uid(),
|
id: uid(),
|
||||||
});
|
});
|
||||||
var $a = $('<span>', {id: 'newLag'});
|
var $a = $('<span>', {'class': 'cryptpad-lag', id: 'newLag'});
|
||||||
$('<span>', {'class': 'bar1'}).appendTo($a);
|
$('<span>', {'class': 'bar1'}).appendTo($a);
|
||||||
$('<span>', {'class': 'bar2'}).appendTo($a);
|
$('<span>', {'class': 'bar2'}).appendTo($a);
|
||||||
$('<span>', {'class': 'bar3'}).appendTo($a);
|
$('<span>', {'class': 'bar3'}).appendTo($a);
|
||||||
@@ -391,7 +407,7 @@ define([
|
|||||||
var title;
|
var title;
|
||||||
var $lag = $(lagElement);
|
var $lag = $(lagElement);
|
||||||
if (lag) {
|
if (lag) {
|
||||||
$lag.attr('class', '');
|
$lag.attr('class', 'cryptpad-lag');
|
||||||
firstConnection = false;
|
firstConnection = false;
|
||||||
title = Messages.lag + ' : ' + lag + ' ms\n';
|
title = Messages.lag + ' : ' + lag + ' ms\n';
|
||||||
if (lag > 30000) {
|
if (lag > 30000) {
|
||||||
@@ -412,7 +428,7 @@ define([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!firstConnection) {
|
else if (!firstConnection) {
|
||||||
$lag.attr('class', '');
|
$lag.attr('class', 'cryptpad-lag');
|
||||||
// Display the red light at the 2nd failed attemp to get the lag
|
// Display the red light at the 2nd failed attemp to get the lag
|
||||||
lagLight.addClass('lag-red');
|
lagLight.addClass('lag-red');
|
||||||
title = Messages.redLight;
|
title = Messages.redLight;
|
||||||
@@ -474,6 +490,24 @@ define([
|
|||||||
$userContainer.append($lag);
|
$userContainer.append($lag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.displayed.indexOf('limit') !== -1 && Config.enablePinning) {
|
||||||
|
var usage;
|
||||||
|
var $limitIcon = $('<span>', {'class': 'fa fa-exclamation-triangle'});
|
||||||
|
var $limit = $('<span>', {
|
||||||
|
'class': LIMIT_ELEM_CLS,
|
||||||
|
'title': Messages.pinLimitReached
|
||||||
|
}).append($limitIcon).hide().appendTo($userContainer);
|
||||||
|
var todo = function (e, overLimit) {
|
||||||
|
if (e) { return void console.error("Unable to get the pinned usage"); }
|
||||||
|
if (overLimit) {
|
||||||
|
$limit.show().click(function () {
|
||||||
|
Cryptpad.alert(Messages.pinLimitReachedAlert, null, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Cryptpad.isOverPinLimit(todo);
|
||||||
|
}
|
||||||
|
|
||||||
if (config.displayed.indexOf('newpad') !== -1) {
|
if (config.displayed.indexOf('newpad') !== -1) {
|
||||||
var pads_options = [];
|
var pads_options = [];
|
||||||
Config.availablePadTypes.forEach(function (p) {
|
Config.availablePadTypes.forEach(function (p) {
|
||||||
@@ -504,7 +538,7 @@ define([
|
|||||||
// User dropdown
|
// User dropdown
|
||||||
if (config.displayed.indexOf('useradmin') !== -1) {
|
if (config.displayed.indexOf('useradmin') !== -1) {
|
||||||
var userMenuCfg = {};
|
var userMenuCfg = {};
|
||||||
if (config.userData) {
|
if (!config.hideDisplayName) {
|
||||||
userMenuCfg = {
|
userMenuCfg = {
|
||||||
displayNameCls: USERNAME_CLS,
|
displayNameCls: USERNAME_CLS,
|
||||||
changeNameButtonCls: USERBUTTON_CLS,
|
changeNameButtonCls: USERBUTTON_CLS,
|
||||||
@@ -524,7 +558,8 @@ define([
|
|||||||
$userButton.click(function (e) {
|
$userButton.click(function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
Cryptpad.getLastName(function (lastName) {
|
Cryptpad.getLastName(function (err, lastName) {
|
||||||
|
if (err) { return void console.error("Cannot get last name", err); }
|
||||||
Cryptpad.prompt(Messages.changeNamePrompt, lastName || '', function (newName) {
|
Cryptpad.prompt(Messages.changeNamePrompt, lastName || '', function (newName) {
|
||||||
if (newName === null && typeof(lastName) === "string") { return; }
|
if (newName === null && typeof(lastName) === "string") { return; }
|
||||||
if (newName === null) { newName = ''; }
|
if (newName === null) { newName = ''; }
|
||||||
@@ -791,7 +826,7 @@ define([
|
|||||||
if (!connected) { return; }
|
if (!connected) { return; }
|
||||||
checkLag(getLag, lagElement);
|
checkLag(getLag, lagElement);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
} else { connected = true; }
|
||||||
|
|
||||||
var failed = function () {
|
var failed = function () {
|
||||||
connected = false;
|
connected = false;
|
||||||
|
|||||||
905
www/common/toolbar2.js
Normal file
905
www/common/toolbar2.js
Normal file
@@ -0,0 +1,905 @@
|
|||||||
|
define([
|
||||||
|
'jquery',
|
||||||
|
'/customize/application_config.js',
|
||||||
|
'/api/config'
|
||||||
|
], function ($, Config, ApiConfig) {
|
||||||
|
var Messages = {};
|
||||||
|
var Cryptpad;
|
||||||
|
|
||||||
|
var Bar = {
|
||||||
|
constants: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
var SPINNER_DISAPPEAR_TIME = 3000;
|
||||||
|
|
||||||
|
// Toolbar parts
|
||||||
|
var TOOLBAR_CLS = Bar.constants.toolbar = 'cryptpad-toolbar';
|
||||||
|
var TOP_CLS = Bar.constants.top = 'cryptpad-toolbar-top';
|
||||||
|
var LEFTSIDE_CLS = Bar.constants.leftside = 'cryptpad-toolbar-leftside';
|
||||||
|
var RIGHTSIDE_CLS = Bar.constants.rightside = 'cryptpad-toolbar-rightside';
|
||||||
|
var HISTORY_CLS = Bar.constants.history = 'cryptpad-toolbar-history';
|
||||||
|
|
||||||
|
// Userlist
|
||||||
|
var USERLIST_CLS = Bar.constants.userlist = "cryptpad-dropdown-users";
|
||||||
|
var EDITSHARE_CLS = Bar.constants.editShare = "cryptpad-dropdown-editShare";
|
||||||
|
var VIEWSHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-viewShare";
|
||||||
|
var SHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-share";
|
||||||
|
|
||||||
|
// Top parts
|
||||||
|
var USER_CLS = Bar.constants.userAdmin = "cryptpad-user";
|
||||||
|
var SPINNER_CLS = Bar.constants.spinner = 'cryptpad-spinner';
|
||||||
|
var STATE_CLS = Bar.constants.state = 'cryptpad-state';
|
||||||
|
var LAG_CLS = Bar.constants.lag = 'cryptpad-lag';
|
||||||
|
var LIMIT_CLS = Bar.constants.lag = 'cryptpad-limit';
|
||||||
|
var TITLE_CLS = Bar.constants.title = "cryptpad-title";
|
||||||
|
var NEWPAD_CLS = Bar.constants.newpad = "cryptpad-newpad";
|
||||||
|
|
||||||
|
// User admin menu
|
||||||
|
var USERADMIN_CLS = Bar.constants.user = 'cryptpad-user-dropdown';
|
||||||
|
var USERNAME_CLS = Bar.constants.username = 'cryptpad-toolbar-username';
|
||||||
|
var READONLY_CLS = Bar.constants.readonly = 'cryptpad-readonly';
|
||||||
|
var USERBUTTON_CLS = Bar.constants.changeUsername = "cryptpad-change-username";
|
||||||
|
|
||||||
|
// Create the toolbar element
|
||||||
|
|
||||||
|
var uid = function () {
|
||||||
|
return 'cryptpad-uid-' + String(Math.random()).substring(2);
|
||||||
|
};
|
||||||
|
|
||||||
|
var styleToolbar = function ($container, href, version) {
|
||||||
|
href = href || '/customize/toolbar.css' + (version?('?' + version): '');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: href,
|
||||||
|
dataType: 'text',
|
||||||
|
success: function (data) {
|
||||||
|
$container.append($('<style>').text(data));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var createRealtimeToolbar = function (config) {
|
||||||
|
if (!config.$container) { return; }
|
||||||
|
var $container = config.$container;
|
||||||
|
var $toolbar = $('<div>', {
|
||||||
|
'class': TOOLBAR_CLS,
|
||||||
|
id: uid(),
|
||||||
|
});
|
||||||
|
|
||||||
|
var $topContainer = $('<div>', {'class': TOP_CLS});
|
||||||
|
var $userContainer = $('<span>', {
|
||||||
|
'class': USER_CLS
|
||||||
|
}).appendTo($topContainer);
|
||||||
|
$('<span>', {'class': SPINNER_CLS}).hide().appendTo($userContainer);
|
||||||
|
$('<span>', {'class': STATE_CLS}).hide().appendTo($userContainer);
|
||||||
|
$('<span>', {'class': LAG_CLS}).hide().appendTo($userContainer);
|
||||||
|
$('<span>', {'class': LIMIT_CLS}).hide().appendTo($userContainer);
|
||||||
|
$('<span>', {'class': NEWPAD_CLS + ' dropdown-bar'}).hide().appendTo($userContainer);
|
||||||
|
$('<span>', {'class': USERADMIN_CLS + ' dropdown-bar'}).hide().appendTo($userContainer);
|
||||||
|
|
||||||
|
$toolbar.append($topContainer)
|
||||||
|
.append($('<div>', {'class': LEFTSIDE_CLS}))
|
||||||
|
.append($('<div>', {'class': RIGHTSIDE_CLS}))
|
||||||
|
.append($('<div>', {'class': HISTORY_CLS}));
|
||||||
|
|
||||||
|
// The 'notitle' class removes the line added for the title with a small screen
|
||||||
|
if (!config.title || typeof config.title !== "object") {
|
||||||
|
$toolbar.addClass('notitle');
|
||||||
|
}
|
||||||
|
|
||||||
|
$container.prepend($toolbar);
|
||||||
|
|
||||||
|
if (ApiConfig && ApiConfig.requireConf && ApiConfig.requireConf.urlArgs) {
|
||||||
|
styleToolbar($container, undefined, ApiConfig.requireConf.urlArgs);
|
||||||
|
} else {
|
||||||
|
styleToolbar($container);
|
||||||
|
}
|
||||||
|
return $toolbar;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Userlist elements
|
||||||
|
|
||||||
|
var checkSynchronizing = function (toolbar, config) {
|
||||||
|
if (!toolbar.state) { return; }
|
||||||
|
var userList = config.userList.list.users;
|
||||||
|
var userNetfluxId = config.userList.userNetfluxId;
|
||||||
|
var meIdx = userList.indexOf(userNetfluxId);
|
||||||
|
if (meIdx === -1) {
|
||||||
|
toolbar.state.text(Messages.synchronizing);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
toolbar.state.text('');
|
||||||
|
};
|
||||||
|
var getOtherUsers = function(config) {
|
||||||
|
var userList = config.userList.list.users;
|
||||||
|
var userData = config.userList.data;
|
||||||
|
var userNetfluxId = config.userList.userNetfluxId;
|
||||||
|
|
||||||
|
var i = 0; // duplicates counter
|
||||||
|
var list = [];
|
||||||
|
|
||||||
|
// Display only one time each user (if he is connected in multiple tabs)
|
||||||
|
var myUid = userData[userNetfluxId] ? userData[userNetfluxId].uid : undefined;
|
||||||
|
var uids = [];
|
||||||
|
userList.forEach(function(user) {
|
||||||
|
if (user !== userNetfluxId) {
|
||||||
|
var data = userData[user] || {};
|
||||||
|
var userName = data.name;
|
||||||
|
var userId = data.uid;
|
||||||
|
if (userName && uids.indexOf(userId) === -1 && (!myUid || userId !== myUid)) {
|
||||||
|
uids.push(userId);
|
||||||
|
list.push(userName);
|
||||||
|
} else if (userName) { i++; }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
list: list,
|
||||||
|
duplicates: i
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var arrayIntersect = function(a, b) {
|
||||||
|
return $.grep(a, function(i) {
|
||||||
|
return $.inArray(i, b) > -1;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var updateUserList = function (toolbar, config) {
|
||||||
|
// Make sure the elements are displayed
|
||||||
|
var $userButtons = toolbar.userlist;
|
||||||
|
|
||||||
|
var userList = config.userList.list.users;
|
||||||
|
var userData = config.userList.data;
|
||||||
|
var userNetfluxId = config.userList.userNetfluxId;
|
||||||
|
|
||||||
|
var numberOfUsers = userList.length;
|
||||||
|
|
||||||
|
// If we are using old pads (readonly unavailable), only editing users are in userList.
|
||||||
|
// With new pads, we also have readonly users in userList, so we have to intersect with
|
||||||
|
// the userData to have only the editing users. We can't use userData directly since it
|
||||||
|
// may contain data about users that have already left the channel.
|
||||||
|
userList = config.readOnly === -1 ? userList : arrayIntersect(userList, Object.keys(userData));
|
||||||
|
|
||||||
|
// Names of editing users
|
||||||
|
var others = getOtherUsers(config);
|
||||||
|
var editUsersNames = others.list;
|
||||||
|
var duplicates = others.duplicates; // Number of duplicates
|
||||||
|
|
||||||
|
var numberOfEditUsers = userList.length - duplicates;
|
||||||
|
var numberOfViewUsers = numberOfUsers - userList.length;
|
||||||
|
|
||||||
|
// Number of anonymous editing users
|
||||||
|
var anonymous = numberOfEditUsers - editUsersNames.length;
|
||||||
|
|
||||||
|
// Update the userlist
|
||||||
|
var $usersTitle = $('<h2>').text(Messages.users);
|
||||||
|
var $editUsers = $userButtons.find('.' + USERLIST_CLS);
|
||||||
|
$editUsers.html('').append($usersTitle);
|
||||||
|
|
||||||
|
var $editUsersList = $('<pre>');
|
||||||
|
// Yourself (edit only)
|
||||||
|
if (config.readOnly !== 1) {
|
||||||
|
$editUsers.append('<span class="yourself">' + Messages.yourself + '</span>');
|
||||||
|
anonymous--;
|
||||||
|
}
|
||||||
|
// Editors
|
||||||
|
$editUsersList.text(editUsersNames.join('\n')); // .text() to avoid XSS
|
||||||
|
$editUsers.append($editUsersList);
|
||||||
|
// Anonymous editors
|
||||||
|
if (anonymous > 0) {
|
||||||
|
var text = anonymous === 1 ? Messages.anonymousUser : Messages.anonymousUsers;
|
||||||
|
$editUsers.append('<span class="anonymous">' + anonymous + ' ' + text + '</span>');
|
||||||
|
}
|
||||||
|
// Viewers
|
||||||
|
if (numberOfViewUsers > 0) {
|
||||||
|
var viewText = '<span class="viewer">';
|
||||||
|
if (numberOfEditUsers > 0) {
|
||||||
|
$editUsers.append('<br>');
|
||||||
|
viewText += Messages.and + ' ';
|
||||||
|
}
|
||||||
|
var viewerText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer;
|
||||||
|
viewText += numberOfViewUsers + ' ' + viewerText + '</span>';
|
||||||
|
$editUsers.append(viewText);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the buttons
|
||||||
|
var fa_editusers = '<span class="fa fa-users"></span>';
|
||||||
|
var fa_viewusers = '<span class="fa fa-eye"></span>';
|
||||||
|
var viewersText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer;
|
||||||
|
var editorsText = numberOfEditUsers !== 1 ? Messages.editors : Messages.editor;
|
||||||
|
var $span = $('<span>', {'class': 'large'}).html(fa_editusers + ' ' + numberOfEditUsers + ' ' + editorsText + ' ' + fa_viewusers + ' ' + numberOfViewUsers + ' ' + viewersText);
|
||||||
|
var $spansmall = $('<span>', {'class': 'narrow'}).html(fa_editusers + ' ' + numberOfEditUsers + ' ' + fa_viewusers + ' ' + numberOfViewUsers);
|
||||||
|
$userButtons.find('.buttonTitle').html('').append($span).append($spansmall);
|
||||||
|
|
||||||
|
// Change username in useradmin dropdown
|
||||||
|
if (config.displayed.indexOf('useradmin') !== -1) {
|
||||||
|
var $userAdminElement = toolbar.$userAdmin;
|
||||||
|
var $userElement = $userAdminElement.find('.' + USERNAME_CLS);
|
||||||
|
$userElement.show();
|
||||||
|
if (config.readOnly === 1) {
|
||||||
|
$userElement.addClass(READONLY_CLS).text(Messages.readonly);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var name = userData[userNetfluxId] && userData[userNetfluxId].name;
|
||||||
|
if (!name) {
|
||||||
|
name = Messages.anonymous;
|
||||||
|
}
|
||||||
|
$userElement.removeClass(READONLY_CLS).text(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var initUserList = function (toolbar, config) {
|
||||||
|
if (config.userList && config.userList.list && config.userList.userNetfluxId) {
|
||||||
|
var userList = config.userList.list;
|
||||||
|
userList.change.push(function () {
|
||||||
|
var users = userList.users;
|
||||||
|
if (users.indexOf(config.userList.userNetfluxId) !== -1) {toolbar.connected = true;}
|
||||||
|
if (!toolbar.connected) { return; }
|
||||||
|
checkSynchronizing(toolbar, config);
|
||||||
|
if (config.userList.data) {
|
||||||
|
updateUserList(toolbar, config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Create sub-elements
|
||||||
|
|
||||||
|
var createUserList = function (toolbar, config) {
|
||||||
|
if (!config.userList || !config.userList.list ||
|
||||||
|
!config.userList.data || !config.userList.userNetfluxId) {
|
||||||
|
throw new Error("You must provide a `userList` object to display the userlist");
|
||||||
|
}
|
||||||
|
var dropdownConfig = {
|
||||||
|
options: [{
|
||||||
|
tag: 'p',
|
||||||
|
attributes: {'class': USERLIST_CLS},
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
var $block = Cryptpad.createDropdown(dropdownConfig);
|
||||||
|
$block.attr('id', 'userButtons');
|
||||||
|
toolbar.$leftside.prepend($block);
|
||||||
|
|
||||||
|
return $block;
|
||||||
|
};
|
||||||
|
|
||||||
|
var createShare = function (toolbar, config) {
|
||||||
|
var secret = Cryptpad.find(config, ['share', 'secret']);
|
||||||
|
var channel = Cryptpad.find(config, ['share', 'channel']);
|
||||||
|
if (!secret || !channel) {
|
||||||
|
throw new Error("Unable to display the share button: share.secret and share.channel required");
|
||||||
|
}
|
||||||
|
Cryptpad.getRecentPads(function (err, recent) {
|
||||||
|
var $shareIcon = $('<span>', {'class': 'fa fa-share-alt'});
|
||||||
|
var $span = $('<span>', {'class': 'large'}).append(' ' +Messages.shareButton);
|
||||||
|
var hashes = Cryptpad.getHashes(channel, secret);
|
||||||
|
var options = [];
|
||||||
|
|
||||||
|
// If we have a stronger version in drive, add it and add a redirect button
|
||||||
|
var stronger = recent && Cryptpad.findStronger(null, recent);
|
||||||
|
if (stronger) {
|
||||||
|
var parsed = Cryptpad.parsePadUrl(stronger);
|
||||||
|
hashes.editHash = parsed.hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hashes.editHash) {
|
||||||
|
options.push({
|
||||||
|
tag: 'a',
|
||||||
|
attributes: {title: Messages.editShareTitle, 'class': 'editShare'},
|
||||||
|
content: '<span class="fa fa-users"></span> ' + Messages.editShare
|
||||||
|
});
|
||||||
|
if (stronger) {
|
||||||
|
// We're in view mode, display the "open editing link" button
|
||||||
|
options.push({
|
||||||
|
tag: 'a',
|
||||||
|
attributes: {
|
||||||
|
title: Messages.editOpenTitle,
|
||||||
|
'class': 'editOpen',
|
||||||
|
href: window.location.pathname + '#' + hashes.editHash,
|
||||||
|
target: '_blank'
|
||||||
|
},
|
||||||
|
content: '<span class="fa fa-users"></span> ' + Messages.editOpen
|
||||||
|
});
|
||||||
|
}
|
||||||
|
options.push({tag: 'hr'});
|
||||||
|
}
|
||||||
|
if (hashes.viewHash) {
|
||||||
|
options.push({
|
||||||
|
tag: 'a',
|
||||||
|
attributes: {title: Messages.viewShareTitle, 'class': 'viewShare'},
|
||||||
|
content: '<span class="fa fa-eye"></span> ' + Messages.viewShare
|
||||||
|
});
|
||||||
|
if (hashes.editHash && !stronger) {
|
||||||
|
// We're in edit mode, display the "open readonly" button
|
||||||
|
options.push({
|
||||||
|
tag: 'a',
|
||||||
|
attributes: {
|
||||||
|
title: Messages.viewOpenTitle,
|
||||||
|
'class': 'viewOpen',
|
||||||
|
href: window.location.pathname + '#' + hashes.viewHash,
|
||||||
|
target: '_blank'
|
||||||
|
},
|
||||||
|
content: '<span class="fa fa-eye"></span> ' + Messages.viewOpen
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hashes.fileHash) {
|
||||||
|
options.push({
|
||||||
|
tag: 'a',
|
||||||
|
attributes: {title: Messages.viewShareTitle, 'class': 'fileShare'},
|
||||||
|
content: '<span class="fa fa-eye"></span> ' + Messages.viewShare
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var dropdownConfigShare = {
|
||||||
|
text: $('<div>').append($shareIcon).append($span).html(),
|
||||||
|
options: options
|
||||||
|
};
|
||||||
|
var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare);
|
||||||
|
$shareBlock.find('button').attr('id', 'shareButton');
|
||||||
|
$shareBlock.find('.dropdown-bar-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS);
|
||||||
|
|
||||||
|
if (hashes.editHash) {
|
||||||
|
$shareBlock.find('a.editShare').click(function () {
|
||||||
|
var url = window.location.origin + window.location.pathname + '#' + hashes.editHash;
|
||||||
|
var success = Cryptpad.Clipboard.copy(url);
|
||||||
|
if (success) { Cryptpad.log(Messages.shareSuccess); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (hashes.viewHash) {
|
||||||
|
$shareBlock.find('a.viewShare').click(function () {
|
||||||
|
var url = window.location.origin + window.location.pathname + '#' + hashes.viewHash ;
|
||||||
|
var success = Cryptpad.Clipboard.copy(url);
|
||||||
|
if (success) { Cryptpad.log(Messages.shareSuccess); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (hashes.fileHash) {
|
||||||
|
$shareBlock.find('a.fileShare').click(function () {
|
||||||
|
var url = window.location.origin + window.location.pathname + '#' + hashes.fileHash ;
|
||||||
|
var success = Cryptpad.Clipboard.copy(url);
|
||||||
|
if (success) { Cryptpad.log(Messages.shareSuccess); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbar.$leftside.append($shareBlock);
|
||||||
|
toolbar.share = $shareBlock;
|
||||||
|
});
|
||||||
|
|
||||||
|
return "Loading share button";
|
||||||
|
};
|
||||||
|
|
||||||
|
var createFileShare = function (toolbar) {
|
||||||
|
if (!window.location.hash) {
|
||||||
|
throw new Error("Unable to display the share button: hash required in the URL");
|
||||||
|
}
|
||||||
|
var $shareIcon = $('<span>', {'class': 'fa fa-share-alt'});
|
||||||
|
var $span = $('<span>', {'class': 'large'}).append(' ' +Messages.shareButton);
|
||||||
|
var $button = $('<button>', {'id': 'shareButton'}).append($shareIcon).append($span);
|
||||||
|
$button.click(function () {
|
||||||
|
var url = window.location.href;
|
||||||
|
var success = Cryptpad.Clipboard.copy(url);
|
||||||
|
if (success) { Cryptpad.log(Messages.shareSuccess); }
|
||||||
|
});
|
||||||
|
|
||||||
|
toolbar.$leftside.append($button);
|
||||||
|
return $button;
|
||||||
|
};
|
||||||
|
|
||||||
|
var createTitle = function (toolbar, config) {
|
||||||
|
var $titleContainer = $('<span>', {
|
||||||
|
id: 'toolbarTitle',
|
||||||
|
'class': TITLE_CLS
|
||||||
|
}).appendTo(toolbar.$top);
|
||||||
|
|
||||||
|
// TODO: move these functions to toolbar or common?
|
||||||
|
if (typeof config.title !== "object") {
|
||||||
|
console.error("config.title", config);
|
||||||
|
throw new Error("config.title is not an object");
|
||||||
|
}
|
||||||
|
var callback = config.title.onRename;
|
||||||
|
var placeholder = config.title.defaultName;
|
||||||
|
var suggestName = config.title.suggestName;
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
var $text = $('<span>', {
|
||||||
|
'class': 'title'
|
||||||
|
}).appendTo($titleContainer);
|
||||||
|
var $pencilIcon = $('<span>', {
|
||||||
|
'class': 'pencilIcon',
|
||||||
|
'title': Messages.clickToEdit
|
||||||
|
});
|
||||||
|
if (config.readOnly === 1 || typeof(Cryptpad) === "undefined") { return $titleContainer; }
|
||||||
|
var $input = $('<input>', {
|
||||||
|
type: 'text',
|
||||||
|
placeholder: placeholder
|
||||||
|
}).appendTo($titleContainer).hide();
|
||||||
|
if (config.readOnly !== 1) {
|
||||||
|
$text.attr("title", Messages.clickToEdit);
|
||||||
|
$text.addClass("editable");
|
||||||
|
var $icon = $('<span>', {
|
||||||
|
'class': 'fa fa-pencil readonly',
|
||||||
|
style: 'font-family: FontAwesome;'
|
||||||
|
});
|
||||||
|
$pencilIcon.append($icon).appendTo($titleContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events
|
||||||
|
$input.on('mousedown', function (e) {
|
||||||
|
if (!$input.is(":focus")) {
|
||||||
|
$input.focus();
|
||||||
|
}
|
||||||
|
e.stopPropagation();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
$input.on('keyup', function (e) {
|
||||||
|
if (e.which === 13 && toolbar.connected === true) {
|
||||||
|
var name = $input.val().trim();
|
||||||
|
if (name === "") {
|
||||||
|
name = $input.attr('placeholder');
|
||||||
|
}
|
||||||
|
Cryptpad.renamePad(name, function (err, newtitle) {
|
||||||
|
if (err) { return; }
|
||||||
|
$text.text(newtitle);
|
||||||
|
callback(null, newtitle);
|
||||||
|
$input.hide();
|
||||||
|
$text.show();
|
||||||
|
//$pencilIcon.css('display', '');
|
||||||
|
});
|
||||||
|
} else if (e.which === 27) {
|
||||||
|
$input.hide();
|
||||||
|
$text.show();
|
||||||
|
//$pencilIcon.css('display', '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var displayInput = function () {
|
||||||
|
if (toolbar.connected === false) { return; }
|
||||||
|
$text.hide();
|
||||||
|
//$pencilIcon.css('display', 'none');
|
||||||
|
var inputVal = suggestName() || "";
|
||||||
|
$input.val(inputVal);
|
||||||
|
$input.show();
|
||||||
|
$input.focus();
|
||||||
|
};
|
||||||
|
$text.on('click', displayInput);
|
||||||
|
$pencilIcon.on('click', displayInput);
|
||||||
|
return $titleContainer;
|
||||||
|
};
|
||||||
|
|
||||||
|
var createLinkToMain = function (toolbar) {
|
||||||
|
var $linkContainer = $('<span>', {
|
||||||
|
'class': "cryptpad-link"
|
||||||
|
}).appendTo(toolbar.$top);
|
||||||
|
var $imgTag = $('<img>', {
|
||||||
|
src: "/customize/cryptofist_mini.png",
|
||||||
|
alt: "Cryptpad"
|
||||||
|
});
|
||||||
|
|
||||||
|
// We need to override the "a" tag action here because it is inside the iframe!
|
||||||
|
var $aTagSmall = $('<a>', {
|
||||||
|
href: "/",
|
||||||
|
title: Messages.header_logoTitle,
|
||||||
|
'class': "cryptpad-logo"
|
||||||
|
}).append($imgTag);
|
||||||
|
var $span = $('<span>').text('CryptPad');
|
||||||
|
var $aTagBig = $aTagSmall.clone().addClass('large').append($span);
|
||||||
|
$aTagSmall.addClass('narrow');
|
||||||
|
var onClick = function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.ctrlKey) {
|
||||||
|
window.open('/');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.location = "/";
|
||||||
|
};
|
||||||
|
|
||||||
|
var onContext = function (e) { e.stopPropagation(); };
|
||||||
|
|
||||||
|
$aTagBig.click(onClick).contextmenu(onContext);
|
||||||
|
$aTagSmall.click(onClick).contextmenu(onContext);
|
||||||
|
|
||||||
|
$linkContainer.append($aTagSmall).append($aTagBig);
|
||||||
|
|
||||||
|
return $linkContainer;
|
||||||
|
};
|
||||||
|
|
||||||
|
var checkLag = function (toolbar, config, $lagEl) {
|
||||||
|
var lag;
|
||||||
|
var $lag = $lagEl || toolbar.lag;
|
||||||
|
if (!$lag) { return; }
|
||||||
|
var getLag = config.network.getLag;
|
||||||
|
if(typeof getLag === "function") {
|
||||||
|
lag = getLag();
|
||||||
|
}
|
||||||
|
var lagLight = $('<div>', {
|
||||||
|
'class': 'lag'
|
||||||
|
});
|
||||||
|
var title;
|
||||||
|
if (lag && toolbar.connected) {
|
||||||
|
$lag.attr('class', LAG_CLS);
|
||||||
|
toolbar.firstConnection = false;
|
||||||
|
title = Messages.lag + ' : ' + lag + ' ms\n';
|
||||||
|
if (lag > 30000) {
|
||||||
|
$lag.addClass('lag0');
|
||||||
|
title = Messages.redLight;
|
||||||
|
} else if (lag > 5000) {
|
||||||
|
$lag.addClass('lag1');
|
||||||
|
title += Messages.orangeLight;
|
||||||
|
} else if (lag > 1000) {
|
||||||
|
$lag.addClass('lag2');
|
||||||
|
title += Messages.orangeLight;
|
||||||
|
} else if (lag > 300) {
|
||||||
|
$lag.addClass('lag3');
|
||||||
|
title += Messages.greenLight;
|
||||||
|
} else {
|
||||||
|
$lag.addClass('lag4');
|
||||||
|
title += Messages.greenLight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!toolbar.firstConnection) {
|
||||||
|
$lag.attr('class', LAG_CLS);
|
||||||
|
// Display the red light at the 2nd failed attemp to get the lag
|
||||||
|
lagLight.addClass('lag-red');
|
||||||
|
title = Messages.redLight;
|
||||||
|
}
|
||||||
|
if (title) {
|
||||||
|
$lag.attr('title', title);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var createLag = function (toolbar, config) {
|
||||||
|
var $a = toolbar.$userAdmin.find('.'+LAG_CLS).show();
|
||||||
|
$('<span>', {'class': 'bar1'}).appendTo($a);
|
||||||
|
$('<span>', {'class': 'bar2'}).appendTo($a);
|
||||||
|
$('<span>', {'class': 'bar3'}).appendTo($a);
|
||||||
|
$('<span>', {'class': 'bar4'}).appendTo($a);
|
||||||
|
if (config.realtime) {
|
||||||
|
checkLag(toolbar, config, $a);
|
||||||
|
setInterval(function () {
|
||||||
|
if (!toolbar.connected) { return; }
|
||||||
|
checkLag(toolbar, config);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
return $a;
|
||||||
|
};
|
||||||
|
|
||||||
|
var kickSpinner = function (toolbar, config, local) {
|
||||||
|
if (!toolbar.spinner) { return; }
|
||||||
|
var $spin = toolbar.spinner;
|
||||||
|
$spin.find('.spin').show();
|
||||||
|
$spin.find('.synced').hide();
|
||||||
|
var onSynced = function () {
|
||||||
|
if ($spin.timeout) { clearTimeout($spin.timeout); }
|
||||||
|
$spin.timeout = setTimeout(function () {
|
||||||
|
$spin.find('.spin').hide();
|
||||||
|
$spin.find('.synced').show();
|
||||||
|
}, local ? 0 : SPINNER_DISAPPEAR_TIME);
|
||||||
|
};
|
||||||
|
if (Cryptpad) {
|
||||||
|
Cryptpad.whenRealtimeSyncs(config.realtime, onSynced);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSynced();
|
||||||
|
};
|
||||||
|
var ks = function (toolbar, config, local) {
|
||||||
|
return function () {
|
||||||
|
if (toolbar.connected) { kickSpinner(toolbar, config, local); }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var createSpinner = function (toolbar, config) {
|
||||||
|
var $spin = toolbar.$userAdmin.find('.'+SPINNER_CLS).show();
|
||||||
|
$('<span>', {
|
||||||
|
id: uid(),
|
||||||
|
'class': 'spin fa fa-spinner fa-pulse',
|
||||||
|
}).appendTo($spin).hide();
|
||||||
|
$('<span>', {
|
||||||
|
id: uid(),
|
||||||
|
'class': 'synced fa fa-check',
|
||||||
|
title: Messages.synced
|
||||||
|
}).appendTo($spin);
|
||||||
|
toolbar.$userAdmin.prepend($spin);
|
||||||
|
if (config.realtime) {
|
||||||
|
config.realtime.onPatch(ks(toolbar, config));
|
||||||
|
config.realtime.onMessage(ks(toolbar, config, true));
|
||||||
|
}
|
||||||
|
return $spin;
|
||||||
|
};
|
||||||
|
|
||||||
|
var createState = function (toolbar) {
|
||||||
|
return toolbar.$userAdmin.find('.'+STATE_CLS).text(Messages.synchronizing).show();
|
||||||
|
};
|
||||||
|
|
||||||
|
var createLimit = function (toolbar) {
|
||||||
|
if (!Config.enablePinning) { return; }
|
||||||
|
var $limitIcon = $('<span>', {'class': 'fa fa-exclamation-triangle'});
|
||||||
|
var $limit = toolbar.$userAdmin.find('.'+LIMIT_CLS).attr({
|
||||||
|
'title': Messages.pinLimitReached
|
||||||
|
}).append($limitIcon).hide();
|
||||||
|
var todo = function (e, overLimit) {
|
||||||
|
if (e) { return void console.error("Unable to get the pinned usage"); }
|
||||||
|
if (overLimit) {
|
||||||
|
$limit.show().click(function () {
|
||||||
|
Cryptpad.alert(Messages.pinLimitReachedAlert, null, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Cryptpad.isOverPinLimit(todo);
|
||||||
|
return $limit;
|
||||||
|
};
|
||||||
|
|
||||||
|
var createNewPad = function (toolbar) {
|
||||||
|
var $newPad = toolbar.$userAdmin.find('.'+NEWPAD_CLS).show();
|
||||||
|
|
||||||
|
var pads_options = [];
|
||||||
|
Config.availablePadTypes.forEach(function (p) {
|
||||||
|
if (p === 'drive') { return; }
|
||||||
|
pads_options.push({
|
||||||
|
tag: 'a',
|
||||||
|
attributes: {
|
||||||
|
'target': '_blank',
|
||||||
|
'href': '/' + p + '/',
|
||||||
|
},
|
||||||
|
content: Messages.type[p]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var $plusIcon = $('<span>', {'class': 'fa fa-plus'});
|
||||||
|
var $newbig = $('<span>', {'class': 'big'}).append(' ' +Messages.newButton);
|
||||||
|
var $newButton = $('<div>').append($plusIcon).append($newbig);
|
||||||
|
var dropdownConfig = {
|
||||||
|
text: $newButton.html(), // Button initial text
|
||||||
|
options: pads_options, // Entries displayed in the menu
|
||||||
|
left: true, // Open to the left of the button,
|
||||||
|
container: $newPad
|
||||||
|
};
|
||||||
|
var $newPadBlock = Cryptpad.createDropdown(dropdownConfig);
|
||||||
|
$newPadBlock.find('button').attr('title', Messages.newButtonTitle);
|
||||||
|
$newPadBlock.find('button').attr('id', 'newdoc');
|
||||||
|
return $newPadBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
var createUserAdmin = function (toolbar, config) {
|
||||||
|
var $userAdmin = toolbar.$userAdmin.find('.'+USERADMIN_CLS).show();
|
||||||
|
var userMenuCfg = {
|
||||||
|
$initBlock: $userAdmin
|
||||||
|
};
|
||||||
|
if (!config.hideDisplayName) { // TODO: config.userAdmin.hideDisplayName?
|
||||||
|
$.extend(true, userMenuCfg, {
|
||||||
|
displayNameCls: USERNAME_CLS,
|
||||||
|
changeNameButtonCls: USERBUTTON_CLS,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (config.readOnly !== 1) {
|
||||||
|
userMenuCfg.displayName = 1;
|
||||||
|
userMenuCfg.displayChangeName = 1;
|
||||||
|
}
|
||||||
|
Cryptpad.createUserAdminMenu(userMenuCfg);
|
||||||
|
|
||||||
|
var $userButton = toolbar.$userNameButton = $userAdmin.find('a.' + USERBUTTON_CLS);
|
||||||
|
$userButton.click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
Cryptpad.getLastName(function (err, lastName) {
|
||||||
|
if (err) { return void console.error("Cannot get last name", err); }
|
||||||
|
Cryptpad.prompt(Messages.changeNamePrompt, lastName || '', function (newName) {
|
||||||
|
if (newName === null && typeof(lastName) === "string") { return; }
|
||||||
|
if (newName === null) { newName = ''; }
|
||||||
|
Cryptpad.changeDisplayName(newName);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Cryptpad.onDisplayNameChanged(function () {
|
||||||
|
Cryptpad.findCancelButton().click();
|
||||||
|
});
|
||||||
|
|
||||||
|
return $userAdmin;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Events
|
||||||
|
var initClickEvents = function (toolbar, config) {
|
||||||
|
var removeDropdowns = function () {
|
||||||
|
toolbar.$toolbar.find('.cryptpad-dropdown').hide();
|
||||||
|
};
|
||||||
|
var cancelEditTitle = function (e) {
|
||||||
|
// Now we want to apply the title even if we click somewhere else
|
||||||
|
if ($(e.target).parents('.' + TITLE_CLS).length || !toolbar.title) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var $title = toolbar.title;
|
||||||
|
if (!$title.find('input').is(':visible')) { return; }
|
||||||
|
|
||||||
|
// Press enter
|
||||||
|
var ev = $.Event("keyup");
|
||||||
|
ev.which = 13;
|
||||||
|
$title.find('input').trigger(ev);
|
||||||
|
};
|
||||||
|
// Click in the main window
|
||||||
|
var w = config.ifrw || window;
|
||||||
|
$(w).on('click', removeDropdowns);
|
||||||
|
$(w).on('click', cancelEditTitle);
|
||||||
|
// Click in iframes
|
||||||
|
try {
|
||||||
|
if (w.$ && w.$('iframe').length) {
|
||||||
|
config.ifrw.$('iframe').each(function (i, el) {
|
||||||
|
$(el.contentWindow).on('click', removeDropdowns);
|
||||||
|
$(el.contentWindow).on('click', cancelEditTitle);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// empty try catch in case this iframe is problematic
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
var initNotifications = function (toolbar, config) {
|
||||||
|
// Display notifications when users are joining/leaving the session
|
||||||
|
var oldUserData;
|
||||||
|
if (!config.userList || !config.userList.list || !config.userList.userNetfluxId) { return; }
|
||||||
|
var userList = config.userList.list;
|
||||||
|
var userNetfluxId = config.userList.userNetfluxId;
|
||||||
|
if (typeof Cryptpad !== "undefined" && userList) {
|
||||||
|
var notify = function(type, name, oldname) {
|
||||||
|
// type : 1 (+1 user), 0 (rename existing user), -1 (-1 user)
|
||||||
|
if (typeof name === "undefined") { return; }
|
||||||
|
name = name || Messages.anonymous;
|
||||||
|
switch(type) {
|
||||||
|
case 1:
|
||||||
|
Cryptpad.log(Messages._getKey("notifyJoined", [name]));
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
oldname = (oldname === "") ? Messages.anonymous : oldname;
|
||||||
|
Cryptpad.log(Messages._getKey("notifyRenamed", [oldname, name]));
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
Cryptpad.log(Messages._getKey("notifyLeft", [name]));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("Invalid type of notification");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var userPresent = function (id, user, data) {
|
||||||
|
if (!(user && user.uid)) {
|
||||||
|
console.log('no uid');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
console.log('no data');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var count = 0;
|
||||||
|
Object.keys(data).forEach(function (k) {
|
||||||
|
if (data[k] && data[k].uid === user.uid) { count++; }
|
||||||
|
});
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
|
userList.change.push(function (newdata) {
|
||||||
|
// Notify for disconnected users
|
||||||
|
if (typeof oldUserData !== "undefined") {
|
||||||
|
for (var u in oldUserData) {
|
||||||
|
// if a user's uid is still present after having left, don't notify
|
||||||
|
if (userList.users.indexOf(u) === -1) {
|
||||||
|
var temp = JSON.parse(JSON.stringify(oldUserData[u]));
|
||||||
|
delete oldUserData[u];
|
||||||
|
if (userPresent(u, temp, newdata || oldUserData) < 1) {
|
||||||
|
notify(-1, temp.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update the "oldUserData" object and notify for new users and names changed
|
||||||
|
if (typeof newdata === "undefined") { return; }
|
||||||
|
if (typeof oldUserData === "undefined") {
|
||||||
|
oldUserData = JSON.parse(JSON.stringify(newdata));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (config.readOnly === 0 && !oldUserData[userNetfluxId]) {
|
||||||
|
oldUserData = JSON.parse(JSON.stringify(newdata));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (var k in newdata) {
|
||||||
|
if (k !== userNetfluxId && userList.users.indexOf(k) !== -1) {
|
||||||
|
if (typeof oldUserData[k] === "undefined") {
|
||||||
|
// if the same uid is already present in the userdata, don't notify
|
||||||
|
if (!userPresent(k, newdata[k], oldUserData)) {
|
||||||
|
notify(1, newdata[k].name);
|
||||||
|
}
|
||||||
|
} else if (oldUserData[k].name !== newdata[k].name) {
|
||||||
|
notify(0, newdata[k].name, oldUserData[k].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oldUserData = JSON.parse(JSON.stringify(newdata));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Main
|
||||||
|
|
||||||
|
Bar.create = function (cfg) {
|
||||||
|
var config = cfg || {};
|
||||||
|
Cryptpad = config.common;
|
||||||
|
Messages = Cryptpad.Messages;
|
||||||
|
config.readOnly = (typeof config.readOnly !== "undefined") ? (config.readOnly ? 1 : 0) : -1;
|
||||||
|
config.displayed = config.displayed || [];
|
||||||
|
config.network = cfg.network || Cryptpad.getNetwork();
|
||||||
|
|
||||||
|
var toolbar = {};
|
||||||
|
|
||||||
|
toolbar.connected = false;
|
||||||
|
toolbar.firstConnection = true;
|
||||||
|
|
||||||
|
var $toolbar = toolbar.$toolbar = createRealtimeToolbar(config);
|
||||||
|
toolbar.$leftside = $toolbar.find('.'+Bar.constants.leftside);
|
||||||
|
toolbar.$rightside = $toolbar.find('.'+Bar.constants.rightside);
|
||||||
|
toolbar.$top = $toolbar.find('.'+Bar.constants.top);
|
||||||
|
toolbar.$history = $toolbar.find('.'+Bar.constants.history);
|
||||||
|
|
||||||
|
toolbar.$userAdmin = $toolbar.find('.'+Bar.constants.userAdmin);
|
||||||
|
|
||||||
|
// Create the subelements
|
||||||
|
var tb = {};
|
||||||
|
tb['userlist'] = createUserList;
|
||||||
|
tb['share'] = createShare;
|
||||||
|
tb['fileshare'] = createFileShare;
|
||||||
|
tb['title'] = createTitle;
|
||||||
|
tb['lag'] = createLag;
|
||||||
|
tb['spinner'] = createSpinner;
|
||||||
|
tb['state'] = createState;
|
||||||
|
tb['limit'] = createLimit;
|
||||||
|
tb['newpad'] = createNewPad;
|
||||||
|
tb['useradmin'] = createUserAdmin;
|
||||||
|
|
||||||
|
|
||||||
|
var addElement = toolbar.addElement = function (arr, additionnalCfg, init) {
|
||||||
|
if (typeof additionnalCfg === "object") { $.extend(true, config, additionnalCfg); }
|
||||||
|
arr.forEach(function (el) {
|
||||||
|
if (typeof el !== "string" || !el.trim()) { return; }
|
||||||
|
if (typeof tb[el] === "function") {
|
||||||
|
if (!init && config.displayed.indexOf(el) !== -1) { return; } // Already done
|
||||||
|
toolbar[el] = tb[el](toolbar, config);
|
||||||
|
if (!init) { config.displayed.push(el); }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
addElement(config.displayed, {}, true);
|
||||||
|
initUserList(toolbar, config);
|
||||||
|
|
||||||
|
toolbar['linkToMain'] = createLinkToMain(toolbar, config);
|
||||||
|
|
||||||
|
if (!config.realtime) { toolbar.connected = true; }
|
||||||
|
|
||||||
|
initClickEvents(toolbar, config);
|
||||||
|
initNotifications(toolbar, config);
|
||||||
|
|
||||||
|
var failed = toolbar.failed = function () {
|
||||||
|
toolbar.connected = false;
|
||||||
|
if (toolbar.state) {
|
||||||
|
toolbar.state.text(Messages.disconnected);
|
||||||
|
}
|
||||||
|
checkLag(toolbar, config);
|
||||||
|
};
|
||||||
|
toolbar.reconnecting = function (userId) {
|
||||||
|
if (config.userList) { config.userList.userNetfluxId = userId; }
|
||||||
|
toolbar.connected = false;
|
||||||
|
if (toolbar.state) {
|
||||||
|
toolbar.state.text(Messages.reconnecting);
|
||||||
|
}
|
||||||
|
checkLag(toolbar, config);
|
||||||
|
};
|
||||||
|
|
||||||
|
// On log out, remove permanently the realtime elements of the toolbar
|
||||||
|
Cryptpad.onLogout(function () {
|
||||||
|
failed();
|
||||||
|
if (toolbar.useradmin) { toolbar.useradmin.hide(); }
|
||||||
|
if (toolbar.userlist) { toolbar.userlist.hide(); }
|
||||||
|
});
|
||||||
|
|
||||||
|
return toolbar;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Bar;
|
||||||
|
});
|
||||||
@@ -71,7 +71,7 @@ define([], function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var orderOfNodes = tree.orderOfNodes = function (a, b, root) {
|
tree.orderOfNodes = function (a, b, root) {
|
||||||
// b might not be supplied
|
// b might not be supplied
|
||||||
if (!b) { return; }
|
if (!b) { return; }
|
||||||
// a and b might be the same element
|
// a and b might be the same element
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
define([
|
define([
|
||||||
'jquery',
|
'jquery',
|
||||||
], function ($) {
|
'/customize/application_config.js'
|
||||||
|
], function ($, AppConfig) {
|
||||||
var module = {};
|
var module = {};
|
||||||
|
|
||||||
var ROOT = module.ROOT = "root";
|
var ROOT = module.ROOT = "root";
|
||||||
@@ -8,7 +9,7 @@ define([
|
|||||||
var TRASH = module.TRASH = "trash";
|
var TRASH = module.TRASH = "trash";
|
||||||
var TEMPLATE = module.TEMPLATE = "template";
|
var TEMPLATE = module.TEMPLATE = "template";
|
||||||
|
|
||||||
var init = module.init = function (files, config) {
|
module.init = function (files, config) {
|
||||||
var exp = {};
|
var exp = {};
|
||||||
var Cryptpad = config.Cryptpad;
|
var Cryptpad = config.Cryptpad;
|
||||||
var Messages = Cryptpad.Messages;
|
var Messages = Cryptpad.Messages;
|
||||||
@@ -18,7 +19,6 @@ define([
|
|||||||
var NEW_FILE_NAME = Messages.fm_newFile;
|
var NEW_FILE_NAME = Messages.fm_newFile;
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
var DEBUG = config.DEBUG || false;
|
|
||||||
var logging = function () {
|
var logging = function () {
|
||||||
console.log.apply(console, arguments);
|
console.log.apply(console, arguments);
|
||||||
};
|
};
|
||||||
@@ -38,17 +38,16 @@ define([
|
|||||||
* UTILS
|
* UTILS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var getStructure = exp.getStructure = function () {
|
exp.getStructure = function () {
|
||||||
var a = {};
|
var a = {};
|
||||||
a[ROOT] = {};
|
a[ROOT] = {};
|
||||||
a[UNSORTED] = [];
|
|
||||||
a[TRASH] = {};
|
a[TRASH] = {};
|
||||||
a[FILES_DATA] = [];
|
a[FILES_DATA] = [];
|
||||||
a[TEMPLATE] = [];
|
a[TEMPLATE] = [];
|
||||||
return a;
|
return a;
|
||||||
};
|
};
|
||||||
var getHrefArray = function () {
|
var getHrefArray = function () {
|
||||||
return [UNSORTED, TEMPLATE];
|
return [TEMPLATE];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -58,7 +57,7 @@ define([
|
|||||||
return typeof(element) === "string";
|
return typeof(element) === "string";
|
||||||
};
|
};
|
||||||
|
|
||||||
var isReadOnlyFile = exp.isReadOnlyFile = function (element) {
|
exp.isReadOnlyFile = function (element) {
|
||||||
if (!isFile(element)) { return false; }
|
if (!isFile(element)) { return false; }
|
||||||
var parsed = Cryptpad.parsePadUrl(element);
|
var parsed = Cryptpad.parsePadUrl(element);
|
||||||
if (!parsed) { return false; }
|
if (!parsed) { return false; }
|
||||||
@@ -69,17 +68,17 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
var isFolder = exp.isFolder = function (element) {
|
var isFolder = exp.isFolder = function (element) {
|
||||||
return typeof(element) !== "string";
|
return typeof(element) === "object";
|
||||||
};
|
};
|
||||||
var isFolderEmpty = exp.isFolderEmpty = function (element) {
|
exp.isFolderEmpty = function (element) {
|
||||||
if (typeof(element) !== "object") { return false; }
|
if (typeof(element) !== "object") { return false; }
|
||||||
return Object.keys(element).length === 0;
|
return Object.keys(element).length === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
var hasSubfolder = exp.hasSubfolder = function (element, trashRoot) {
|
exp.hasSubfolder = function (element, trashRoot) {
|
||||||
if (typeof(element) !== "object") { return false; }
|
if (typeof(element) !== "object") { return false; }
|
||||||
var subfolder = 0;
|
var subfolder = 0;
|
||||||
var addSubfolder = function (el, idx) {
|
var addSubfolder = function (el) {
|
||||||
subfolder += isFolder(el.element) ? 1 : 0;
|
subfolder += isFolder(el.element) ? 1 : 0;
|
||||||
};
|
};
|
||||||
for (var f in element) {
|
for (var f in element) {
|
||||||
@@ -94,10 +93,10 @@ define([
|
|||||||
return subfolder;
|
return subfolder;
|
||||||
};
|
};
|
||||||
|
|
||||||
var hasFile = exp.hasFile = function (element, trashRoot) {
|
exp.hasFile = function (element, trashRoot) {
|
||||||
if (typeof(element) !== "object") { return false; }
|
if (typeof(element) !== "object") { return false; }
|
||||||
var file = 0;
|
var file = 0;
|
||||||
var addFile = function (el, idx) {
|
var addFile = function (el) {
|
||||||
file += isFile(el.element) ? 1 : 0;
|
file += isFile(el.element) ? 1 : 0;
|
||||||
};
|
};
|
||||||
for (var f in element) {
|
for (var f in element) {
|
||||||
@@ -232,7 +231,7 @@ define([
|
|||||||
_getFiles[TRASH] = function () {
|
_getFiles[TRASH] = function () {
|
||||||
var root = files[TRASH];
|
var root = files[TRASH];
|
||||||
var ret = [];
|
var ret = [];
|
||||||
var addFiles = function (el, idx) {
|
var addFiles = function (el) {
|
||||||
if (isFile(el.element)) {
|
if (isFile(el.element)) {
|
||||||
if(ret.indexOf(el.element) === -1) { ret.push(el.element); }
|
if(ret.indexOf(el.element) === -1) { ret.push(el.element); }
|
||||||
} else {
|
} else {
|
||||||
@@ -297,6 +296,9 @@ define([
|
|||||||
|
|
||||||
return paths;
|
return paths;
|
||||||
};
|
};
|
||||||
|
exp.findFileInRoot = function (href) {
|
||||||
|
return _findFileInRoot([ROOT], href);
|
||||||
|
};
|
||||||
var _findFileInHrefArray = function (rootName, href) {
|
var _findFileInHrefArray = function (rootName, href) {
|
||||||
var unsorted = files[rootName].slice();
|
var unsorted = files[rootName].slice();
|
||||||
var ret = [];
|
var ret = [];
|
||||||
@@ -345,12 +347,11 @@ define([
|
|||||||
};
|
};
|
||||||
var findFile = exp.findFile = function (href) {
|
var findFile = exp.findFile = function (href) {
|
||||||
var rootpaths = _findFileInRoot([ROOT], href);
|
var rootpaths = _findFileInRoot([ROOT], href);
|
||||||
var unsortedpaths = _findFileInHrefArray(UNSORTED, href);
|
|
||||||
var templatepaths = _findFileInHrefArray(TEMPLATE, href);
|
var templatepaths = _findFileInHrefArray(TEMPLATE, href);
|
||||||
var trashpaths = _findFileInTrash([TRASH], href);
|
var trashpaths = _findFileInTrash([TRASH], href);
|
||||||
return rootpaths.concat(unsortedpaths, templatepaths, trashpaths);
|
return rootpaths.concat(templatepaths, trashpaths);
|
||||||
};
|
};
|
||||||
var search = exp.search = function (value) {
|
exp.search = function (value) {
|
||||||
if (typeof(value) !== "string") { return []; }
|
if (typeof(value) !== "string") { return []; }
|
||||||
var res = [];
|
var res = [];
|
||||||
// Search in ROOT
|
// Search in ROOT
|
||||||
@@ -401,7 +402,7 @@ define([
|
|||||||
|
|
||||||
var ret = [];
|
var ret = [];
|
||||||
res.forEach(function (l) {
|
res.forEach(function (l) {
|
||||||
var paths = findFile(l);
|
//var paths = findFile(l);
|
||||||
ret.push({
|
ret.push({
|
||||||
paths: findFile(l),
|
paths: findFile(l),
|
||||||
data: exp.getFileData(l)
|
data: exp.getFileData(l)
|
||||||
@@ -426,19 +427,24 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
// FILES DATA
|
// FILES DATA
|
||||||
var pushFileData = exp.pushData = function (data) {
|
var pushFileData = exp.pushData = function (data, cb) {
|
||||||
Cryptpad.pinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e, hash) {
|
if (typeof cb !== "function") { cb = function () {}; }
|
||||||
if (e) { console.log(e); return; }
|
var todo = function () {
|
||||||
console.log(hash);
|
files[FILES_DATA].push(data);
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
if (!Cryptpad.isLoggedIn() || !AppConfig.enablePinning) { return void todo(); }
|
||||||
|
Cryptpad.pinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e) {
|
||||||
|
if (e) { return void cb(e); }
|
||||||
|
todo();
|
||||||
});
|
});
|
||||||
files[FILES_DATA].push(data);
|
|
||||||
};
|
};
|
||||||
var spliceFileData = exp.removeData = function (idx) {
|
var spliceFileData = exp.removeData = function (idx) {
|
||||||
var data = files[FILES_DATA][idx];
|
var data = files[FILES_DATA][idx];
|
||||||
if (typeof data === "object") {
|
if (typeof data === "object" && Cryptpad.isLoggedIn() && AppConfig.enablePinning) {
|
||||||
Cryptpad.unpinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e, hash) {
|
Cryptpad.unpinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e, hash) {
|
||||||
if (e) { console.log(e); return; }
|
if (e) { return void logError(e); }
|
||||||
console.log(hash);
|
debug('UNPIN', hash);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
files[FILES_DATA].splice(idx, 1);
|
files[FILES_DATA].splice(idx, 1);
|
||||||
@@ -524,7 +530,7 @@ define([
|
|||||||
});
|
});
|
||||||
exp.delete(toRemove, cb);
|
exp.delete(toRemove, cb);
|
||||||
};
|
};
|
||||||
var restore = exp.restore = function (path, cb) {
|
exp.restore = function (path, cb) {
|
||||||
if (!isInTrashRoot(path)) { return; }
|
if (!isInTrashRoot(path)) { return; }
|
||||||
var parentPath = path.slice();
|
var parentPath = path.slice();
|
||||||
parentPath.pop();
|
parentPath.pop();
|
||||||
@@ -534,8 +540,10 @@ define([
|
|||||||
|
|
||||||
|
|
||||||
// ADD
|
// ADD
|
||||||
var add = exp.add = function (href, path, name, cb) {
|
var add = exp.add = function (data, path) {
|
||||||
if (!href) { return; }
|
if (!data || typeof(data) !== "object") { return; }
|
||||||
|
var href = data.href;
|
||||||
|
var name = data.title;
|
||||||
var newPath = path, parentEl;
|
var newPath = path, parentEl;
|
||||||
if (path && !Array.isArray(path)) {
|
if (path && !Array.isArray(path)) {
|
||||||
newPath = decodeURIComponent(path).split(',');
|
newPath = decodeURIComponent(path).split(',');
|
||||||
@@ -546,53 +554,50 @@ define([
|
|||||||
parentEl.push(href);
|
parentEl.push(href);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Add to root
|
// Add to root if path is ROOT or if no path
|
||||||
if (path && isPathIn(newPath, [ROOT]) && name) {
|
var filesList = getFiles([ROOT, TRASH, 'hrefArray']);
|
||||||
parentEl = find(newPath);
|
if ((path && isPathIn(newPath, [ROOT]) || filesList.indexOf(href) === -1) && name) {
|
||||||
|
parentEl = find(newPath || [ROOT]);
|
||||||
if (parentEl) {
|
if (parentEl) {
|
||||||
var newName = getAvailableName(parentEl, name);
|
var newName = getAvailableName(parentEl, name);
|
||||||
parentEl[newName] = href;
|
parentEl[newName] = href;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// No path: push to unsorted
|
|
||||||
var filesList = getFiles([ROOT, TRASH, 'hrefArray']);
|
|
||||||
if (filesList.indexOf(href) === -1) { files[UNSORTED].push(href); }
|
|
||||||
|
|
||||||
if (typeof cb === "function") { cb(); }
|
|
||||||
};
|
};
|
||||||
var addFile = exp.addFile = function (filePath, name, type, cb) {
|
exp.addFile = function (filePath, name, type, cb) {
|
||||||
var parentEl = findElement(files, filePath);
|
var parentEl = findElement(files, filePath);
|
||||||
var fileName = getAvailableName(parentEl, name || NEW_FILE_NAME);
|
var fileName = getAvailableName(parentEl, name || NEW_FILE_NAME);
|
||||||
var href = '/' + type + '/#' + Cryptpad.createRandomHash();
|
var href = '/' + type + '/#' + Cryptpad.createRandomHash();
|
||||||
parentEl[fileName] = href;
|
|
||||||
|
|
||||||
pushFileData({
|
pushFileData({
|
||||||
href: href,
|
href: href,
|
||||||
title: fileName,
|
title: fileName,
|
||||||
atime: +new Date(),
|
atime: +new Date(),
|
||||||
ctime: +new Date()
|
ctime: +new Date()
|
||||||
});
|
}, function (err) {
|
||||||
|
if (err) { return void cb(err); }
|
||||||
var newPath = filePath.slice();
|
parentEl[fileName] = href;
|
||||||
newPath.push(fileName);
|
var newPath = filePath.slice();
|
||||||
cb({
|
newPath.push(fileName);
|
||||||
newPath: newPath
|
cb(void 0, {
|
||||||
|
newPath: newPath
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var addFolder = exp.addFolder = function (folderPath, name, cb) {
|
exp.addFolder = function (folderPath, name, cb) {
|
||||||
var parentEl = find(folderPath);
|
var parentEl = find(folderPath);
|
||||||
var folderName = getAvailableName(parentEl, name || NEW_FOLDER_NAME);
|
var folderName = getAvailableName(parentEl, name || NEW_FOLDER_NAME);
|
||||||
parentEl[folderName] = {};
|
parentEl[folderName] = {};
|
||||||
var newPath = folderPath.slice();
|
var newPath = folderPath.slice();
|
||||||
newPath.push(folderName);
|
newPath.push(folderName);
|
||||||
cb({
|
cb(void 0, {
|
||||||
newPath: newPath
|
newPath: newPath
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// FORGET (move with href not path)
|
// FORGET (move with href not path)
|
||||||
var forget = exp.forget = function (href) {
|
exp.forget = function (href) {
|
||||||
var paths = findFile(href);
|
var paths = findFile(href);
|
||||||
move(paths, [TRASH]);
|
move(paths, [TRASH]);
|
||||||
};
|
};
|
||||||
@@ -601,6 +606,10 @@ define([
|
|||||||
// Permanently delete multiple files at once using a list of paths
|
// Permanently delete multiple files at once using a list of paths
|
||||||
// NOTE: We have to be careful when removing elements from arrays (trash root, unsorted or template)
|
// NOTE: We have to be careful when removing elements from arrays (trash root, unsorted or template)
|
||||||
var removePadAttribute = function (f) {
|
var removePadAttribute = function (f) {
|
||||||
|
if (typeof(f) !== 'string') {
|
||||||
|
console.error("Can't find pad attribute for an undefined pad");
|
||||||
|
return;
|
||||||
|
}
|
||||||
Object.keys(files).forEach(function (key) {
|
Object.keys(files).forEach(function (key) {
|
||||||
var hash = f.indexOf('#') !== -1 ? f.slice(f.indexOf('#') + 1) : null;
|
var hash = f.indexOf('#') !== -1 ? f.slice(f.indexOf('#') + 1) : null;
|
||||||
if (hash && key.indexOf(hash) === 0) {
|
if (hash && key.indexOf(hash) === 0) {
|
||||||
@@ -690,18 +699,18 @@ define([
|
|||||||
// FILES_DATA (replaceHref)
|
// FILES_DATA (replaceHref)
|
||||||
if (!nocheck) { checkDeletedFiles(); }
|
if (!nocheck) { checkDeletedFiles(); }
|
||||||
};
|
};
|
||||||
var deletePath = exp.delete = function (paths, cb, nocheck) {
|
exp.delete = function (paths, cb, nocheck) {
|
||||||
deleteMultiplePermanently(paths, nocheck);
|
deleteMultiplePermanently(paths, nocheck);
|
||||||
if (typeof cb === "function") { cb(); }
|
if (typeof cb === "function") { cb(); }
|
||||||
};
|
};
|
||||||
var emptyTrash = exp.emptyTrash = function (cb) {
|
exp.emptyTrash = function (cb) {
|
||||||
files[TRASH] = {};
|
files[TRASH] = {};
|
||||||
checkDeletedFiles();
|
checkDeletedFiles();
|
||||||
if(cb) { cb(); }
|
if(cb) { cb(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
// RENAME
|
// RENAME
|
||||||
var rename = exp.rename = function (path, newName, cb) {
|
exp.rename = function (path, newName, cb) {
|
||||||
if (path.length <= 1) {
|
if (path.length <= 1) {
|
||||||
logError('Renaming `root` is forbidden');
|
logError('Renaming `root` is forbidden');
|
||||||
return;
|
return;
|
||||||
@@ -722,7 +731,7 @@ define([
|
|||||||
parentEl[newName] = element;
|
parentEl[newName] = element;
|
||||||
parentEl[oldName] = undefined;
|
parentEl[oldName] = undefined;
|
||||||
delete parentEl[oldName];
|
delete parentEl[oldName];
|
||||||
cb();
|
if (typeof cb === "function") { cb(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
// REPLACE
|
// REPLACE
|
||||||
@@ -743,7 +752,7 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Replace a href by a stronger one everywhere in the drive (except FILES_DATA)
|
// Replace a href by a stronger one everywhere in the drive (except FILES_DATA)
|
||||||
var replaceHref = exp.replace = function (o, n) {
|
exp.replace = function (o, n) {
|
||||||
if (!isFile(o) || !isFile(n)) { return; }
|
if (!isFile(o) || !isFile(n)) { return; }
|
||||||
var paths = findFile(o);
|
var paths = findFile(o);
|
||||||
|
|
||||||
@@ -772,7 +781,7 @@ define([
|
|||||||
* INTEGRITY CHECK
|
* INTEGRITY CHECK
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var fixFiles = exp.fixFiles = function () {
|
exp.fixFiles = function () {
|
||||||
// Explore the tree and check that everything is correct:
|
// Explore the tree and check that everything is correct:
|
||||||
// * 'root', 'trash', 'unsorted' and 'filesData' exist and are objects
|
// * 'root', 'trash', 'unsorted' and 'filesData' exist and are objects
|
||||||
// * ROOT: Folders are objects, files are href
|
// * ROOT: Folders are objects, files are href
|
||||||
@@ -780,7 +789,7 @@ define([
|
|||||||
// * FILES_DATA: - Data (title, cdate, adte) are stored in filesData. filesData contains only href keys linking to object with title, cdate, adate.
|
// * FILES_DATA: - Data (title, cdate, adte) are stored in filesData. filesData contains only href keys linking to object with title, cdate, adate.
|
||||||
// - Dates (adate, cdate) can be parsed/formatted
|
// - Dates (adate, cdate) can be parsed/formatted
|
||||||
// - All files in filesData should be either in 'root', 'trash' or 'unsorted'. If that's not the case, copy the fily to 'unsorted'
|
// - All files in filesData should be either in 'root', 'trash' or 'unsorted'. If that's not the case, copy the fily to 'unsorted'
|
||||||
// * UNSORTED: Contains only files (href), and does not contains files that are in ROOT
|
// * TEMPLATE: Contains only files (href), and does not contains files that are in ROOT
|
||||||
debug("Cleaning file system...");
|
debug("Cleaning file system...");
|
||||||
|
|
||||||
var before = JSON.stringify(files);
|
var before = JSON.stringify(files);
|
||||||
@@ -821,26 +830,37 @@ define([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// Make sure unsorted doesn't exist anymore
|
||||||
var fixUnsorted = function () {
|
var fixUnsorted = function () {
|
||||||
if (!Array.isArray(files[UNSORTED])) { debug("UNSORTED was not an array"); files[UNSORTED] = []; }
|
if (!files[UNSORTED]) { return; }
|
||||||
files[UNSORTED] = Cryptpad.deduplicateString(files[UNSORTED].slice());
|
debug("UNSORTED still exists in the object, removing it...");
|
||||||
var us = files[UNSORTED];
|
var us = files[UNSORTED];
|
||||||
|
if (us.length === 0) {
|
||||||
|
delete files[UNSORTED];
|
||||||
|
return;
|
||||||
|
}
|
||||||
var rootFiles = getFiles([ROOT, TEMPLATE]).slice();
|
var rootFiles = getFiles([ROOT, TEMPLATE]).slice();
|
||||||
var toClean = [];
|
//var toClean = [];
|
||||||
us.forEach(function (el, idx) {
|
var root = find([ROOT]);
|
||||||
|
us.forEach(function (el) {
|
||||||
if (!isFile(el) || rootFiles.indexOf(el) !== -1) {
|
if (!isFile(el) || rootFiles.indexOf(el) !== -1) {
|
||||||
toClean.push(idx);
|
return;
|
||||||
|
//toClean.push(idx);
|
||||||
}
|
}
|
||||||
|
var name = getFileData(el).title || NEW_FILE_NAME;
|
||||||
|
var newName = getAvailableName(root, name);
|
||||||
|
root[newName] = el;
|
||||||
});
|
});
|
||||||
toClean.forEach(function (idx) {
|
delete files[UNSORTED];
|
||||||
|
/*toClean.forEach(function (idx) {
|
||||||
us.splice(idx, 1);
|
us.splice(idx, 1);
|
||||||
});
|
});*/
|
||||||
};
|
};
|
||||||
var fixTemplate = function () {
|
var fixTemplate = function () {
|
||||||
if (!Array.isArray(files[TEMPLATE])) { debug("TEMPLATE was not an array"); files[TEMPLATE] = []; }
|
if (!Array.isArray(files[TEMPLATE])) { debug("TEMPLATE was not an array"); files[TEMPLATE] = []; }
|
||||||
files[TEMPLATE] = Cryptpad.deduplicateString(files[TEMPLATE].slice());
|
files[TEMPLATE] = Cryptpad.deduplicateString(files[TEMPLATE].slice());
|
||||||
var us = files[TEMPLATE];
|
var us = files[TEMPLATE];
|
||||||
var rootFiles = getFiles([ROOT, UNSORTED]).slice();
|
var rootFiles = getFiles([ROOT]).slice();
|
||||||
var toClean = [];
|
var toClean = [];
|
||||||
us.forEach(function (el, idx) {
|
us.forEach(function (el, idx) {
|
||||||
if (!isFile(el) || rootFiles.indexOf(el) !== -1) {
|
if (!isFile(el) || rootFiles.indexOf(el) !== -1) {
|
||||||
@@ -855,16 +875,24 @@ define([
|
|||||||
if (!$.isArray(files[FILES_DATA])) { debug("FILES_DATA was not an array"); files[FILES_DATA] = []; }
|
if (!$.isArray(files[FILES_DATA])) { debug("FILES_DATA was not an array"); files[FILES_DATA] = []; }
|
||||||
var fd = files[FILES_DATA];
|
var fd = files[FILES_DATA];
|
||||||
var rootFiles = getFiles([ROOT, TRASH, 'hrefArray']);
|
var rootFiles = getFiles([ROOT, TRASH, 'hrefArray']);
|
||||||
|
var root = find([ROOT]);
|
||||||
var toClean = [];
|
var toClean = [];
|
||||||
fd.forEach(function (el, idx) {
|
fd.forEach(function (el) {
|
||||||
if (!el || typeof(el) !== "object") {
|
if (!el || typeof(el) !== "object") {
|
||||||
debug("An element in filesData was not an object.", el);
|
debug("An element in filesData was not an object.", el);
|
||||||
toClean.push(el);
|
toClean.push(el);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!el.href) {
|
||||||
|
debug("Rmoving an element in filesData with a missing href.", el);
|
||||||
|
toClean.push(el);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (rootFiles.indexOf(el.href) === -1) {
|
if (rootFiles.indexOf(el.href) === -1) {
|
||||||
debug("An element in filesData was not in ROOT, UNSORTED or TRASH.", el);
|
debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", el);
|
||||||
files[UNSORTED].push(el.href);
|
var name = el.title || NEW_FILE_NAME;
|
||||||
|
var newName = getAvailableName(root, name);
|
||||||
|
root[newName] = el.href;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,18 +20,18 @@
|
|||||||
visibilityChange: visibilityChange,
|
visibilityChange: visibilityChange,
|
||||||
};
|
};
|
||||||
|
|
||||||
var isSupported = Visible.isSupported = function () {
|
Visible.isSupported = function () {
|
||||||
return !(typeof(document.addEventListener) === "undefined" ||
|
return !(typeof(document.addEventListener) === "undefined" ||
|
||||||
typeof document[hidden] === "undefined");
|
typeof document[hidden] === "undefined");
|
||||||
};
|
};
|
||||||
|
|
||||||
var onChange = Visible.onChange = function (f) {
|
Visible.onChange = function (f) {
|
||||||
document.addEventListener(visibilityChange, function (ev) {
|
document.addEventListener(visibilityChange, function (ev) {
|
||||||
f(!document[hidden], ev);
|
f(!document[hidden], ev);
|
||||||
}, false);
|
}, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
var currently = Visible.currently = function () {
|
Visible.currently = function () {
|
||||||
return !document[hidden];
|
return !document[hidden];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ body {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 20px;
|
font-size: 16px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
@@ -43,6 +43,9 @@ body {
|
|||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
div:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
.fa {
|
.fa {
|
||||||
/*min-width: 17px;*/
|
/*min-width: 17px;*/
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
@@ -66,7 +69,7 @@ li {
|
|||||||
.contextMenu {
|
.contextMenu {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 50;
|
z-index: 500;
|
||||||
}
|
}
|
||||||
.contextMenu li {
|
.contextMenu li {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -89,6 +92,16 @@ li {
|
|||||||
.selected .fa-plus-square-o {
|
.selected .fa-plus-square-o {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
.selectedTmp {
|
||||||
|
border: 1px dotted #bbb;
|
||||||
|
background: #AAA;
|
||||||
|
color: #ddd;
|
||||||
|
margin: -1px;
|
||||||
|
}
|
||||||
|
.selectedTmp .fa-minus-square-o,
|
||||||
|
.selectedTmp .fa-plus-square-o {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
span.fa-folder,
|
span.fa-folder,
|
||||||
span.fa-folder-open {
|
span.fa-folder-open {
|
||||||
color: #FEDE8B;
|
color: #FEDE8B;
|
||||||
@@ -215,6 +228,13 @@ span.fa-folder-open {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
#content .selectBox {
|
||||||
|
display: none;
|
||||||
|
background-color: rgba(100, 100, 100, 0.7);
|
||||||
|
position: absolute;
|
||||||
|
z-index: 50;
|
||||||
}
|
}
|
||||||
#content.readonly {
|
#content.readonly {
|
||||||
background: #e6e6e6;
|
background: #e6e6e6;
|
||||||
@@ -242,7 +262,7 @@ span.fa-folder-open {
|
|||||||
#content li:not(.header) *:not(input) {
|
#content li:not(.header) *:not(input) {
|
||||||
/*pointer-events: none;*/
|
/*pointer-events: none;*/
|
||||||
}
|
}
|
||||||
#content li:not(.header):hover:not(.selected) {
|
#content li:not(.header):hover:not(.selected, .selectedTmp) {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
#content li:not(.header):hover .name {
|
#content li:not(.header):hover .name {
|
||||||
@@ -289,6 +309,9 @@ span.fa-folder-open {
|
|||||||
width: 50px;
|
width: 50px;
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
}
|
}
|
||||||
|
#content .element .truncated {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
#content div.grid {
|
#content div.grid {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
@@ -296,19 +319,35 @@ span.fa-folder-open {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 10px 10px;
|
margin: 10px 10px;
|
||||||
width: 140px;
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
padding-top: 10px;
|
padding-top: 5px;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
max-height: 145px;
|
|
||||||
}
|
}
|
||||||
#content div.grid li:not(.selected) {
|
#content div.grid li:not(.selected):not(.selectedTmp) {
|
||||||
border: transparent 1px;
|
border: 1px solid #CCC;
|
||||||
}
|
}
|
||||||
#content div.grid li .name {
|
#content div.grid li .name {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
margin: 8px 0;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#content div.grid li.element {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
#content div.grid li .truncated {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
#content div.grid li input {
|
#content div.grid li input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -317,7 +356,8 @@ span.fa-folder-open {
|
|||||||
#content div.grid li .fa {
|
#content div.grid li .fa {
|
||||||
display: block;
|
display: block;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
font-size: 40px;
|
font-size: 48px;
|
||||||
|
margin: 8px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
#content div.grid li .fa.listonly {
|
#content div.grid li .fa.listonly {
|
||||||
@@ -326,6 +366,9 @@ span.fa-folder-open {
|
|||||||
#content div.grid .listElement {
|
#content div.grid .listElement {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
#content .list {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
#content .list ul {
|
#content .list ul {
|
||||||
display: table;
|
display: table;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ html, body {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 20px;
|
font-size: 16px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +70,10 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
.fa {
|
.fa {
|
||||||
/*min-width: 17px;*/
|
/*min-width: 17px;*/
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
@@ -96,7 +100,7 @@ li {
|
|||||||
.contextMenu {
|
.contextMenu {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 50;
|
z-index: 500;
|
||||||
li {
|
li {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@@ -121,6 +125,16 @@ li {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.selectedTmp {
|
||||||
|
border: 1px dotted #bbb;
|
||||||
|
background: #AAA;
|
||||||
|
color: #ddd;
|
||||||
|
margin: -1px;
|
||||||
|
.fa-minus-square-o, .fa-plus-square-o {
|
||||||
|
color: @tree-fg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
&.fa-folder, &.fa-folder-open {
|
&.fa-folder, &.fa-folder-open {
|
||||||
color: #FEDE8B;
|
color: #FEDE8B;
|
||||||
@@ -260,6 +274,13 @@ span {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
|
position: relative;
|
||||||
|
.selectBox {
|
||||||
|
display: none;
|
||||||
|
background-color: rgba(100, 100, 100, 0.7);
|
||||||
|
position: absolute;
|
||||||
|
z-index: 50;
|
||||||
|
}
|
||||||
&.readonly {
|
&.readonly {
|
||||||
background: @content-bg-ro;
|
background: @content-bg-ro;
|
||||||
}
|
}
|
||||||
@@ -287,7 +308,7 @@ span {
|
|||||||
/*pointer-events: none;*/
|
/*pointer-events: none;*/
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
&:not(.selected) {
|
&:not(.selected, .selectedTmp) {
|
||||||
background-color: @drive-hover;
|
background-color: @drive-hover;
|
||||||
}
|
}
|
||||||
.name {
|
.name {
|
||||||
@@ -343,25 +364,45 @@ span {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.element {
|
||||||
|
.truncated { display: none; }
|
||||||
|
}
|
||||||
div.grid {
|
div.grid {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
li {
|
li {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 10px 10px;
|
margin: 10px 10px;
|
||||||
width: 140px;
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
padding-top: 10px;
|
padding-top: 5px;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
max-height: 145px;
|
|
||||||
|
|
||||||
&:not(.selected) {
|
&:not(.selected):not(.selectedTmp) {
|
||||||
border: transparent 1px;
|
border: 1px solid #CCC;
|
||||||
}
|
}
|
||||||
.name {
|
.name {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
margin: 8px 0;
|
||||||
|
display: inline-flex;
|
||||||
|
//align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
//text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
&.element {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.truncated {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0; right: 0;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -370,7 +411,8 @@ span {
|
|||||||
.fa {
|
.fa {
|
||||||
display: block;
|
display: block;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
font-size: 40px;
|
font-size: 48px;
|
||||||
|
margin: 8px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
&.listonly {
|
&.listonly {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -384,6 +426,7 @@ span {
|
|||||||
|
|
||||||
.list {
|
.list {
|
||||||
// Make it act as a table!
|
// Make it act as a table!
|
||||||
|
padding-left: 20px;
|
||||||
ul {
|
ul {
|
||||||
display: table;
|
display: table;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div class="app-container" tabindex="0">
|
<div class="app-container" tabindex="0">
|
||||||
<div id="tree">
|
<div id="tree">
|
||||||
</div>
|
</div>
|
||||||
<div id="content">
|
<div id="content" tabindex="2">
|
||||||
</div>
|
</div>
|
||||||
<div id="treeContextMenu" class="contextMenu dropdown clearfix">
|
<div id="treeContextMenu" class="contextMenu dropdown clearfix">
|
||||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
|
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<li><a tabindex="-1" data-icon="fa-file-code-o" class="newdoc own editable dropdown-item" data-type="code" data-localization="button_newcode">New code</a></li>
|
<li><a tabindex="-1" data-icon="fa-file-code-o" class="newdoc own editable dropdown-item" data-type="code" data-localization="button_newcode">New code</a></li>
|
||||||
<li><a tabindex="-1" data-icon="fa-file-powerpoint-o" class="newdoc own editable dropdown-item" data-type="slide" data-localization="button_newslide">New slide</a></li>
|
<li><a tabindex="-1" data-icon="fa-file-powerpoint-o" class="newdoc own editable dropdown-item" data-type="slide" data-localization="button_newslide">New slide</a></li>
|
||||||
<li><a tabindex="-1" data-icon="fa-calendar" class="newdoc own editable dropdown-item" data-type="poll" data-localization="button_newpoll">New poll</a></li>
|
<li><a tabindex="-1" data-icon="fa-calendar" class="newdoc own editable dropdown-item" data-type="poll" data-localization="button_newpoll">New poll</a></li>
|
||||||
<li><a tabindex="-1" data-icon="fa-calendar" class="newdoc own editable dropdown-item" data-type="whiteboard" data-localization="button_newwhiteboard">New whiteboard</a></li>
|
<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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="defaultContextMenu" class="contextMenu dropdown clearfix">
|
<div id="defaultContextMenu" class="contextMenu dropdown clearfix">
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -169,6 +169,7 @@ define([
|
|||||||
|
|
||||||
var $input = Input({
|
var $input = Input({
|
||||||
placeholder: 'card description',
|
placeholder: 'card description',
|
||||||
|
id: id,
|
||||||
})
|
})
|
||||||
.addClass('card-title');
|
.addClass('card-title');
|
||||||
|
|
||||||
@@ -206,7 +207,7 @@ define([
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
*/
|
*/
|
||||||
Card.move = function (uid, A, B) {
|
Card.move = function (/*uid, A, B*/) {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -228,11 +229,10 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
var card = proxy.cards[cid];
|
var card = proxy.cards[cid];
|
||||||
|
card = card; // TODO actually draw
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var Draw = Board.Draw = function ($lists) {
|
Board.Draw = function ($lists) {
|
||||||
proxy.listOrder.forEach(function (luid) {
|
proxy.listOrder.forEach(function (luid) {
|
||||||
List.draw($lists, luid);
|
List.draw($lists, luid);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ define([
|
|||||||
'/bower_components/chainpad-listmap/chainpad-listmap.js',
|
'/bower_components/chainpad-listmap/chainpad-listmap.js',
|
||||||
'/bower_components/chainpad-crypto/crypto.js',
|
'/bower_components/chainpad-crypto/crypto.js',
|
||||||
'/common/cryptpad-common.js',
|
'/common/cryptpad-common.js',
|
||||||
'/common/visible.js',
|
//'/common/visible.js',
|
||||||
'/common/notify.js',
|
//'/common/notify.js',
|
||||||
'/bower_components/file-saver/FileSaver.min.js'
|
'/bower_components/file-saver/FileSaver.min.js'
|
||||||
], function ($, Config, Messages, Board, TextPatcher, Listmap, Crypto, Cryptpad, Visible, Notify) {
|
], function ($, Config, Messages, Board, TextPatcher, Listmap, Crypto, Cryptpad /*, Visible, Notify*/) {
|
||||||
|
|
||||||
var saveAs = window.saveAs;
|
// var saveAs = window.saveAs;
|
||||||
|
|
||||||
Cryptpad.styleAlerts();
|
Cryptpad.styleAlerts();
|
||||||
console.log("Initializing your realtime session...");
|
console.log("Initializing your realtime session...");
|
||||||
@@ -23,28 +23,28 @@ define([
|
|||||||
Board: Board,
|
Board: Board,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
var unnotify = function () {
|
var unnotify = function () {
|
||||||
if (!(module.tabNotification &&
|
if (!(module.tabNotification &&
|
||||||
typeof(module.tabNotification.cancel) === 'function')) { return; }
|
typeof(module.tabNotification.cancel) === 'function')) { return; }
|
||||||
module.tabNotification.cancel();
|
module.tabNotification.cancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
var notify = function () {
|
var notify = function () {
|
||||||
if (!(Visible.isSupported() && !Visible.currently())) { return; }
|
if (!(Visible.isSupported() && !Visible.currently())) { return; }
|
||||||
unnotify();
|
unnotify();
|
||||||
module.tabNotification = Notify.tab(1000, 10);
|
module.tabNotification = Notify.tab(1000, 10);
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
var setEditable = function (bool) {
|
var setEditable = function (bool) {
|
||||||
|
bool = bool;
|
||||||
};
|
};
|
||||||
|
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
|
|
||||||
|
|
||||||
var $lists = $('#lists');
|
var $lists = $('#lists');
|
||||||
|
|
||||||
var $addList = $('#create-list').click(function () {
|
$('#create-list').click(function () {
|
||||||
Board.List.draw($lists);
|
Board.List.draw($lists);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ define([
|
|||||||
Cryptpad.log("You are the first user to visit this board");
|
Cryptpad.log("You are the first user to visit this board");
|
||||||
};
|
};
|
||||||
|
|
||||||
var whenReady = function (opt) {
|
var whenReady = function () {
|
||||||
var rt = module.rt;
|
var rt = module.rt;
|
||||||
var proxy = rt.proxy;
|
var proxy = rt.proxy;
|
||||||
|
|
||||||
@@ -63,7 +63,6 @@ define([
|
|||||||
Board.Draw($lists);
|
Board.Draw($lists);
|
||||||
|
|
||||||
if (first) { firstUser(); }
|
if (first) { firstUser(); }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
@@ -78,10 +77,10 @@ define([
|
|||||||
var proxy = rt.proxy;
|
var proxy = rt.proxy;
|
||||||
proxy
|
proxy
|
||||||
.on('create', function (info) {
|
.on('create', function (info) {
|
||||||
var realtime = module.realtime = info.realtime;
|
module.realtime = info.realtime;
|
||||||
window.location.hash = info.channel + secret.key;
|
window.location.hash = info.channel + secret.key;
|
||||||
})
|
})
|
||||||
.on('ready', function (info) {
|
.on('ready', function () {
|
||||||
Cryptpad.log("Ready!");
|
Cryptpad.log("Ready!");
|
||||||
whenReady({
|
whenReady({
|
||||||
|
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
define([
|
|
||||||
'jquery',
|
|
||||||
'/bower_components/chainpad-crypto/crypto.js',
|
|
||||||
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
|
||||||
'/common/toolbar.js',
|
|
||||||
'/common/cryptpad-common.js',
|
|
||||||
'/common/visible.js',
|
|
||||||
'/common/notify.js',
|
|
||||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
|
||||||
], function ($, Crypto, realtimeInput, Toolbar, Cryptpad, Visible, Notify) {
|
|
||||||
var Messages = Cryptpad.Messages;
|
|
||||||
window.Nacl = window.nacl;
|
|
||||||
$(function () {
|
|
||||||
|
|
||||||
var ifrw = $('#pad-iframe')[0].contentWindow;
|
|
||||||
var $iframe = $('#pad-iframe').contents();
|
|
||||||
|
|
||||||
Cryptpad.addLoadingScreen();
|
|
||||||
|
|
||||||
var andThen = function () {
|
|
||||||
var $bar = $iframe.find('.toolbar-container');
|
|
||||||
var secret = Cryptpad.getSecrets();
|
|
||||||
var readOnly = secret.keys && !secret.keys.editKeyStr;
|
|
||||||
if (!secret.keys) {
|
|
||||||
secret.keys = secret.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
var $mt = $iframe.find('#encryptedFile');
|
|
||||||
$mt.attr('src', './assets/image.png-encrypted');
|
|
||||||
$mt.attr('data-crypto-key', 'TBo77200c0e/FdldQFcnQx4Y');
|
|
||||||
$mt.attr('data-type', 'image/png');
|
|
||||||
require(['/common/media-tag.js'], function (MediaTag) {
|
|
||||||
MediaTag($mt[0]);
|
|
||||||
Cryptpad.removeLoadingScreen();
|
|
||||||
var configTb = {
|
|
||||||
displayed: ['useradmin', 'newpad'],
|
|
||||||
ifrw: ifrw,
|
|
||||||
common: Cryptpad
|
|
||||||
};
|
|
||||||
toolbar = Toolbar.create($bar, null, null, null, null, configTb);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Cryptpad.ready(function (err, anv) {
|
|
||||||
andThen();
|
|
||||||
Cryptpad.reportAppUsage();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -128,7 +128,7 @@ define([
|
|||||||
|
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
|
|
||||||
var onInit = config.onInit = function (info) {
|
config.onInit = function (info) {
|
||||||
var realtime = module.realtime = info.realtime;
|
var realtime = module.realtime = info.realtime;
|
||||||
window.location.hash = info.channel + secret.key;
|
window.location.hash = info.channel + secret.key;
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
var readValues = function () {
|
var readValues = function () {
|
||||||
UI.each(function (ui, i, list) {
|
UI.each(function (ui) {
|
||||||
Map[ui.id] = ui.value();
|
Map[ui.id] = ui.value();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -165,7 +165,7 @@ define([
|
|||||||
if (UI.ids.indexOf(key) === -1) { Map[key] = parsed[key]; }
|
if (UI.ids.indexOf(key) === -1) { Map[key] = parsed[key]; }
|
||||||
});
|
});
|
||||||
|
|
||||||
UI.each(function (ui, i, list) {
|
UI.each(function (ui) {
|
||||||
var newval = parsed[ui.id];
|
var newval = parsed[ui.id];
|
||||||
var oldval = ui.value();
|
var oldval = ui.value();
|
||||||
|
|
||||||
@@ -178,9 +178,7 @@ define([
|
|||||||
if (ui.preserveCursor) {
|
if (ui.preserveCursor) {
|
||||||
op = TextPatcher.diff(oldval, newval);
|
op = TextPatcher.diff(oldval, newval);
|
||||||
selects = ['selectionStart', 'selectionEnd'].map(function (attr) {
|
selects = ['selectionStart', 'selectionEnd'].map(function (attr) {
|
||||||
var before = element[attr];
|
return TextPatcher.transformCursor(element[attr], op);
|
||||||
var after = TextPatcher.transformCursor(element[attr], op);
|
|
||||||
return after;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,13 +193,13 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var onRemote = config.onRemote = function (info) {
|
config.onRemote = function () {
|
||||||
if (initializing) { return; }
|
if (initializing) { return; }
|
||||||
/* integrate remote changes */
|
/* integrate remote changes */
|
||||||
updateValues();
|
updateValues();
|
||||||
};
|
};
|
||||||
|
|
||||||
var onReady = config.onReady = function (info) {
|
config.onReady = function () {
|
||||||
updateValues();
|
updateValues();
|
||||||
|
|
||||||
console.log("READY");
|
console.log("READY");
|
||||||
@@ -209,13 +207,13 @@ define([
|
|||||||
initializing = false;
|
initializing = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
var onAbort = config.onAbort = function (info) {
|
config.onAbort = function () {
|
||||||
window.alert("Network Connection Lost");
|
window.alert("Network Connection Lost");
|
||||||
};
|
};
|
||||||
|
|
||||||
var rt = Realtime.start(config);
|
Realtime.start(config);
|
||||||
|
|
||||||
UI.each(function (ui, i, list) {
|
UI.each(function (ui) {
|
||||||
var type = ui.type;
|
var type = ui.type;
|
||||||
var events = eventsByType[type];
|
var events = eventsByType[type];
|
||||||
ui.$.on(events, onLocal);
|
ui.$.on(events, onLocal);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
define([], function () {
|
define([], function () {
|
||||||
var ula = {};
|
var ula = {};
|
||||||
|
|
||||||
var uid = ula.uid = (function () {
|
ula.uid = (function () {
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var prefix = 'rt_';
|
var prefix = 'rt_';
|
||||||
return function () { return prefix + i++; };
|
return function () { return prefix + i++; };
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
||||||
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
|
|
||||||
<style>
|
|
||||||
html, body{
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
textarea{
|
|
||||||
position: absolute;
|
|
||||||
top: 5vh;
|
|
||||||
left: 0px;
|
|
||||||
border: 0px;
|
|
||||||
|
|
||||||
padding-top: 15px;
|
|
||||||
width: 100%;
|
|
||||||
height: 95vh;
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100vh;
|
|
||||||
|
|
||||||
font-size: 30px;
|
|
||||||
background-color: #073642;
|
|
||||||
color: #839496;
|
|
||||||
|
|
||||||
overflow-x: hidden;
|
|
||||||
|
|
||||||
/* disallow textarea resizes */
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea[disabled] {
|
|
||||||
background-color: #275662;
|
|
||||||
color: #637476;
|
|
||||||
}
|
|
||||||
|
|
||||||
#panel {
|
|
||||||
position: fixed;
|
|
||||||
top: 0px;
|
|
||||||
right: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: 5vh;
|
|
||||||
z-index: 95;
|
|
||||||
background-color: #777;
|
|
||||||
/* min-height: 75px; */
|
|
||||||
}
|
|
||||||
#run {
|
|
||||||
display: block;
|
|
||||||
float: right;
|
|
||||||
height: 100%;
|
|
||||||
width: 10vw;
|
|
||||||
z-index: 100;
|
|
||||||
line-height: 5vw;
|
|
||||||
font-size: 1.5em;
|
|
||||||
background-color: #222;
|
|
||||||
color: #CCC;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 5%;
|
|
||||||
border: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<textarea></textarea>
|
|
||||||
<div id="panel">
|
|
||||||
<!-- TODO update this element when new users join -->
|
|
||||||
<span id="users"></span>
|
|
||||||
<!-- what else should go in the panel? -->
|
|
||||||
<a href="#" id="run">RUN</a>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
define([
|
|
||||||
'jquery',
|
|
||||||
'/api/config',
|
|
||||||
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
|
||||||
'/bower_components/chainpad-crypto/crypto.js',
|
|
||||||
'/bower_components/textpatcher/TextPatcher.amd.js',
|
|
||||||
'/common/cryptpad-common.js'
|
|
||||||
], function ($, Config, Realtime, Crypto, TextPatcher, Cryptpad) {
|
|
||||||
|
|
||||||
var secret = Cryptpad.getSecrets();
|
|
||||||
|
|
||||||
var $textarea = $('textarea'),
|
|
||||||
$run = $('#run');
|
|
||||||
|
|
||||||
var module = {};
|
|
||||||
|
|
||||||
var config = {
|
|
||||||
initialState: '',
|
|
||||||
websocketURL: Config.websocketURL,
|
|
||||||
channel: secret.channel,
|
|
||||||
crypto: Crypto.createEncryptor(secret.key),
|
|
||||||
};
|
|
||||||
var initializing = true;
|
|
||||||
|
|
||||||
var setEditable = function (bool) { $textarea.attr('disabled', !bool); };
|
|
||||||
var canonicalize = function (text) { return text.replace(/\r\n/g, '\n'); };
|
|
||||||
|
|
||||||
setEditable(false);
|
|
||||||
|
|
||||||
var onInit = config.onInit = function (info) {
|
|
||||||
window.location.hash = info.channel + secret.key;
|
|
||||||
$(window).on('hashchange', function() { window.location.reload(); });
|
|
||||||
};
|
|
||||||
|
|
||||||
var onRemote = config.onRemote = function (info) {
|
|
||||||
if (initializing) { return; }
|
|
||||||
|
|
||||||
var userDoc = info.realtime.getUserDoc();
|
|
||||||
var current = canonicalize($textarea.val());
|
|
||||||
|
|
||||||
var op = TextPatcher.diff(current, userDoc);
|
|
||||||
|
|
||||||
var elem = $textarea[0];
|
|
||||||
|
|
||||||
var selects = ['selectionStart', 'selectionEnd'].map(function (attr) {
|
|
||||||
return TextPatcher.transformCursor(elem[attr], op);
|
|
||||||
});
|
|
||||||
|
|
||||||
$textarea.val(userDoc);
|
|
||||||
elem.selectionStart = selects[0];
|
|
||||||
elem.selectionEnd = selects[1];
|
|
||||||
|
|
||||||
// TODO do something on external messages
|
|
||||||
// http://webdesign.tutsplus.com/tutorials/how-to-display-update-notifications-in-the-browser-tab--cms-23458
|
|
||||||
};
|
|
||||||
|
|
||||||
var onReady = config.onReady = function (info) {
|
|
||||||
module.patchText = TextPatcher.create({
|
|
||||||
realtime: info.realtime
|
|
||||||
// logging: true
|
|
||||||
});
|
|
||||||
initializing = false;
|
|
||||||
setEditable(true);
|
|
||||||
$textarea.val(info.realtime.getUserDoc());
|
|
||||||
};
|
|
||||||
|
|
||||||
var onAbort = config.onAbort = function (info) {
|
|
||||||
setEditable(false);
|
|
||||||
window.alert("Server Connection Lost");
|
|
||||||
};
|
|
||||||
|
|
||||||
var onLocal = config.onLocal = function () {
|
|
||||||
if (initializing) { return; }
|
|
||||||
module.patchText(canonicalize($textarea.val()));
|
|
||||||
};
|
|
||||||
|
|
||||||
var rt = window.rt = Realtime.start(config);
|
|
||||||
|
|
||||||
var splice = function (str, index, chars) {
|
|
||||||
var count = chars.length;
|
|
||||||
return str.slice(0, index) + chars + str.slice((index -1) + count);
|
|
||||||
};
|
|
||||||
|
|
||||||
var setSelectionRange = function (input, start, end) {
|
|
||||||
if (input.setSelectionRange) {
|
|
||||||
input.focus();
|
|
||||||
input.setSelectionRange(start, end);
|
|
||||||
} else if (input.createTextRange) {
|
|
||||||
var range = input.createTextRange();
|
|
||||||
range.collapse(true);
|
|
||||||
range.moveEnd('character', end);
|
|
||||||
range.moveStart('character', start);
|
|
||||||
range.select();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var setCursor = function (el, pos) {
|
|
||||||
setSelectionRange(el, pos, pos);
|
|
||||||
};
|
|
||||||
|
|
||||||
var state = {};
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
$textarea.on('keydown', function (e) {
|
|
||||||
// track when control keys are pushed down
|
|
||||||
//switch (e.key) { }
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
$textarea.on('keyup', function (e) {
|
|
||||||
// track when control keys are released
|
|
||||||
});
|
|
||||||
|
|
||||||
//$textarea.on('change', onLocal);
|
|
||||||
$textarea.on('keypress', function (e) {
|
|
||||||
onLocal();
|
|
||||||
switch (e.key) {
|
|
||||||
case 'Tab':
|
|
||||||
// insert a tab wherever the cursor is...
|
|
||||||
var start = $textarea.prop('selectionStart');
|
|
||||||
var end = $textarea.prop('selectionEnd');
|
|
||||||
if (typeof start !== 'undefined') {
|
|
||||||
if (start === end) {
|
|
||||||
$textarea.val(function (i, val) {
|
|
||||||
return splice(val, start, "\t");
|
|
||||||
});
|
|
||||||
setCursor($textarea[0], start +1);
|
|
||||||
} else {
|
|
||||||
// indentation?? this ought to be fun.
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// simulate a keypress so the event goes through..
|
|
||||||
// prevent default behaviour for tab
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
onLocal();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
['cut', 'paste', 'change', 'keyup', 'keydown', 'select', 'textInput']
|
|
||||||
.forEach(function (evt) {
|
|
||||||
$textarea.on(evt, onLocal);
|
|
||||||
});
|
|
||||||
|
|
||||||
$run.click(function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var content = $textarea.val();
|
|
||||||
|
|
||||||
try {
|
|
||||||
eval(content); // jshint ignore:line
|
|
||||||
} catch (err) {
|
|
||||||
// FIXME don't use alert, make an errorbox
|
|
||||||
window.alert(err.message);
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -11,12 +11,10 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a href="/examples/form/">forms</a></li>
|
<li><a href="/examples/form/">forms</a></li>
|
||||||
<li><a href="/examples/text/">text</a></li>
|
<li><a href="/examples/text/">text</a></li>
|
||||||
<li><a href="/examples/hack/">textareas with executable content</a></li>
|
|
||||||
<li><a href="/examples/board/">kanban board</a></li>
|
<li><a href="/examples/board/">kanban board</a></li>
|
||||||
<li><a href="/examples/json/">json objects</a></li>
|
<!-- <li><a href="/examples/json/">json objects</a></li> -->
|
||||||
<li><a href="/examples/read/">ajax-like get/put behaviour</a></li>
|
<li><a href="/examples/read/">ajax-like get/put behaviour</a></li>
|
||||||
<li><a href="/examples/render/">render markdown content as html</a></li>
|
<li><a href="/examples/render/">render markdown content as html</a></li>
|
||||||
<li><a href="/examples/style/">edit a page's style tag</a></li>
|
<li><a href="/examples/style/">edit a page's style tag</a></li>
|
||||||
<li><a href="/examples/upload/">upload content</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|||||||
@@ -26,15 +26,13 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var initializing = true;
|
|
||||||
|
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
|
|
||||||
var rt = module.rt = RtListMap.create(config);
|
var rt = module.rt = RtListMap.create(config);
|
||||||
rt.proxy.on('create', function (info) {
|
rt.proxy.on('create', function (info) {
|
||||||
console.log("initializing...");
|
console.log("initializing...");
|
||||||
window.location.hash = info.channel + secret.key;
|
window.location.hash = info.channel + secret.key;
|
||||||
}).on('ready', function (info) {
|
}).on('ready', function () {
|
||||||
console.log("...your realtime object is ready");
|
console.log("...your realtime object is ready");
|
||||||
|
|
||||||
rt.proxy
|
rt.proxy
|
||||||
@@ -42,7 +40,7 @@ define([
|
|||||||
.on('change', [], function (o, n, p) {
|
.on('change', [], function (o, n, p) {
|
||||||
console.log("root change event firing for path [%s]: %s => %s", p.join(','), o, n);
|
console.log("root change event firing for path [%s]: %s => %s", p.join(','), o, n);
|
||||||
})
|
})
|
||||||
.on('remove', [], function (o, p, root) {
|
.on('remove', [], function (o, p) {
|
||||||
console.log("Removal of value [%s] at path [%s]", o, p.join(','));
|
console.log("Removal of value [%s] at path [%s]", o, p.join(','));
|
||||||
})
|
})
|
||||||
.on('change', ['a', 'b', 'c'], function (o, n, p) {
|
.on('change', ['a', 'b', 'c'], function (o, n, p) {
|
||||||
@@ -51,7 +49,7 @@ define([
|
|||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
// on(event, cb)
|
// on(event, cb)
|
||||||
.on('disconnect', function (info) {
|
.on('disconnect', function () {
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
window.alert("Network connection lost");
|
window.alert("Network connection lost");
|
||||||
});
|
});
|
||||||
@@ -65,6 +63,7 @@ define([
|
|||||||
|
|
||||||
console.log("evaluating `%s`", value);
|
console.log("evaluating `%s`", value);
|
||||||
var x = rt.proxy;
|
var x = rt.proxy;
|
||||||
|
x = x; // LOL jshint says this is unused otherwise <3
|
||||||
|
|
||||||
console.log('> ', eval(value)); // jshint ignore:line
|
console.log('> ', eval(value)); // jshint ignore:line
|
||||||
console.log();
|
console.log();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ define([
|
|||||||
'/common/cryptpad-common.js',
|
'/common/cryptpad-common.js',
|
||||||
'/common/pinpad.js'
|
'/common/pinpad.js'
|
||||||
], function ($, Cryptpad, Pinpad) {
|
], function ($, Cryptpad, Pinpad) {
|
||||||
var APP = window.APP = {
|
window.APP = {
|
||||||
Cryptpad: Cryptpad,
|
Cryptpad: Cryptpad,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
Cryptpad.ready(function (err, env) {
|
Cryptpad.ready(function () {
|
||||||
var network = Cryptpad.getNetwork();
|
var network = Cryptpad.getNetwork();
|
||||||
var proxy = Cryptpad.getStore().getProxy().proxy;
|
var proxy = Cryptpad.getStore().getProxy().proxy;
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ define([
|
|||||||
'jquery',
|
'jquery',
|
||||||
'/common/cryptget.js'
|
'/common/cryptget.js'
|
||||||
], function ($, Crypt) {
|
], function ($, Crypt) {
|
||||||
|
|
||||||
var $target = $('#target');
|
var $target = $('#target');
|
||||||
var $dest = $('#dest');
|
|
||||||
|
|
||||||
var useDoc = function (err, doc) {
|
var useDoc = function (err, doc) {
|
||||||
if (err) { return console.error(err); }
|
if (err) { return console.error(err); }
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ define([
|
|||||||
|
|
||||||
var initializing = true;
|
var initializing = true;
|
||||||
|
|
||||||
var onInit = config.onInit = function (info) {
|
config.onInit = function (info) {
|
||||||
window.location.hash = info.channel + secret.key;
|
window.location.hash = info.channel + secret.key;
|
||||||
module.realtime = info.realtime;
|
module.realtime = info.realtime;
|
||||||
};
|
};
|
||||||
@@ -73,7 +73,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
// when your editor is ready
|
// when your editor is ready
|
||||||
var onReady = config.onReady = function (info) {
|
config.onReady = function () {
|
||||||
console.log("Realtime is ready!");
|
console.log("Realtime is ready!");
|
||||||
var userDoc = module.realtime.getUserDoc();
|
var userDoc = module.realtime.getUserDoc();
|
||||||
lazyDraw(getContent(userDoc));
|
lazyDraw(getContent(userDoc));
|
||||||
@@ -81,13 +81,13 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
// when remote editors do things...
|
// when remote editors do things...
|
||||||
var onRemote = config.onRemote = function () {
|
config.onRemote = function () {
|
||||||
if (initializing) { return; }
|
if (initializing) { return; }
|
||||||
var userDoc = module.realtime.getUserDoc();
|
var userDoc = module.realtime.getUserDoc();
|
||||||
lazyDraw(getContent(userDoc));
|
lazyDraw(getContent(userDoc));
|
||||||
};
|
};
|
||||||
|
|
||||||
var onLocal = config.onLocal = function () {
|
config.onLocal = function () {
|
||||||
// we're not really expecting any local events for this editor...
|
// we're not really expecting any local events for this editor...
|
||||||
/* but we might add a second pane in the future so that you don't need
|
/* but we might add a second pane in the future so that you don't need
|
||||||
a second window to edit your markdown */
|
a second window to edit your markdown */
|
||||||
@@ -96,9 +96,9 @@ define([
|
|||||||
lazyDraw(userDoc);
|
lazyDraw(userDoc);
|
||||||
};
|
};
|
||||||
|
|
||||||
var onAbort = config.onAbort = function () {
|
config.onAbort = function () {
|
||||||
window.alert("Network Connection Lost");
|
window.alert("Network Connection Lost");
|
||||||
};
|
};
|
||||||
|
|
||||||
var rts = Realtime.start(config);
|
Realtime.start(config);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ define([
|
|||||||
crypto: Crypto.createEncryptor(secret.key),
|
crypto: Crypto.createEncryptor(secret.key),
|
||||||
};
|
};
|
||||||
|
|
||||||
var userName = module.userName = config.userName = Crypto.rand64(8);
|
|
||||||
|
|
||||||
var lazyDraw = (function () {
|
var lazyDraw = (function () {
|
||||||
var to,
|
var to,
|
||||||
delay = 500;
|
delay = 500;
|
||||||
@@ -37,7 +35,7 @@ define([
|
|||||||
|
|
||||||
var initializing = true;
|
var initializing = true;
|
||||||
|
|
||||||
var onInit = config.onInit = function (info) {
|
config.onInit = function (info) {
|
||||||
window.location.hash = info.channel + secret.key;
|
window.location.hash = info.channel + secret.key;
|
||||||
var realtime = module.realtime = info.realtime;
|
var realtime = module.realtime = info.realtime;
|
||||||
module.patchText = TextPatcher.create({
|
module.patchText = TextPatcher.create({
|
||||||
@@ -50,28 +48,28 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var onReady = config.onReady = function (info) {
|
config.onReady = function () {
|
||||||
var userDoc = module.realtime.getUserDoc();
|
var userDoc = module.realtime.getUserDoc();
|
||||||
draw(userDoc);
|
draw(userDoc);
|
||||||
console.log("Ready");
|
console.log("Ready");
|
||||||
initializing = false;
|
initializing = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
var onRemote = config.onRemote = function () {
|
config.onRemote = function () {
|
||||||
draw(module.realtime.getUserDoc());
|
draw(module.realtime.getUserDoc());
|
||||||
};
|
};
|
||||||
|
|
||||||
var onAbort = config.onAbort = function (info) {
|
config.onAbort = function () {
|
||||||
// notify the user of the abort
|
// notify the user of the abort
|
||||||
window.alert("Network Connection Lost");
|
window.alert("Network Connection Lost");
|
||||||
};
|
};
|
||||||
|
|
||||||
var onLocal = config.onLocal = function () {
|
config.onLocal = function () {
|
||||||
// nope
|
// nope
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
$edit.attr('href', '/examples/text/'+ window.location.hash);
|
$edit.attr('href', '/examples/text/'+ window.location.hash);
|
||||||
|
|
||||||
var rt = Realtime.start(config);
|
Realtime.start(config);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ define([
|
|||||||
TextPatcher: TextPatcher
|
TextPatcher: TextPatcher
|
||||||
};
|
};
|
||||||
|
|
||||||
var userName = module.userName = Crypto.rand64(8);
|
|
||||||
|
|
||||||
var initializing = true;
|
var initializing = true;
|
||||||
var $textarea = $('textarea');
|
var $textarea = $('textarea');
|
||||||
|
|
||||||
@@ -30,14 +28,14 @@ define([
|
|||||||
|
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
|
|
||||||
var onInit = config.onInit = function (info) {
|
config.onInit = function (info) {
|
||||||
window.location.hash = info.channel + secret.key;
|
window.location.hash = info.channel + secret.key;
|
||||||
$(window).on('hashchange', function() {
|
$(window).on('hashchange', function() {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var onRemote = config.onRemote = function (info) {
|
config.onRemote = function () {
|
||||||
if (initializing) { return; }
|
if (initializing) { return; }
|
||||||
var userDoc = module.realtime.getUserDoc();
|
var userDoc = module.realtime.getUserDoc();
|
||||||
var content = canonicalize($textarea.val());
|
var content = canonicalize($textarea.val());
|
||||||
@@ -59,7 +57,7 @@ define([
|
|||||||
module.patchText(canonicalize($textarea.val()));
|
module.patchText(canonicalize($textarea.val()));
|
||||||
};
|
};
|
||||||
|
|
||||||
var onReady = config.onReady = function (info) {
|
config.onReady = function (info) {
|
||||||
var realtime = module.realtime = info.realtime;
|
var realtime = module.realtime = info.realtime;
|
||||||
module.patchText = TextPatcher.create({
|
module.patchText = TextPatcher.create({
|
||||||
realtime: realtime
|
realtime: realtime
|
||||||
@@ -71,12 +69,12 @@ define([
|
|||||||
initializing = false;
|
initializing = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
var onAbort = config.onAbort = function (info) {
|
config.onAbort = function () {
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
window.alert("Server Connection Lost");
|
window.alert("Server Connection Lost");
|
||||||
};
|
};
|
||||||
|
|
||||||
var onConnectionChange = config.onConnectionChange = function (info) {
|
config.onConnectionChange = function (info) {
|
||||||
if (info.state) {
|
if (info.state) {
|
||||||
initializing = true;
|
initializing = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -85,7 +83,7 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var rt = Realtime.start(config);
|
Realtime.start(config);
|
||||||
|
|
||||||
['cut', 'paste', 'change', 'keyup', 'keydown', 'select', 'textInput']
|
['cut', 'paste', 'change', 'keyup', 'keydown', 'select', 'textInput']
|
||||||
.forEach(function (evt) {
|
.forEach(function (evt) {
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
define([
|
|
||||||
'jquery',
|
|
||||||
'/common/cryptget.js',
|
|
||||||
'/bower_components/chainpad-crypto/crypto.js'
|
|
||||||
], function ($, Crypt, Crypto) {
|
|
||||||
var Nacl = window.nacl;
|
|
||||||
|
|
||||||
var key = Nacl.randomBytes(32);
|
|
||||||
|
|
||||||
var handleFile = function (body) {
|
|
||||||
//console.log("plaintext");
|
|
||||||
//console.log(body);
|
|
||||||
|
|
||||||
/*
|
|
||||||
0 && Crypt.put(body, function (e, out) {
|
|
||||||
if (e) { return void console.error(e); }
|
|
||||||
if (out) {
|
|
||||||
console.log(out);
|
|
||||||
}
|
|
||||||
}); */
|
|
||||||
|
|
||||||
var data = {};
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
var cyphertext = data.payload = Crypto.encrypt(body, key);
|
|
||||||
console.log("encrypted");
|
|
||||||
console.log(cyphertext);
|
|
||||||
|
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
var decrypted = Crypto.decrypt(cyphertext, key);
|
|
||||||
//console.log('decrypted');
|
|
||||||
//console.log(decrypted);
|
|
||||||
|
|
||||||
|
|
||||||
if (decrypted !== body) {
|
|
||||||
throw new Error("failed to maintain integrity with round trip");
|
|
||||||
}
|
|
||||||
|
|
||||||
// finding... files are entirely too large.
|
|
||||||
|
|
||||||
|
|
||||||
console.log(data.payload.length, body.length); // 1491393, 588323
|
|
||||||
console.log(body.length / data.payload.length); // 0.3944788529918003
|
|
||||||
console.log(data.payload.length / body.length); // 2.534990132971174
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
http://stackoverflow.com/questions/19959072/sending-binary-data-in-javascript-over-http
|
|
||||||
|
|
||||||
// Since we deal with Firefox and Chrome only
|
|
||||||
var bytesToSend = [253, 0, 128, 1];
|
|
||||||
var bytesArray = new Uint8Array(bytesToSend);
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: '%your_service_url%',
|
|
||||||
type: 'POST',
|
|
||||||
contentType: 'application/octet-stream',
|
|
||||||
data: bytesArray,
|
|
||||||
processData: false
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
|
|
||||||
var $file = $('input[type="file"]');
|
|
||||||
$file.on('change', function (e) {
|
|
||||||
var file = e.target.files[0];
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.onload = function (e) {
|
|
||||||
handleFile(e.target.result);
|
|
||||||
};
|
|
||||||
reader.readAsText(file);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
188
www/file/file-crypto.js
Normal file
188
www/file/file-crypto.js
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
define([
|
||||||
|
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||||
|
], function () {
|
||||||
|
var Nacl = window.nacl;
|
||||||
|
var PARANOIA = true;
|
||||||
|
|
||||||
|
var plainChunkLength = 128 * 1024;
|
||||||
|
var cypherChunkLength = 131088;
|
||||||
|
|
||||||
|
var encodePrefix = function (p) {
|
||||||
|
return [
|
||||||
|
65280, // 255 << 8
|
||||||
|
255,
|
||||||
|
].map(function (n, i) {
|
||||||
|
return (p & n) >> ((1 - i) * 8);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var decodePrefix = function (A) {
|
||||||
|
return (A[0] << 8) | A[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
var slice = function (A) {
|
||||||
|
return Array.prototype.slice.call(A);
|
||||||
|
};
|
||||||
|
|
||||||
|
var createNonce = function () {
|
||||||
|
return new Uint8Array(new Array(24).fill(0));
|
||||||
|
};
|
||||||
|
|
||||||
|
var increment = function (N) {
|
||||||
|
var l = N.length;
|
||||||
|
while (l-- > 1) {
|
||||||
|
if (PARANOIA) {
|
||||||
|
if (typeof(N[l]) !== 'number') {
|
||||||
|
throw new Error('E_UNSAFE_TYPE');
|
||||||
|
}
|
||||||
|
if (N[l] > 255) {
|
||||||
|
throw new Error('E_OUT_OF_BOUNDS');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* jshint probably suspects this is unsafe because we lack types
|
||||||
|
but as long as this is only used on nonces, it should be safe */
|
||||||
|
if (N[l] !== 255) { return void N[l]++; } // jshint ignore:line
|
||||||
|
N[l] = 0;
|
||||||
|
|
||||||
|
// you don't need to worry about this running out.
|
||||||
|
// you'd need a REAAAALLY big file
|
||||||
|
if (l === 0) {
|
||||||
|
throw new Error('E_NONCE_TOO_LARGE');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var joinChunks = function (chunks) {
|
||||||
|
return new Uint8Array(chunks.reduce(function (A, B) {
|
||||||
|
return slice(A).concat(slice(B));
|
||||||
|
}, []));
|
||||||
|
};
|
||||||
|
|
||||||
|
var decrypt = function (u8, key, cb) {
|
||||||
|
var fail = function (e) {
|
||||||
|
cb(e || "DECRYPTION_ERROR");
|
||||||
|
};
|
||||||
|
|
||||||
|
var nonce = createNonce();
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
var prefix = u8.subarray(0, 2);
|
||||||
|
var metadataLength = decodePrefix(prefix);
|
||||||
|
|
||||||
|
var res = {
|
||||||
|
metadata: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
var metaBox = new Uint8Array(u8.subarray(2, 2 + metadataLength));
|
||||||
|
|
||||||
|
var metaChunk = Nacl.secretbox.open(metaBox, nonce, key);
|
||||||
|
increment(nonce);
|
||||||
|
|
||||||
|
try {
|
||||||
|
res.metadata = JSON.parse(Nacl.util.encodeUTF8(metaChunk));
|
||||||
|
} catch (e) {
|
||||||
|
return fail('E_METADATA_DECRYPTION');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.metadata) {
|
||||||
|
return void setTimeout(function () {
|
||||||
|
cb('NO_METADATA');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var takeChunk = function () {
|
||||||
|
var start = i * cypherChunkLength + 2 + metadataLength;
|
||||||
|
var end = start + cypherChunkLength;
|
||||||
|
i++;
|
||||||
|
var box = new Uint8Array(u8.subarray(start, end));
|
||||||
|
|
||||||
|
// decrypt the chunk
|
||||||
|
var plaintext = Nacl.secretbox.open(box, nonce, key);
|
||||||
|
increment(nonce);
|
||||||
|
return plaintext;
|
||||||
|
};
|
||||||
|
|
||||||
|
var chunks = [];
|
||||||
|
// decrypt file contents
|
||||||
|
var chunk;
|
||||||
|
for (;i * cypherChunkLength < u8.length;) {
|
||||||
|
chunk = takeChunk();
|
||||||
|
if (!chunk) {
|
||||||
|
return window.setTimeout(fail);
|
||||||
|
}
|
||||||
|
chunks.push(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send chunks
|
||||||
|
res.content = joinChunks(chunks);
|
||||||
|
|
||||||
|
cb(void 0, res);
|
||||||
|
};
|
||||||
|
|
||||||
|
// metadata
|
||||||
|
/* { filename: 'raccoon.jpg', type: 'image/jpeg' } */
|
||||||
|
var encrypt = function (u8, metadata, key) {
|
||||||
|
var nonce = createNonce();
|
||||||
|
|
||||||
|
// encode metadata
|
||||||
|
var metaBuffer = Array.prototype.slice
|
||||||
|
.call(Nacl.util.decodeUTF8(JSON.stringify(metadata)));
|
||||||
|
|
||||||
|
var plaintext = new Uint8Array(metaBuffer);
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
0: metadata
|
||||||
|
1: u8
|
||||||
|
2: done
|
||||||
|
*/
|
||||||
|
|
||||||
|
var state = 0;
|
||||||
|
|
||||||
|
var next = function (cb) {
|
||||||
|
var start;
|
||||||
|
var end;
|
||||||
|
var part;
|
||||||
|
var box;
|
||||||
|
|
||||||
|
// DONE
|
||||||
|
if (state === 2) { return void cb(); }
|
||||||
|
|
||||||
|
if (state === 0) { // metadata...
|
||||||
|
part = new Uint8Array(plaintext);
|
||||||
|
box = Nacl.secretbox(part, nonce, key);
|
||||||
|
increment(nonce);
|
||||||
|
|
||||||
|
if (box.length > 65535) {
|
||||||
|
return void cb('METADATA_TOO_LARGE');
|
||||||
|
}
|
||||||
|
var prefixed = new Uint8Array(encodePrefix(box.length)
|
||||||
|
.concat(slice(box)));
|
||||||
|
state++;
|
||||||
|
return void cb(void 0, prefixed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// encrypt the rest of the file...
|
||||||
|
start = i * plainChunkLength;
|
||||||
|
end = start + plainChunkLength;
|
||||||
|
|
||||||
|
part = u8.subarray(start, end);
|
||||||
|
box = Nacl.secretbox(part, nonce, key);
|
||||||
|
increment(nonce);
|
||||||
|
i++;
|
||||||
|
|
||||||
|
// regular data is done
|
||||||
|
if (i * plainChunkLength >= u8.length) { state = 2; }
|
||||||
|
|
||||||
|
return void cb(void 0, box);
|
||||||
|
};
|
||||||
|
|
||||||
|
return next;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
decrypt: decrypt,
|
||||||
|
encrypt: encrypt,
|
||||||
|
joinChunks: joinChunks,
|
||||||
|
};
|
||||||
|
});
|
||||||
65
www/file/inner.html
Normal file
65
www/file/inner.html
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||||
|
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
|
||||||
|
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
.cryptpad-toolbar {
|
||||||
|
margin-bottom: 1px;
|
||||||
|
padding: 0px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
#file {
|
||||||
|
display: block;
|
||||||
|
height: 300px;
|
||||||
|
width: 300px;
|
||||||
|
border: 2px solid black;
|
||||||
|
margin: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputfile {
|
||||||
|
width: 0.1px;
|
||||||
|
height: 0.1px;
|
||||||
|
opacity: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
display: block;
|
||||||
|
height: 500px;
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.inputfile + label {
|
||||||
|
border: 2px solid black;
|
||||||
|
background-color: rgba(50, 50, 50, .10);
|
||||||
|
margin: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputfile:focus + label,
|
||||||
|
.inputfile + label:hover {
|
||||||
|
background-color: rgba(50, 50, 50, 0.30);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</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">Choose a file</label>
|
||||||
|
</div>
|
||||||
|
<div id="feedback" class="block hidden">
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
236
www/file/main.js
Normal file
236
www/file/main.js
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
define([
|
||||||
|
'jquery',
|
||||||
|
'/bower_components/chainpad-crypto/crypto.js',
|
||||||
|
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
||||||
|
'/common/toolbar2.js',
|
||||||
|
'/common/cryptpad-common.js',
|
||||||
|
'/common/visible.js',
|
||||||
|
'/common/notify.js',
|
||||||
|
'/file/file-crypto.js',
|
||||||
|
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||||
|
'/bower_components/file-saver/FileSaver.min.js',
|
||||||
|
], function ($, Crypto, realtimeInput, Toolbar, Cryptpad, Visible, Notify, FileCrypto) {
|
||||||
|
var Messages = Cryptpad.Messages;
|
||||||
|
var saveAs = window.saveAs;
|
||||||
|
var Nacl = window.nacl;
|
||||||
|
|
||||||
|
var APP = {};
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
|
||||||
|
var ifrw = $('#pad-iframe')[0].contentWindow;
|
||||||
|
var $iframe = $('#pad-iframe').contents();
|
||||||
|
var $form = $iframe.find('#upload-form');
|
||||||
|
|
||||||
|
Cryptpad.addLoadingScreen();
|
||||||
|
|
||||||
|
var Title;
|
||||||
|
|
||||||
|
var myFile;
|
||||||
|
var myDataType;
|
||||||
|
|
||||||
|
var upload = function (blob, metadata) {
|
||||||
|
console.log(metadata);
|
||||||
|
var u8 = new Uint8Array(blob);
|
||||||
|
|
||||||
|
var key = Nacl.randomBytes(32);
|
||||||
|
var next = FileCrypto.encrypt(u8, metadata, key);
|
||||||
|
|
||||||
|
var chunks = [];
|
||||||
|
|
||||||
|
var sendChunk = function (box, cb) {
|
||||||
|
var enc = Nacl.util.encodeBase64(box);
|
||||||
|
|
||||||
|
chunks.push(box);
|
||||||
|
Cryptpad.rpc.send('UPLOAD', enc, function (e, msg) {
|
||||||
|
cb(e, msg);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var again = function (err, box) {
|
||||||
|
if (err) { throw new Error(err); }
|
||||||
|
if (box) {
|
||||||
|
return void sendChunk(box, function (e) {
|
||||||
|
if (e) { return console.error(e); }
|
||||||
|
next(again);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not box then done
|
||||||
|
Cryptpad.rpc.send('UPLOAD_COMPLETE', '', function (e, res) {
|
||||||
|
if (e) { return void console.error(e); }
|
||||||
|
var id = res[0];
|
||||||
|
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);
|
||||||
|
window.location.hash = Cryptpad.getFileHashFromKeys(id, b64Key);
|
||||||
|
|
||||||
|
$form.hide();
|
||||||
|
|
||||||
|
APP.toolbar.addElement(['fileshare'], {});
|
||||||
|
|
||||||
|
// check if the uploaded file can be decrypted
|
||||||
|
var newU8 = FileCrypto.joinChunks(chunks);
|
||||||
|
FileCrypto.decrypt(newU8, key, function (e, res) {
|
||||||
|
if (e) { return console.error(e); }
|
||||||
|
var title = document.title = res.metadata.name;
|
||||||
|
myFile = res.content;
|
||||||
|
myDataType = res.metadata.type;
|
||||||
|
|
||||||
|
var defaultName = Cryptpad.getDefaultName(Cryptpad.parsePadUrl(window.location.href));
|
||||||
|
Title.updateTitle(title || defaultName);
|
||||||
|
APP.toolbar.title.show();
|
||||||
|
Cryptpad.alert("successfully uploaded: " + title);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Cryptpad.rpc.send('UPLOAD_STATUS', '', function (e, pending) {
|
||||||
|
if (e) {
|
||||||
|
console.error(e);
|
||||||
|
return void Cryptpad.alert("something went wrong");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pending[0]) {
|
||||||
|
return void Cryptpad.confirm('upload pending, abort?', function (yes) {
|
||||||
|
if (!yes) { return; }
|
||||||
|
Cryptpad.rpc.send('UPLOAD_CANCEL', '', function (e, res) {
|
||||||
|
if (e) { return void console.error(e); }
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next(again);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var uploadMode = false;
|
||||||
|
|
||||||
|
var andThen = function () {
|
||||||
|
var $bar = $iframe.find('.toolbar-container');
|
||||||
|
|
||||||
|
var secret;
|
||||||
|
var hexFileName;
|
||||||
|
if (window.location.hash) {
|
||||||
|
secret = Cryptpad.getSecrets();
|
||||||
|
if (!secret.keys) { throw new Error("You need a hash"); } // TODO
|
||||||
|
hexFileName = Cryptpad.base64ToHex(secret.channel);
|
||||||
|
} else {
|
||||||
|
uploadMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var getTitle = function () {
|
||||||
|
var pad = Cryptpad.getRelativeHref(window.location.href);
|
||||||
|
var fo = Cryptpad.getStore().getProxy().fo;
|
||||||
|
var data = fo.getFileData(pad);
|
||||||
|
return data ? data.title : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
var exportFile = function () {
|
||||||
|
var suggestion = document.title;
|
||||||
|
Cryptpad.prompt(Messages.exportPrompt,
|
||||||
|
Cryptpad.fixFileName(suggestion), function (filename) {
|
||||||
|
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'];
|
||||||
|
if (secret && hexFileName) {
|
||||||
|
displayed.push('fileshare');
|
||||||
|
}
|
||||||
|
|
||||||
|
var configTb = {
|
||||||
|
displayed: displayed,
|
||||||
|
ifrw: ifrw,
|
||||||
|
common: Cryptpad,
|
||||||
|
title: Title.getTitleConfig(),
|
||||||
|
hideDisplayName: true,
|
||||||
|
$container: $bar
|
||||||
|
};
|
||||||
|
var toolbar = APP.toolbar = Toolbar.create(configTb);
|
||||||
|
|
||||||
|
Title.setToolbar(toolbar);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
var src = Cryptpad.getBlobPathFromHex(hexFileName);
|
||||||
|
return Cryptpad.fetch(src, function (e, u8) {
|
||||||
|
// now decrypt the u8
|
||||||
|
if (e) { return window.alert('error'); }
|
||||||
|
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
||||||
|
var key = Nacl.util.decodeBase64(cryptKey);
|
||||||
|
|
||||||
|
FileCrypto.decrypt(u8, key, function (e, data) {
|
||||||
|
if (e) {
|
||||||
|
Cryptpad.removeLoadingScreen();
|
||||||
|
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);
|
||||||
|
Cryptpad.removeLoadingScreen();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Cryptpad.isLoggedIn()) {
|
||||||
|
return Cryptpad.alert("You must be logged in to upload files");
|
||||||
|
}
|
||||||
|
|
||||||
|
$form.css({
|
||||||
|
display: 'block',
|
||||||
|
});
|
||||||
|
|
||||||
|
var handleFile = function (file) {
|
||||||
|
console.log(file);
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onloadend = function () {
|
||||||
|
upload(this.result, {
|
||||||
|
name: file.name,
|
||||||
|
type: file.type,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
$form.find("#file").on('change', function (e) {
|
||||||
|
var file = e.target.files[0];
|
||||||
|
handleFile(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
$form
|
||||||
|
.on('drag dragstart dragend dragover dragenter dragleave drop', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
})
|
||||||
|
.on('drop', function (e) {
|
||||||
|
var dropped = e.originalEvent.dataTransfer.files;
|
||||||
|
handleFile(dropped[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// we're in upload mode
|
||||||
|
Cryptpad.removeLoadingScreen();
|
||||||
|
};
|
||||||
|
|
||||||
|
Cryptpad.ready(function () {
|
||||||
|
andThen();
|
||||||
|
Cryptpad.reportAppUsage();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,28 +1,16 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html class="cp pad">
|
||||||
<head>
|
<head>
|
||||||
|
<title>CryptPad</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
|
||||||
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
|
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
|
||||||
<link rel="icon" type="image/png"
|
<link rel="icon" type="image/png"
|
||||||
href="/customize/main-favicon.png"
|
href="/customize/main-favicon.png"
|
||||||
|
|
||||||
data-main-favicon="/customize/main-favicon.png"
|
data-main-favicon="/customize/main-favicon.png"
|
||||||
data-alt-favicon="/customize/alt-favicon.png"
|
data-alt-favicon="/customize/alt-favicon.png"
|
||||||
id="favicon" />
|
id="favicon" />
|
||||||
<style>
|
<link rel="stylesheet" href="/customize/main.css" />
|
||||||
input {
|
|
||||||
width: 50vw;
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
max-width: 90vw;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Upload</h1>
|
|
||||||
|
|
||||||
<input type="file">
|
|
||||||
87
www/file/test/main.js
Normal file
87
www/file/test/main.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
define([
|
||||||
|
'jquery',
|
||||||
|
'/bower_components/chainpad-crypto/crypto.js',
|
||||||
|
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
||||||
|
'/common/toolbar.js',
|
||||||
|
'/common/cryptpad-common.js',
|
||||||
|
'/common/visible.js',
|
||||||
|
'/common/notify.js',
|
||||||
|
'/file/file-crypto.js',
|
||||||
|
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||||
|
'/bower_components/file-saver/FileSaver.min.js',
|
||||||
|
], function ($, Crypto, realtimeInput, Toolbar, Cryptpad, Visible, Notify, FileCrypto) {
|
||||||
|
var Nacl = window.nacl;
|
||||||
|
$(function () {
|
||||||
|
|
||||||
|
var filesAreSame = function (a, b) {
|
||||||
|
var l = a.length;
|
||||||
|
if (l !== b.length) { return false; }
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
for (; i < l; i++) { if (a[i] !== b[i]) { return false; } }
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadataIsSame = function (A, B) {
|
||||||
|
return !Object.keys(A).some(function (k) {
|
||||||
|
return A[k] !== B[k];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var upload = function (blob, metadata) {
|
||||||
|
var u8 = new Uint8Array(blob);
|
||||||
|
var key = Nacl.randomBytes(32);
|
||||||
|
|
||||||
|
var next = FileCrypto.encrypt(u8, metadata, key);
|
||||||
|
|
||||||
|
var chunks = [];
|
||||||
|
var sendChunk = function (box, cb) {
|
||||||
|
chunks.push(box);
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
var again = function (err, box) {
|
||||||
|
if (err) { throw new Error(err); }
|
||||||
|
|
||||||
|
if (box) {
|
||||||
|
return void sendChunk(box, function (e) {
|
||||||
|
if (e) {
|
||||||
|
console.error(e);
|
||||||
|
return Cryptpad.alert('Something went wrong');
|
||||||
|
}
|
||||||
|
next(again);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// check if the uploaded file can be decrypted
|
||||||
|
var newU8 = FileCrypto.joinChunks(chunks);
|
||||||
|
|
||||||
|
console.log('encrypted file with metadata is %s uint8s', newU8.length);
|
||||||
|
FileCrypto.decrypt(newU8, key, function (e, res) {
|
||||||
|
if (e) { return Cryptpad.alert(e); }
|
||||||
|
|
||||||
|
if (filesAreSame(blob, res.content) &&
|
||||||
|
metadataIsSame(res.metadata, metadata)) {
|
||||||
|
Cryptpad.alert("successfully uploaded");
|
||||||
|
} else {
|
||||||
|
Cryptpad.alert('encryption failure!');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
next(again);
|
||||||
|
};
|
||||||
|
|
||||||
|
var andThen = function () {
|
||||||
|
var src = '/customize/cryptofist_mini.png';
|
||||||
|
Cryptpad.fetch(src, function (e, file) {
|
||||||
|
console.log('original file is %s uint8s', file.length);
|
||||||
|
upload(file, {
|
||||||
|
pew: 'pew',
|
||||||
|
bang: 'bang',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
andThen();
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,11 +3,6 @@ define([
|
|||||||
'/common/cryptpad-common.js',
|
'/common/cryptpad-common.js',
|
||||||
'/common/login.js'
|
'/common/login.js'
|
||||||
], function ($, Cryptpad, Login) {
|
], function ($, Cryptpad, Login) {
|
||||||
|
|
||||||
var APP = window.APP = {
|
|
||||||
Cryptpad: Cryptpad,
|
|
||||||
};
|
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
var $main = $('#mainBlock');
|
var $main = $('#mainBlock');
|
||||||
var Messages = Cryptpad.Messages;
|
var Messages = Cryptpad.Messages;
|
||||||
@@ -61,66 +56,69 @@ define([
|
|||||||
$('button.login').click();
|
$('button.login').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('button.login').click(function (e) {
|
$('button.login').click(function () {
|
||||||
Cryptpad.addLoadingScreen(Messages.login_hashing);
|
// setTimeout 100ms to remove the keyboard on mobile devices before the loading screen pops up
|
||||||
// We need a setTimeout(cb, 0) otherwise the loading screen is only displayed after hashing the password
|
|
||||||
window.setTimeout(function () {
|
window.setTimeout(function () {
|
||||||
loginReady(function () {
|
Cryptpad.addLoadingScreen(Messages.login_hashing);
|
||||||
var uname = $uname.val();
|
// We need a setTimeout(cb, 0) otherwise the loading screen is only displayed after hashing the password
|
||||||
var passwd = $passwd.val();
|
window.setTimeout(function () {
|
||||||
Login.loginOrRegister(uname, passwd, false, function (err, result) {
|
loginReady(function () {
|
||||||
if (!err) {
|
var uname = $uname.val();
|
||||||
var proxy = result.proxy;
|
var passwd = $passwd.val();
|
||||||
|
Login.loginOrRegister(uname, passwd, false, function (err, result) {
|
||||||
|
if (!err) {
|
||||||
|
var proxy = result.proxy;
|
||||||
|
|
||||||
// successful validation and user already exists
|
// successful validation and user already exists
|
||||||
// set user hash in localStorage and redirect to drive
|
// set user hash in localStorage and redirect to drive
|
||||||
if (!proxy.login_name) {
|
if (!proxy.login_name) {
|
||||||
result.proxy.login_name = result.userName;
|
result.proxy.login_name = result.userName;
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy.edPrivate = result.edPrivate;
|
proxy.edPrivate = result.edPrivate;
|
||||||
proxy.edPublic = result.edPublic;
|
proxy.edPublic = result.edPublic;
|
||||||
|
|
||||||
Cryptpad.feedback('LOGIN', true);
|
Cryptpad.feedback('LOGIN', true);
|
||||||
Cryptpad.whenRealtimeSyncs(result.realtime, function() {
|
Cryptpad.whenRealtimeSyncs(result.realtime, function() {
|
||||||
Cryptpad.login(result.userHash, result.userName, function () {
|
Cryptpad.login(result.userHash, result.userName, function () {
|
||||||
if (sessionStorage.redirectTo) {
|
if (sessionStorage.redirectTo) {
|
||||||
var h = sessionStorage.redirectTo;
|
var h = sessionStorage.redirectTo;
|
||||||
var parser = document.createElement('a');
|
var parser = document.createElement('a');
|
||||||
parser.href = h;
|
parser.href = h;
|
||||||
if (parser.origin === window.location.origin) {
|
if (parser.origin === window.location.origin) {
|
||||||
delete sessionStorage.redirectTo;
|
delete sessionStorage.redirectTo;
|
||||||
window.location.href = h;
|
window.location.href = h;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
window.location.href = '/drive/';
|
||||||
window.location.href = '/drive/';
|
});
|
||||||
});
|
});
|
||||||
});
|
return;
|
||||||
return;
|
}
|
||||||
}
|
switch (err) {
|
||||||
switch (err) {
|
case 'NO_SUCH_USER':
|
||||||
case 'NO_SUCH_USER':
|
Cryptpad.removeLoadingScreen(function () {
|
||||||
Cryptpad.removeLoadingScreen(function () {
|
Cryptpad.alert(Messages.login_noSuchUser);
|
||||||
Cryptpad.alert(Messages.login_noSuchUser);
|
});
|
||||||
});
|
break;
|
||||||
break;
|
case 'INVAL_USER':
|
||||||
case 'INVAL_USER':
|
Cryptpad.removeLoadingScreen(function () {
|
||||||
Cryptpad.removeLoadingScreen(function () {
|
Cryptpad.alert(Messages.login_invalUser);
|
||||||
Cryptpad.alert(Messages.login_invalUser);
|
});
|
||||||
});
|
break;
|
||||||
break;
|
case 'INVAL_PASS':
|
||||||
case 'INVAL_PASS':
|
Cryptpad.removeLoadingScreen(function () {
|
||||||
Cryptpad.removeLoadingScreen(function () {
|
Cryptpad.alert(Messages.login_invalPass);
|
||||||
Cryptpad.alert(Messages.login_invalPass);
|
});
|
||||||
});
|
break;
|
||||||
break;
|
default: // UNHANDLED ERROR
|
||||||
default: // UNHANDLED ERROR
|
Cryptpad.errorLoadingScreen(Messages.login_unhandledError);
|
||||||
Cryptpad.errorLoadingScreen(Messages.login_unhandledError);
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
});
|
}, 0);
|
||||||
}, 0);
|
}, 100);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
BIN
www/media/assets/image.png-encrypted
Normal file
BIN
www/media/assets/image.png-encrypted
Normal file
Binary file not shown.
47
www/media/index.html
Normal file
47
www/media/index.html
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="cp pad">
|
||||||
|
<head>
|
||||||
|
<title>CryptPad</title>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
|
||||||
|
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
|
||||||
|
<link rel="icon" type="image/png"
|
||||||
|
href="/customize/main-favicon.png"
|
||||||
|
data-main-favicon="/customize/main-favicon.png"
|
||||||
|
data-alt-favicon="/customize/alt-favicon.png"
|
||||||
|
id="favicon" />
|
||||||
|
<link rel="stylesheet" href="/customize/main.css" />
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
#pad-iframe {
|
||||||
|
position:fixed;
|
||||||
|
top:0px;
|
||||||
|
left:0px;
|
||||||
|
bottom:0px;
|
||||||
|
right:0px;
|
||||||
|
width:100%;
|
||||||
|
height:100%;
|
||||||
|
border:none;
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<iframe id="pad-iframe"></iframe><script src="/common/noscriptfix.js"></script>
|
||||||
|
<div id="loading">
|
||||||
|
<div class="loadingContainer">
|
||||||
|
<img class="cryptofist" src="/customize/cryptofist_small.png" />
|
||||||
|
<div class="spinnerContainer">
|
||||||
|
<span class="fa fa-spinner fa-pulse fa-4x fa-fw"></span>
|
||||||
|
</div>
|
||||||
|
<p data-localization="loading"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
|
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
|
||||||
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
|
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
|
||||||
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
|
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
|
||||||
<script src="/bower_components/ckeditor/ckeditor.js"></script>
|
|
||||||
<style>
|
<style>
|
||||||
html, body {
|
html, body {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
@@ -15,6 +14,9 @@
|
|||||||
padding: 0px;
|
padding: 0px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
media-tag * {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
106
www/media/main.js
Normal file
106
www/media/main.js
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
define([
|
||||||
|
'jquery',
|
||||||
|
'/bower_components/chainpad-crypto/crypto.js',
|
||||||
|
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
||||||
|
'/common/toolbar.js',
|
||||||
|
'/common/cryptpad-common.js',
|
||||||
|
//'/common/visible.js',
|
||||||
|
//'/common/notify.js',
|
||||||
|
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||||
|
'/bower_components/file-saver/FileSaver.min.js',
|
||||||
|
], function ($, Crypto, realtimeInput, Toolbar, Cryptpad /*, Visible, Notify*/) {
|
||||||
|
//var Messages = Cryptpad.Messages;
|
||||||
|
//var saveAs = window.saveAs;
|
||||||
|
//window.Nacl = window.nacl;
|
||||||
|
$(function () {
|
||||||
|
|
||||||
|
var ifrw = $('#pad-iframe')[0].contentWindow;
|
||||||
|
var $iframe = $('#pad-iframe').contents();
|
||||||
|
|
||||||
|
Cryptpad.addLoadingScreen();
|
||||||
|
|
||||||
|
var andThen = function () {
|
||||||
|
var $bar = $iframe.find('.toolbar-container');
|
||||||
|
var secret = Cryptpad.getSecrets();
|
||||||
|
|
||||||
|
if (!secret.keys) { throw new Error("You need a hash"); } // TODO
|
||||||
|
|
||||||
|
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
||||||
|
var fileId = secret.channel;
|
||||||
|
var hexFileName = Cryptpad.base64ToHex(fileId);
|
||||||
|
var type = "image/png";
|
||||||
|
|
||||||
|
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
||||||
|
var defaultName = Cryptpad.getDefaultName(parsed);
|
||||||
|
|
||||||
|
var getTitle = function () {
|
||||||
|
var pad = Cryptpad.getRelativeHref(window.location.href);
|
||||||
|
var fo = Cryptpad.getStore().getProxy().fo;
|
||||||
|
var data = fo.getFileData(pad);
|
||||||
|
return data ? data.title : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
var updateTitle = function (newTitle) {
|
||||||
|
Cryptpad.renamePad(newTitle, function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
console.log("Couldn't set pad title");
|
||||||
|
console.error(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.title = newTitle;
|
||||||
|
$bar.find('.' + Toolbar.constants.title).find('span.title').text(data);
|
||||||
|
$bar.find('.' + Toolbar.constants.title).find('input').val(data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var suggestName = function () {
|
||||||
|
return document.title || getTitle() || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
var renameCb = function (err, title) {
|
||||||
|
document.title = title;
|
||||||
|
};
|
||||||
|
|
||||||
|
var $mt = $iframe.find('#encryptedFile');
|
||||||
|
$mt.attr('src', '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName);
|
||||||
|
$mt.attr('data-crypto-key', 'cryptpad:'+cryptKey);
|
||||||
|
$mt.attr('data-type', type);
|
||||||
|
|
||||||
|
window.onMediaMetadata = function (metadata) {
|
||||||
|
if (!metadata || metadata.name !== defaultName) { return; }
|
||||||
|
var title = document.title = metadata.name;
|
||||||
|
updateTitle(title || defaultName);
|
||||||
|
};
|
||||||
|
|
||||||
|
require(['/common/media-tag.js'], function (MediaTag) {
|
||||||
|
var configTb = {
|
||||||
|
displayed: ['useradmin', 'share', 'newpad'],
|
||||||
|
ifrw: ifrw,
|
||||||
|
common: Cryptpad,
|
||||||
|
title: {
|
||||||
|
onRename: renameCb,
|
||||||
|
defaultName: defaultName,
|
||||||
|
suggestName: suggestName
|
||||||
|
},
|
||||||
|
share: {
|
||||||
|
secret: secret,
|
||||||
|
channel: hexFileName
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Toolbar.create($bar, null, null, null, null, configTb);
|
||||||
|
|
||||||
|
updateTitle(Cryptpad.initialName || getTitle() || defaultName);
|
||||||
|
|
||||||
|
MediaTag($mt[0]);
|
||||||
|
|
||||||
|
Cryptpad.removeLoadingScreen();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Cryptpad.ready(function () {
|
||||||
|
andThen();
|
||||||
|
Cryptpad.reportAppUsage();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -42,7 +42,7 @@ define(function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (editor.contextMenu) {
|
if (editor.contextMenu) {
|
||||||
editor.contextMenu.addListener(function(startElement, selection, path) {
|
editor.contextMenu.addListener(function(startElement) {
|
||||||
if (startElement) {
|
if (startElement) {
|
||||||
var anchor = getActiveLink(editor);
|
var anchor = getActiveLink(editor);
|
||||||
if (anchor && anchor.getAttribute('href')) {
|
if (anchor && anchor.getAttribute('href')) {
|
||||||
|
|||||||
301
www/pad/main.js
301
www/pad/main.js
@@ -3,7 +3,7 @@ define([
|
|||||||
'/bower_components/chainpad-crypto/crypto.js',
|
'/bower_components/chainpad-crypto/crypto.js',
|
||||||
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
||||||
'/bower_components/hyperjson/hyperjson.js',
|
'/bower_components/hyperjson/hyperjson.js',
|
||||||
'/common/toolbar.js',
|
'/common/toolbar2.js',
|
||||||
'/common/cursor.js',
|
'/common/cursor.js',
|
||||||
'/bower_components/chainpad-json-validator/json-ot.js',
|
'/bower_components/chainpad-json-validator/json-ot.js',
|
||||||
'/common/TypingTests.js',
|
'/common/TypingTests.js',
|
||||||
@@ -11,14 +11,11 @@ define([
|
|||||||
'/bower_components/textpatcher/TextPatcher.js',
|
'/bower_components/textpatcher/TextPatcher.js',
|
||||||
'/common/cryptpad-common.js',
|
'/common/cryptpad-common.js',
|
||||||
'/common/cryptget.js',
|
'/common/cryptget.js',
|
||||||
'/common/visible.js',
|
|
||||||
'/common/notify.js',
|
|
||||||
'/pad/links.js',
|
'/pad/links.js',
|
||||||
'/bower_components/file-saver/FileSaver.min.js',
|
'/bower_components/file-saver/FileSaver.min.js',
|
||||||
'/bower_components/diff-dom/diffDOM.js'
|
'/bower_components/diff-dom/diffDOM.js'
|
||||||
], function ($, Crypto, realtimeInput, Hyperjson,
|
], function ($, Crypto, realtimeInput, Hyperjson,
|
||||||
Toolbar, Cursor, JsonOT, TypingTest, JSONSortify, TextPatcher, Cryptpad, Cryptget,
|
Toolbar, Cursor, JsonOT, TypingTest, JSONSortify, TextPatcher, Cryptpad, Cryptget, Links) {
|
||||||
Visible, Notify, Links) {
|
|
||||||
var saveAs = window.saveAs;
|
var saveAs = window.saveAs;
|
||||||
var Messages = Cryptpad.Messages;
|
var Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
@@ -87,7 +84,7 @@ define([
|
|||||||
return hj;
|
return hj;
|
||||||
};
|
};
|
||||||
|
|
||||||
var onConnectError = function (info) {
|
var onConnectError = function () {
|
||||||
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -103,10 +100,8 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
editor.on('instanceReady', Links.addSupportForOpeningLinksInNewTab(Ckeditor));
|
editor.on('instanceReady', Links.addSupportForOpeningLinksInNewTab(Ckeditor));
|
||||||
editor.on('instanceReady', function (Ckeditor) {
|
editor.on('instanceReady', function () {
|
||||||
var $bar = $('#pad-iframe')[0].contentWindow.$('#cke_1_toolbox');
|
var $bar = $('#pad-iframe')[0].contentWindow.$('#cke_1_toolbox');
|
||||||
var parsedHash = Cryptpad.parsePadUrl(window.location.href);
|
|
||||||
var defaultName = Cryptpad.getDefaultName(parsedHash);
|
|
||||||
|
|
||||||
var isHistoryMode = false;
|
var isHistoryMode = false;
|
||||||
|
|
||||||
@@ -167,7 +162,7 @@ define([
|
|||||||
if (['addAttribute', 'modifyAttribute'].indexOf(info.diff.action) !== -1) {
|
if (['addAttribute', 'modifyAttribute'].indexOf(info.diff.action) !== -1) {
|
||||||
if (info.diff.name === 'href') {
|
if (info.diff.name === 'href') {
|
||||||
// console.log(info.diff);
|
// console.log(info.diff);
|
||||||
var href = info.diff.newValue;
|
//var href = info.diff.newValue;
|
||||||
|
|
||||||
// TODO normalize HTML entities
|
// TODO normalize HTML entities
|
||||||
if (/javascript *: */.test(info.diff.newValue)) {
|
if (/javascript *: */.test(info.diff.newValue)) {
|
||||||
@@ -277,56 +272,10 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
var initializing = true;
|
var initializing = true;
|
||||||
var userData = module.userData = {}; // List of pretty names for all users (mapped with their ID)
|
|
||||||
var userList; // List of users still connected to the channel (server IDs)
|
|
||||||
var addToUserData = function(data) {
|
|
||||||
var users = module.users;
|
|
||||||
for (var attrname in data) { userData[attrname] = data[attrname]; }
|
|
||||||
|
|
||||||
if (users && users.length) {
|
var Title;
|
||||||
for (var userKey in userData) {
|
var UserList;
|
||||||
if (users.indexOf(userKey) === -1) { delete userData[userKey]; }
|
var Metadata;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(userList && typeof userList.onChange === "function") {
|
|
||||||
userList.onChange(userData);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var myData = {};
|
|
||||||
var myUserName = ''; // My "pretty name"
|
|
||||||
var myID; // My server ID
|
|
||||||
|
|
||||||
var setMyID = function(info) {
|
|
||||||
myID = info.myID || null;
|
|
||||||
};
|
|
||||||
|
|
||||||
var setName = module.setName = function (newName) {
|
|
||||||
if (typeof(newName) !== 'string') { return; }
|
|
||||||
var myUserNameTemp = newName.trim();
|
|
||||||
if(myUserNameTemp.length > 32) {
|
|
||||||
myUserNameTemp = myUserNameTemp.substr(0, 32);
|
|
||||||
}
|
|
||||||
myUserName = myUserNameTemp;
|
|
||||||
myData[myID] = {
|
|
||||||
name: myUserName,
|
|
||||||
uid: Cryptpad.getUid(),
|
|
||||||
};
|
|
||||||
addToUserData(myData);
|
|
||||||
Cryptpad.setAttribute('username', newName, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.error("Couldn't set username");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
editor.fire('change');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var isDefaultTitle = function () {
|
|
||||||
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
|
||||||
return Cryptpad.isDefaultName(parsed, document.title);
|
|
||||||
};
|
|
||||||
|
|
||||||
var getHeadingText = function () {
|
var getHeadingText = function () {
|
||||||
var text;
|
var text;
|
||||||
@@ -339,14 +288,6 @@ define([
|
|||||||
})) { return text; }
|
})) { return text; }
|
||||||
};
|
};
|
||||||
|
|
||||||
var suggestName = function (fallback) {
|
|
||||||
if (document.title === defaultName) {
|
|
||||||
return getHeadingText() || fallback || "";
|
|
||||||
} else {
|
|
||||||
return document.title || getHeadingText() || defaultName;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var DD = new DiffDom(diffOptions);
|
var DD = new DiffDom(diffOptions);
|
||||||
|
|
||||||
// apply patches, and try not to lose the cursor in the process!
|
// apply patches, and try not to lose the cursor in the process!
|
||||||
@@ -364,12 +305,12 @@ define([
|
|||||||
var hjson = Hyperjson.fromDOM(dom, isNotMagicLine, brFilter);
|
var hjson = Hyperjson.fromDOM(dom, isNotMagicLine, brFilter);
|
||||||
hjson[3] = {
|
hjson[3] = {
|
||||||
metadata: {
|
metadata: {
|
||||||
users: userData,
|
users: UserList.userData,
|
||||||
defaultTitle: defaultName
|
defaultTitle: Title.defaultTitle
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (!initializing) {
|
if (!initializing) {
|
||||||
hjson[3].metadata.title = document.title;
|
hjson[3].metadata.title = Title.title;
|
||||||
} else if (Cryptpad.initialName && !hjson[3].metadata.title) {
|
} else if (Cryptpad.initialName && !hjson[3].metadata.title) {
|
||||||
hjson[3].metadata.title = Cryptpad.initialName;
|
hjson[3].metadata.title = Cryptpad.initialName;
|
||||||
}
|
}
|
||||||
@@ -390,9 +331,6 @@ define([
|
|||||||
validateKey: secret.keys.validateKey || undefined,
|
validateKey: secret.keys.validateKey || undefined,
|
||||||
readOnly: readOnly,
|
readOnly: readOnly,
|
||||||
|
|
||||||
// method which allows us to get the id of the user
|
|
||||||
setMyID: setMyID,
|
|
||||||
|
|
||||||
// Pass in encrypt and decrypt methods
|
// Pass in encrypt and decrypt methods
|
||||||
crypto: Crypto.createEncryptor(secret.keys),
|
crypto: Crypto.createEncryptor(secret.keys),
|
||||||
|
|
||||||
@@ -421,69 +359,7 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var updateTitle = function (newTitle) {
|
realtimeOptions.onRemote = function () {
|
||||||
if (newTitle === document.title) { return; }
|
|
||||||
// Change the title now, and set it back to the old value if there is an error
|
|
||||||
var oldTitle = document.title;
|
|
||||||
document.title = newTitle;
|
|
||||||
Cryptpad.renamePad(newTitle, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.log("Couldn't set pad title");
|
|
||||||
console.error(err);
|
|
||||||
document.title = oldTitle;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
document.title = data;
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('span.title').text(data);
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('input').val(data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateDefaultTitle = function (defaultTitle) {
|
|
||||||
defaultName = defaultTitle;
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('input').attr("placeholder", defaultName);
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateMetadata = function(shjson) {
|
|
||||||
// Extract the user list (metadata) from the hyperjson
|
|
||||||
if (!shjson || typeof (shjson) !== "string") { updateTitle(defaultName); return; }
|
|
||||||
var hjson = JSON.parse(shjson);
|
|
||||||
var peerMetadata = hjson[3];
|
|
||||||
var titleUpdated = false;
|
|
||||||
if (peerMetadata && peerMetadata.metadata) {
|
|
||||||
if (peerMetadata.metadata.users) {
|
|
||||||
var userData = peerMetadata.metadata.users;
|
|
||||||
// Update the local user data
|
|
||||||
addToUserData(userData);
|
|
||||||
}
|
|
||||||
if (peerMetadata.metadata.defaultTitle) {
|
|
||||||
updateDefaultTitle(peerMetadata.metadata.defaultTitle);
|
|
||||||
}
|
|
||||||
if (typeof peerMetadata.metadata.title !== "undefined") {
|
|
||||||
updateTitle(peerMetadata.metadata.title || defaultName);
|
|
||||||
titleUpdated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!titleUpdated) {
|
|
||||||
updateTitle(defaultName);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var unnotify = function () {
|
|
||||||
if (module.tabNotification &&
|
|
||||||
typeof(module.tabNotification.cancel) === 'function') {
|
|
||||||
module.tabNotification.cancel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var notify = function () {
|
|
||||||
if (Visible.isSupported() && !Visible.currently()) {
|
|
||||||
unnotify();
|
|
||||||
module.tabNotification = Notify.tab(1000, 10);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var onRemote = realtimeOptions.onRemote = function () {
|
|
||||||
if (initializing) { return; }
|
if (initializing) { return; }
|
||||||
if (isHistoryMode) { return; }
|
if (isHistoryMode) { return; }
|
||||||
|
|
||||||
@@ -495,7 +371,7 @@ define([
|
|||||||
cursor.update();
|
cursor.update();
|
||||||
|
|
||||||
// Update the user list (metadata) from the hyperjson
|
// Update the user list (metadata) from the hyperjson
|
||||||
updateMetadata(shjson);
|
Metadata.update(shjson);
|
||||||
|
|
||||||
var newInner = JSON.parse(shjson);
|
var newInner = JSON.parse(shjson);
|
||||||
var newSInner;
|
var newSInner;
|
||||||
@@ -540,11 +416,11 @@ define([
|
|||||||
// Notify only when the content has changed, not when someone has joined/left
|
// Notify only when the content has changed, not when someone has joined/left
|
||||||
var oldSInner = stringify(JSON.parse(oldShjson)[2]);
|
var oldSInner = stringify(JSON.parse(oldShjson)[2]);
|
||||||
if (newSInner && newSInner !== oldSInner) {
|
if (newSInner && newSInner !== oldSInner) {
|
||||||
notify();
|
Cryptpad.notify();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var getHTML = function (Dom) {
|
var getHTML = function () {
|
||||||
return ('<!DOCTYPE html>\n' + '<html>\n' + inner.innerHTML);
|
return ('<!DOCTYPE html>\n' + '<html>\n' + inner.innerHTML);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -554,7 +430,7 @@ define([
|
|||||||
|
|
||||||
var exportFile = function () {
|
var exportFile = function () {
|
||||||
var html = getHTML();
|
var html = getHTML();
|
||||||
var suggestion = suggestName('cryptpad-document');
|
var suggestion = Title.suggestTitle('cryptpad-document');
|
||||||
Cryptpad.prompt(Messages.exportPrompt,
|
Cryptpad.prompt(Messages.exportPrompt,
|
||||||
Cryptpad.fixFileName(suggestion) + '.html', function (filename) {
|
Cryptpad.fixFileName(suggestion) + '.html', function (filename) {
|
||||||
if (!(typeof(filename) === 'string' && filename)) { return; }
|
if (!(typeof(filename) === 'string' && filename)) { return; }
|
||||||
@@ -562,45 +438,42 @@ define([
|
|||||||
saveAs(blob, filename);
|
saveAs(blob, filename);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var importFile = function (content, file) {
|
var importFile = function (content) {
|
||||||
var shjson = stringify(Hyperjson.fromDOM(domFromHTML(content).body));
|
var shjson = stringify(Hyperjson.fromDOM(domFromHTML(content).body));
|
||||||
applyHjson(shjson);
|
applyHjson(shjson);
|
||||||
realtimeOptions.onLocal();
|
realtimeOptions.onLocal();
|
||||||
};
|
};
|
||||||
|
|
||||||
var renameCb = function (err, title) {
|
realtimeOptions.onInit = function (info) {
|
||||||
if (err) { return; }
|
UserList = Cryptpad.createUserList(info, realtimeOptions.onLocal, Cryptget, Cryptpad);
|
||||||
document.title = title;
|
|
||||||
editor.fire('change');
|
|
||||||
};
|
|
||||||
|
|
||||||
var onInit = realtimeOptions.onInit = function (info) {
|
var titleCfg = { getHeadingText: getHeadingText };
|
||||||
userList = info.userList;
|
Title = Cryptpad.createTitle(titleCfg, realtimeOptions.onLocal, Cryptpad);
|
||||||
|
|
||||||
|
Metadata = Cryptpad.createMetadata(UserList, Title);
|
||||||
|
|
||||||
var configTb = {
|
var configTb = {
|
||||||
displayed: ['useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad'],
|
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'],
|
||||||
userData: userData,
|
userList: UserList.getToolbarConfig(),
|
||||||
readOnly: readOnly,
|
|
||||||
ifrw: ifrw,
|
|
||||||
share: {
|
share: {
|
||||||
secret: secret,
|
secret: secret,
|
||||||
channel: info.channel
|
channel: info.channel
|
||||||
},
|
},
|
||||||
title: {
|
title: Title.getTitleConfig(),
|
||||||
onRename: renameCb,
|
common: Cryptpad,
|
||||||
defaultName: defaultName,
|
readOnly: readOnly,
|
||||||
suggestName: suggestName
|
ifrw: ifrw,
|
||||||
},
|
realtime: info.realtime,
|
||||||
common: Cryptpad
|
network: info.network,
|
||||||
|
$container: $bar
|
||||||
};
|
};
|
||||||
toolbar = info.realtime.toolbar = Toolbar.create($bar, info.myID, info.realtime, info.getLag, userList, configTb);
|
toolbar = info.realtime.toolbar = Toolbar.create(configTb);
|
||||||
|
|
||||||
var $rightside = $bar.find('.' + Toolbar.constants.rightside);
|
Title.setToolbar(toolbar);
|
||||||
var $userBlock = $bar.find('.' + Toolbar.constants.username);
|
|
||||||
var $usernameButton = module.$userNameButton = $($bar.find('.' + Toolbar.constants.changeUsername));
|
var $rightside = toolbar.$rightside;
|
||||||
|
|
||||||
var editHash;
|
var editHash;
|
||||||
var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys);
|
|
||||||
|
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
||||||
@@ -626,31 +499,13 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* add a history button */
|
/* add a history button */
|
||||||
var histConfig = {};
|
var histConfig = {
|
||||||
histConfig.onRender = function (val) {
|
onLocal: realtimeOptions.onLocal(),
|
||||||
if (typeof val === "undefined") { return; }
|
onRemote: realtimeOptions.onRemote(),
|
||||||
try {
|
setHistory: setHistory,
|
||||||
applyHjson(val || '["BODY",{},[]]');
|
applyVal: function (val) { applyHjson(val || '["BODY",{},[]]'); },
|
||||||
} catch (e) {
|
$toolbar: $bar
|
||||||
// Probably a parse error
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
histConfig.onClose = function () {
|
|
||||||
// Close button clicked
|
|
||||||
setHistory(false, true);
|
|
||||||
};
|
|
||||||
histConfig.onRevert = function () {
|
|
||||||
// Revert button clicked
|
|
||||||
setHistory(false, false);
|
|
||||||
realtimeOptions.onLocal();
|
|
||||||
realtimeOptions.onRemote();
|
|
||||||
};
|
|
||||||
histConfig.onReady = function () {
|
|
||||||
// Called when the history is loaded and the UI displayed
|
|
||||||
setHistory(true);
|
|
||||||
};
|
|
||||||
histConfig.$toolbar = $bar;
|
|
||||||
var $hist = Cryptpad.createButton('history', true, {histConfig: histConfig});
|
var $hist = Cryptpad.createButton('history', true, {histConfig: histConfig});
|
||||||
$rightside.append($hist);
|
$rightside.append($hist);
|
||||||
|
|
||||||
@@ -673,14 +528,10 @@ define([
|
|||||||
/* add an import button */
|
/* add an import button */
|
||||||
var $import = Cryptpad.createButton('import', true, {}, importFile);
|
var $import = Cryptpad.createButton('import', true, {}, importFile);
|
||||||
$rightside.append($import);
|
$rightside.append($import);
|
||||||
|
|
||||||
/* add a rename button */
|
|
||||||
//var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb);
|
|
||||||
//$rightside.append($setTitle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add a forget button */
|
/* add a forget button */
|
||||||
var forgetCb = function (err, title) {
|
var forgetCb = function (err) {
|
||||||
if (err) { return; }
|
if (err) { return; }
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
};
|
};
|
||||||
@@ -689,12 +540,10 @@ define([
|
|||||||
|
|
||||||
// set the hash
|
// set the hash
|
||||||
if (!readOnly) { Cryptpad.replaceHash(editHash); }
|
if (!readOnly) { Cryptpad.replaceHash(editHash); }
|
||||||
|
|
||||||
Cryptpad.onDisplayNameChanged(setName);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// this should only ever get called once, when the chain syncs
|
// this should only ever get called once, when the chain syncs
|
||||||
var onReady = realtimeOptions.onReady = function (info) {
|
realtimeOptions.onReady = function (info) {
|
||||||
if (!module.isMaximized) {
|
if (!module.isMaximized) {
|
||||||
editor.execCommand('maximize');
|
editor.execCommand('maximize');
|
||||||
module.isMaximized = true;
|
module.isMaximized = true;
|
||||||
@@ -713,7 +562,6 @@ define([
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.users = info.userList.users;
|
|
||||||
module.realtime = info.realtime;
|
module.realtime = info.realtime;
|
||||||
|
|
||||||
var shjson = module.realtime.getUserDoc();
|
var shjson = module.realtime.getUserDoc();
|
||||||
@@ -725,13 +573,7 @@ define([
|
|||||||
applyHjson(shjson);
|
applyHjson(shjson);
|
||||||
|
|
||||||
// Update the user list (metadata) from the hyperjson
|
// Update the user list (metadata) from the hyperjson
|
||||||
updateMetadata(shjson);
|
Metadata.update(shjson);
|
||||||
|
|
||||||
if (Visible.isSupported()) {
|
|
||||||
Visible.onChange(function (yes) {
|
|
||||||
if (yes) { unnotify(); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
var shjson2 = stringifyDOM(inner);
|
var shjson2 = stringifyDOM(inner);
|
||||||
@@ -745,42 +587,25 @@ define([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updateTitle(Cryptpad.initialName || defaultName);
|
Title.updateTitle(Cryptpad.initialName || Title.defaultTitle);
|
||||||
documentBody.innerHTML = Messages.initialState;
|
documentBody.innerHTML = Messages.initialState;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cryptpad.getLastName(function (err, lastName) {
|
Cryptpad.removeLoadingScreen(emitResize);
|
||||||
console.log("Unlocking editor");
|
setEditable(!readOnly);
|
||||||
setEditable(!readOnly);
|
initializing = false;
|
||||||
initializing = false;
|
|
||||||
Cryptpad.removeLoadingScreen(emitResize);
|
|
||||||
|
|
||||||
// Update the toolbar list:
|
if (readOnly) { return; }
|
||||||
// Add the current user in the metadata if he has edit rights
|
UserList.getLastName(toolbar.$userNameButton, newPad);
|
||||||
if (readOnly) { return; }
|
editor.focus();
|
||||||
if (typeof(lastName) === 'string') {
|
if (newPad) {
|
||||||
setName(lastName);
|
cursor.setToEnd();
|
||||||
} else {
|
} else {
|
||||||
myData[myID] = {
|
cursor.setToStart();
|
||||||
name: "",
|
}
|
||||||
uid: Cryptpad.getUid()
|
|
||||||
};
|
|
||||||
addToUserData(myData);
|
|
||||||
realtimeOptions.onLocal();
|
|
||||||
module.$userNameButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.focus();
|
|
||||||
if (newPad) {
|
|
||||||
Cryptpad.selectTemplate('pad', info.realtime, Cryptget);
|
|
||||||
cursor.setToEnd();
|
|
||||||
} else {
|
|
||||||
cursor.setToStart();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var onAbort = realtimeOptions.onAbort = function (info) {
|
realtimeOptions.onAbort = function () {
|
||||||
console.log("Aborting the session!");
|
console.log("Aborting the session!");
|
||||||
// stop the user from continuing to edit
|
// stop the user from continuing to edit
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
@@ -788,7 +613,7 @@ define([
|
|||||||
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
var onConnectionChange = realtimeOptions.onConnectionChange = function (info) {
|
realtimeOptions.onConnectionChange = function (info) {
|
||||||
setEditable(info.state);
|
setEditable(info.state);
|
||||||
toolbar.failed();
|
toolbar.failed();
|
||||||
if (info.state) {
|
if (info.state) {
|
||||||
@@ -800,7 +625,7 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var onError = realtimeOptions.onError = onConnectError;
|
realtimeOptions.onError = onConnectError;
|
||||||
|
|
||||||
var onLocal = realtimeOptions.onLocal = function () {
|
var onLocal = realtimeOptions.onLocal = function () {
|
||||||
if (initializing) { return; }
|
if (initializing) { return; }
|
||||||
@@ -816,7 +641,7 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var rti = module.realtimeInput = realtimeInput.start(realtimeOptions);
|
module.realtimeInput = realtimeInput.start(realtimeOptions);
|
||||||
|
|
||||||
Cryptpad.onLogout(function () { setEditable(false); });
|
Cryptpad.onLogout(function () { setEditable(false); });
|
||||||
|
|
||||||
@@ -834,7 +659,7 @@ define([
|
|||||||
// export the typing tests to the window.
|
// export the typing tests to the window.
|
||||||
// call like `test = easyTest()`
|
// call like `test = easyTest()`
|
||||||
// terminate the test like `test.cancel()`
|
// terminate the test like `test.cancel()`
|
||||||
var easyTest = window.easyTest = function () {
|
window.easyTest = function () {
|
||||||
cursor.update();
|
cursor.update();
|
||||||
var start = cursor.Range.start;
|
var start = cursor.Range.start;
|
||||||
var test = TypingTest.testInput(inner, start.el, start.offset, onLocal);
|
var test = TypingTest.testInput(inner, start.el, start.offset, onLocal);
|
||||||
@@ -846,7 +671,7 @@ define([
|
|||||||
|
|
||||||
var interval = 100;
|
var interval = 100;
|
||||||
var second = function (Ckeditor) {
|
var second = function (Ckeditor) {
|
||||||
Cryptpad.ready(function (err, env) {
|
Cryptpad.ready(function () {
|
||||||
andThen(Ckeditor);
|
andThen(Ckeditor);
|
||||||
Cryptpad.reportAppUsage();
|
Cryptpad.reportAppUsage();
|
||||||
});
|
});
|
||||||
|
|||||||
510
www/poll/main.js
510
www/poll/main.js
@@ -7,11 +7,9 @@ define([
|
|||||||
'/common/cryptget.js',
|
'/common/cryptget.js',
|
||||||
'/bower_components/hyperjson/hyperjson.js',
|
'/bower_components/hyperjson/hyperjson.js',
|
||||||
'render.js',
|
'render.js',
|
||||||
'/common/toolbar.js',
|
'/common/toolbar2.js',
|
||||||
'/common/visible.js',
|
|
||||||
'/common/notify.js',
|
|
||||||
'/bower_components/file-saver/FileSaver.min.js'
|
'/bower_components/file-saver/FileSaver.min.js'
|
||||||
], function ($, TextPatcher, Listmap, Crypto, Cryptpad, Cryptget, Hyperjson, Renderer, Toolbar, Visible, Notify) {
|
], function ($, TextPatcher, Listmap, Crypto, Cryptpad, Cryptget, Hyperjson, Renderer, Toolbar) {
|
||||||
|
|
||||||
var Messages = Cryptpad.Messages;
|
var Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
@@ -34,10 +32,9 @@ define([
|
|||||||
if (!DEBUG) {
|
if (!DEBUG) {
|
||||||
debug = function() {};
|
debug = function() {};
|
||||||
}
|
}
|
||||||
var error = console.error;
|
|
||||||
|
|
||||||
Cryptpad.addLoadingScreen();
|
Cryptpad.addLoadingScreen();
|
||||||
var onConnectError = function (info) {
|
var onConnectError = function () {
|
||||||
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,15 +94,6 @@ define([
|
|||||||
return newObj;
|
return newObj;
|
||||||
};
|
};
|
||||||
|
|
||||||
var setColumnDisabled = function (id, state) {
|
|
||||||
if (!state) {
|
|
||||||
$('input[data-rt-id^="' + id + '"]').removeAttr('disabled');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$('input[data-rt-id^="' + id + '"]').attr('disabled', 'disabled');
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var styleUncommittedColumn = function () {
|
var styleUncommittedColumn = function () {
|
||||||
var id = APP.userid;
|
var id = APP.userid;
|
||||||
|
|
||||||
@@ -195,20 +183,6 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var unnotify = function () {
|
|
||||||
if (APP.tabNotification &&
|
|
||||||
typeof(APP.tabNotification.cancel) === 'function') {
|
|
||||||
APP.tabNotification.cancel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var notify = function () {
|
|
||||||
if (Visible.isSupported() && !Visible.currently()) {
|
|
||||||
unnotify();
|
|
||||||
APP.tabNotification = Notify.tab(1000, 10);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Any time the realtime object changes, call this function */
|
/* Any time the realtime object changes, call this function */
|
||||||
var change = function (o, n, path, throttle, cb) {
|
var change = function (o, n, path, throttle, cb) {
|
||||||
if (path && !Cryptpad.isArray(path)) {
|
if (path && !Cryptpad.isArray(path)) {
|
||||||
@@ -237,7 +211,7 @@ define([
|
|||||||
|
|
||||||
https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
|
https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
|
||||||
*/
|
*/
|
||||||
notify();
|
Cryptpad.notify();
|
||||||
|
|
||||||
var getFocus = function () {
|
var getFocus = function () {
|
||||||
var active = document.activeElement;
|
var active = document.activeElement;
|
||||||
@@ -286,7 +260,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Called whenever an event is fired on an input element */
|
/* Called whenever an event is fired on an input element */
|
||||||
var handleInput = function (input, isKeyup) {
|
var handleInput = function (input) {
|
||||||
var type = input.type.toLowerCase();
|
var type = input.type.toLowerCase();
|
||||||
var id = getRealtimeId(input);
|
var id = getRealtimeId(input);
|
||||||
|
|
||||||
@@ -451,82 +425,8 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var userData = APP.userData = {}; // List of pretty names for all users (mapped with their ID)
|
var Title;
|
||||||
var userList; // List of users still connected to the channel (server IDs)
|
var UserList;
|
||||||
var addToUserData = function(data) {
|
|
||||||
var users = userList ? userList.users : undefined;
|
|
||||||
//var userData = APP.proxy.info.userData;
|
|
||||||
for (var attrname in data) { userData[attrname] = data[attrname]; }
|
|
||||||
|
|
||||||
if (users && users.length) {
|
|
||||||
for (var userKey in userData) {
|
|
||||||
if (users.indexOf(userKey) === -1) { delete userData[userKey]; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(userList && typeof userList.onChange === "function") {
|
|
||||||
userList.onChange(userData);
|
|
||||||
}
|
|
||||||
|
|
||||||
APP.proxy.info.userData = userData;
|
|
||||||
};
|
|
||||||
|
|
||||||
var setName = APP.setName = function (newName) {
|
|
||||||
if (typeof(newName) !== 'string') { return; }
|
|
||||||
var myUserNameTemp = newName.trim();
|
|
||||||
if(myUserNameTemp.length > 32) {
|
|
||||||
myUserNameTemp = myUserNameTemp.substr(0, 32);
|
|
||||||
}
|
|
||||||
var myUserName = myUserNameTemp;
|
|
||||||
var myID = APP.myID;
|
|
||||||
var myData = {};
|
|
||||||
myData[myID] = {
|
|
||||||
name: myUserName,
|
|
||||||
uid: Cryptpad.getUid(),
|
|
||||||
};
|
|
||||||
addToUserData(myData);
|
|
||||||
Cryptpad.setAttribute('username', newName, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.error("Couldn't set username");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateTitle = function (newTitle) {
|
|
||||||
if (newTitle === document.title) { return; }
|
|
||||||
// Change the title now, and set it back to the old value if there is an error
|
|
||||||
var oldTitle = document.title;
|
|
||||||
document.title = newTitle;
|
|
||||||
Cryptpad.renamePad(newTitle, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
debug("Couldn't set pad title");
|
|
||||||
error(err);
|
|
||||||
document.title = oldTitle;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
document.title = data;
|
|
||||||
APP.$bar.find('.' + Toolbar.constants.title).find('span.title').text(data);
|
|
||||||
APP.$bar.find('.' + Toolbar.constants.title).find('input').val(data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateDefaultTitle = function (defaultTitle) {
|
|
||||||
defaultName = defaultTitle;
|
|
||||||
APP.$bar.find('.' + Toolbar.constants.title).find('input').attr("placeholder", defaultName);
|
|
||||||
};
|
|
||||||
var renameCb = function (err, title) {
|
|
||||||
if (err) { return; }
|
|
||||||
document.title = title;
|
|
||||||
APP.proxy.info.title = title === defaultName ? "" : title;
|
|
||||||
};
|
|
||||||
|
|
||||||
var suggestName = function (fallback) {
|
|
||||||
if (document.title === defaultName) {
|
|
||||||
return fallback || "";
|
|
||||||
}
|
|
||||||
return document.title || defaultName || "";
|
|
||||||
};
|
|
||||||
|
|
||||||
var copyObject = function (obj) {
|
var copyObject = function (obj) {
|
||||||
return JSON.parse(JSON.stringify(obj));
|
return JSON.parse(JSON.stringify(obj));
|
||||||
@@ -535,252 +435,225 @@ define([
|
|||||||
// special UI elements
|
// special UI elements
|
||||||
var $description = $('#description').attr('placeholder', Messages.poll_descriptionHint || 'description');
|
var $description = $('#description').attr('placeholder', Messages.poll_descriptionHint || 'description');
|
||||||
|
|
||||||
var ready = function (info, userid, readOnly) {
|
var ready = function (info, userid, readOnly) {
|
||||||
debug("READY");
|
debug("READY");
|
||||||
debug('userid: %s', userid);
|
debug('userid: %s', userid);
|
||||||
|
|
||||||
var proxy = APP.proxy;
|
var proxy = APP.proxy;
|
||||||
|
|
||||||
var isNew = false;
|
var isNew = false;
|
||||||
var userDoc = JSON.stringify(proxy);
|
var userDoc = JSON.stringify(proxy);
|
||||||
if (userDoc === "" || userDoc === "{}") { isNew = true; }
|
if (userDoc === "" || userDoc === "{}") { isNew = true; }
|
||||||
|
|
||||||
var uncommitted = APP.uncommitted = {};
|
var uncommitted = APP.uncommitted = {};
|
||||||
prepareProxy(proxy, copyObject(Render.Example));
|
prepareProxy(proxy, copyObject(Render.Example));
|
||||||
prepareProxy(uncommitted, copyObject(Render.Example));
|
prepareProxy(uncommitted, copyObject(Render.Example));
|
||||||
if (!readOnly && proxy.table.colsOrder.indexOf(userid) === -1 &&
|
if (!readOnly && proxy.table.colsOrder.indexOf(userid) === -1 &&
|
||||||
uncommitted.table.colsOrder.indexOf(userid) === -1) {
|
uncommitted.table.colsOrder.indexOf(userid) === -1) {
|
||||||
uncommitted.table.colsOrder.unshift(userid);
|
uncommitted.table.colsOrder.unshift(userid);
|
||||||
}
|
}
|
||||||
|
|
||||||
var displayedObj = mergeUncommitted(proxy, uncommitted, false);
|
var displayedObj = mergeUncommitted(proxy, uncommitted, false);
|
||||||
|
|
||||||
var colsOrder = sortColumns(displayedObj.table.colsOrder, userid);
|
var colsOrder = sortColumns(displayedObj.table.colsOrder, userid);
|
||||||
|
|
||||||
var $table = APP.$table = $(Render.asHTML(displayedObj, null, colsOrder, readOnly));
|
var $table = APP.$table = $(Render.asHTML(displayedObj, null, colsOrder, readOnly));
|
||||||
var $createRow = APP.$createRow = $('#create-option').click(function () {
|
APP.$createRow = $('#create-option').click(function () {
|
||||||
//console.error("BUTTON CLICKED! LOL");
|
//console.error("BUTTON CLICKED! LOL");
|
||||||
Render.createRow(proxy, function (empty, id) {
|
Render.createRow(proxy, function (empty, id) {
|
||||||
change(null, null, null, null, function() {
|
change(null, null, null, null, function() {
|
||||||
$('.edit[data-rt-id="' + id + '"]').click();
|
$('.edit[data-rt-id="' + id + '"]').click();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
var $createCol = APP.$createCol = $('#create-user').click(function () {
|
APP.$createCol = $('#create-user').click(function () {
|
||||||
Render.createColumn(proxy, function (empty, id) {
|
Render.createColumn(proxy, function (empty, id) {
|
||||||
change(null, null, null, null, function() {
|
change(null, null, null, null, function() {
|
||||||
$('.edit[data-rt-id="' + id + '"]').click();
|
$('.edit[data-rt-id="' + id + '"]').click();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Commit button
|
// Commit button
|
||||||
var $commit = APP.$commit = $('#commit').click(function () {
|
APP.$commit = $('#commit').click(function () {
|
||||||
var uncommittedCopy = JSON.parse(JSON.stringify(APP.uncommitted));
|
var uncommittedCopy = JSON.parse(JSON.stringify(APP.uncommitted));
|
||||||
APP.uncommitted = {};
|
APP.uncommitted = {};
|
||||||
prepareProxy(APP.uncommitted, copyObject(Render.Example));
|
prepareProxy(APP.uncommitted, copyObject(Render.Example));
|
||||||
mergeUncommitted(proxy, uncommittedCopy, true);
|
mergeUncommitted(proxy, uncommittedCopy, true);
|
||||||
APP.$commit.hide();
|
APP.$commit.hide();
|
||||||
change();
|
change();
|
||||||
|
});
|
||||||
|
|
||||||
|
// #publish button is removed in readonly
|
||||||
|
APP.$publish = $('#publish')
|
||||||
|
.click(function () {
|
||||||
|
publish(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// #publish button is removed in readonly
|
// #publish button is removed in readonly
|
||||||
var $publish = APP.$publish = $('#publish')
|
APP.$admin = $('#admin')
|
||||||
.click(function () {
|
.click(function () {
|
||||||
publish(true);
|
publish(false);
|
||||||
});
|
|
||||||
|
|
||||||
// #publish button is removed in readonly
|
|
||||||
var $admin = APP.$admin = $('#admin')
|
|
||||||
.click(function () {
|
|
||||||
publish(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Title
|
|
||||||
if (APP.proxy.info.defaultTitle) {
|
|
||||||
updateDefaultTitle(APP.proxy.info.defaultTitle);
|
|
||||||
} else {
|
|
||||||
APP.proxy.info.defaultTitle = defaultName;
|
|
||||||
}
|
|
||||||
if (Cryptpad.initialName && !APP.proxy.info.title) {
|
|
||||||
APP.proxy.info.title = Cryptpad.initialName;
|
|
||||||
updateTitle(Cryptpad.initialName);
|
|
||||||
} else {
|
|
||||||
updateTitle(APP.proxy.info.title || defaultName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Description
|
|
||||||
var resize = function () {
|
|
||||||
var lineCount = $description.val().split('\n').length;
|
|
||||||
$description.css('height', lineCount + 'rem');
|
|
||||||
};
|
|
||||||
$description.on('change keyup', function () {
|
|
||||||
var val = $description.val();
|
|
||||||
proxy.info.description = val;
|
|
||||||
resize();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Title
|
||||||
|
if (APP.proxy.info.defaultTitle) {
|
||||||
|
Title.updateDefaultTitle(APP.proxy.info.defaultTitle);
|
||||||
|
} else {
|
||||||
|
APP.proxy.info.defaultTitle = Title.defaultTitle;
|
||||||
|
}
|
||||||
|
if (Cryptpad.initialName && !APP.proxy.info.title) {
|
||||||
|
APP.proxy.info.title = Cryptpad.initialName;
|
||||||
|
Title.updateTitle(Cryptpad.initialName);
|
||||||
|
} else {
|
||||||
|
Title.updateTitle(APP.proxy.info.title || Title.defaultTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description
|
||||||
|
var resize = function () {
|
||||||
|
var lineCount = $description.val().split('\n').length;
|
||||||
|
$description.css('height', lineCount + 'rem');
|
||||||
|
};
|
||||||
|
$description.on('change keyup', function () {
|
||||||
|
var val = $description.val();
|
||||||
|
proxy.info.description = val;
|
||||||
resize();
|
resize();
|
||||||
if (typeof(proxy.info.description) !== 'undefined') {
|
});
|
||||||
$description.val(proxy.info.description);
|
resize();
|
||||||
}
|
if (typeof(proxy.info.description) !== 'undefined') {
|
||||||
|
$description.val(proxy.info.description);
|
||||||
|
}
|
||||||
|
|
||||||
$('#tableScroll').html('').prepend($table);
|
$('#tableScroll').html('').prepend($table);
|
||||||
updateDisplayedTable();
|
updateDisplayedTable();
|
||||||
|
|
||||||
$table
|
$table
|
||||||
.click(handleClick)
|
.click(handleClick)
|
||||||
.on('keyup', function (e) { handleClick(e, true); });
|
.on('keyup', function (e) { handleClick(e, true); });
|
||||||
|
|
||||||
proxy
|
proxy
|
||||||
.on('change', ['info'], function (o, n, p) {
|
.on('change', ['info'], function (o, n, p) {
|
||||||
if (p[1] === 'title') {
|
if (p[1] === 'title') {
|
||||||
updateTitle(n);
|
Title.updateTitle(n);
|
||||||
notify();
|
Cryptpad.notify();
|
||||||
} else if (p[1] === "userData") {
|
} else if (p[1] === "userData") {
|
||||||
addToUserData(APP.proxy.info.userData);
|
UserList.addToUserData(APP.proxy.info.userData);
|
||||||
} else if (p[1] === 'description') {
|
} else if (p[1] === 'description') {
|
||||||
var op = TextPatcher.diff(o, n);
|
var op = TextPatcher.diff(o, n);
|
||||||
var el = $description[0];
|
var el = $description[0];
|
||||||
|
|
||||||
var selects = ['selectionStart', 'selectionEnd'].map(function (attr) {
|
var selects = ['selectionStart', 'selectionEnd'].map(function (attr) {
|
||||||
var before = el[attr];
|
return TextPatcher.transformCursor(el[attr], op);
|
||||||
var after = TextPatcher.transformCursor(el[attr], op);
|
});
|
||||||
return after;
|
$description.val(n);
|
||||||
});
|
if (op) {
|
||||||
$description.val(n);
|
el.selectionStart = selects[0];
|
||||||
if (op) {
|
el.selectionEnd = selects[1];
|
||||||
el.selectionStart = selects[0];
|
|
||||||
el.selectionEnd = selects[1];
|
|
||||||
}
|
|
||||||
notify();
|
|
||||||
}
|
}
|
||||||
|
Cryptpad.notify();
|
||||||
debug("change: (%s, %s, [%s])", o, n, p.join(', '));
|
|
||||||
})
|
|
||||||
.on('change', ['table'], change)
|
|
||||||
.on('remove', [], change);
|
|
||||||
|
|
||||||
addToUserData(APP.proxy.info.userData);
|
|
||||||
|
|
||||||
if (Visible.isSupported()) {
|
|
||||||
Visible.onChange(function (yes) {
|
|
||||||
if (yes) { unnotify(); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Cryptpad.getLastName(function (err, lastName) {
|
|
||||||
APP.ready = true;
|
|
||||||
|
|
||||||
if (!proxy.published) {
|
|
||||||
publish(false);
|
|
||||||
} else {
|
|
||||||
publish(true);
|
|
||||||
}
|
}
|
||||||
Cryptpad.removeLoadingScreen();
|
|
||||||
|
|
||||||
// Update the toolbar list:
|
debug("change: (%s, %s, [%s])", o, n, p.join(', '));
|
||||||
// Add the current user in the metadata if he has edit rights
|
})
|
||||||
if (readOnly) { return; }
|
.on('change', ['table'], change)
|
||||||
if (typeof(lastName) === 'string') {
|
.on('remove', [], change);
|
||||||
setName(lastName);
|
|
||||||
} else {
|
UserList.addToUserData(APP.proxy.info.userData);
|
||||||
var myData = {};
|
|
||||||
myData[info.myId] = {
|
APP.ready = true;
|
||||||
name: "",
|
if (!proxy.published) {
|
||||||
uid: Cryptpad.getUid(),
|
publish(false);
|
||||||
};
|
} else {
|
||||||
addToUserData(myData);
|
publish(true);
|
||||||
APP.$userNameButton.click();
|
}
|
||||||
}
|
Cryptpad.removeLoadingScreen();
|
||||||
if (isNew) {
|
|
||||||
Cryptpad.selectTemplate('poll', info.realtime, Cryptget);
|
if (readOnly) { return; }
|
||||||
}
|
UserList.getLastName(APP.toolbar.$userNameButton, isNew);
|
||||||
|
};
|
||||||
|
|
||||||
|
var disconnect = function () {
|
||||||
|
//setEditable(false); // TODO
|
||||||
|
APP.toolbar.failed();
|
||||||
|
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
var reconnect = function (info) {
|
||||||
|
//setEditable(true); // TODO
|
||||||
|
APP.toolbar.reconnecting(info.myId);
|
||||||
|
Cryptpad.findOKButton().click();
|
||||||
|
};
|
||||||
|
|
||||||
|
var create = function (info) {
|
||||||
|
APP.myID = info.myID;
|
||||||
|
|
||||||
|
var editHash;
|
||||||
|
if (!readOnly) {
|
||||||
|
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (APP.realtime !== info.realtime) {
|
||||||
|
APP.realtime = info.realtime;
|
||||||
|
APP.patchText = TextPatcher.create({
|
||||||
|
realtime: info.realtime,
|
||||||
|
logging: true,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var onLocal = function () {
|
||||||
|
APP.proxy.info.userData = UserList.userData;
|
||||||
};
|
};
|
||||||
|
UserList = Cryptpad.createUserList(info, onLocal, Cryptget, Cryptpad);
|
||||||
|
|
||||||
var disconnect = function (info) {
|
var onLocalTitle = function () {
|
||||||
//setEditable(false); // TODO
|
APP.proxy.info.title = Title.isDefaultTitle() ? "" : Title.title;
|
||||||
APP.realtime.toolbar.failed();
|
|
||||||
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
|
||||||
};
|
};
|
||||||
|
Title = Cryptpad.createTitle({}, onLocalTitle, Cryptpad);
|
||||||
|
|
||||||
var reconnect = function (info) {
|
var configTb = {
|
||||||
//setEditable(true); // TODO
|
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'],
|
||||||
APP.realtime.toolbar.reconnecting(info.myId);
|
userList: UserList.getToolbarConfig(),
|
||||||
Cryptpad.findOKButton().click();
|
share: {
|
||||||
|
secret: secret,
|
||||||
|
channel: info.channel
|
||||||
|
},
|
||||||
|
title: Title.getTitleConfig(),
|
||||||
|
common: Cryptpad,
|
||||||
|
readOnly: readOnly,
|
||||||
|
ifrw: window,
|
||||||
|
realtime: info.realtime,
|
||||||
|
network: info.network,
|
||||||
|
$container: APP.$bar
|
||||||
};
|
};
|
||||||
|
APP.toolbar = Toolbar.create(configTb);
|
||||||
|
|
||||||
var create = function (info) {
|
Title.setToolbar(APP.toolbar);
|
||||||
var myID = APP.myID = info.myID;
|
|
||||||
|
|
||||||
var editHash;
|
var $rightside = APP.toolbar.$rightside;
|
||||||
var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys);
|
|
||||||
|
|
||||||
if (!readOnly) {
|
/* add a forget button */
|
||||||
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
var forgetCb = function (err) {
|
||||||
}
|
if (err) { return; }
|
||||||
|
disconnect();
|
||||||
|
};
|
||||||
|
var $forgetPad = Cryptpad.createButton('forget', true, {}, forgetCb);
|
||||||
|
$rightside.append($forgetPad);
|
||||||
|
|
||||||
if (APP.realtime !== info.realtime) {
|
// set the hash
|
||||||
APP.realtime = info.realtime;
|
if (!readOnly) { Cryptpad.replaceHash(editHash); }
|
||||||
APP.patchText = TextPatcher.create({
|
|
||||||
realtime: info.realtime,
|
|
||||||
logging: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
userList = APP.userList = info.userList;
|
/* save as template */
|
||||||
|
if (!Cryptpad.isTemplate(window.location.href)) {
|
||||||
var config = {
|
var templateObj = {
|
||||||
displayed: ['useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad'],
|
rt: info.realtime,
|
||||||
userData: userData,
|
Crypt: Cryptget,
|
||||||
readOnly: readOnly,
|
getTitle: function () { return document.title; }
|
||||||
share: {
|
|
||||||
secret: secret,
|
|
||||||
channel: info.channel
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
onRename: renameCb,
|
|
||||||
defaultName: defaultName,
|
|
||||||
suggestName: suggestName
|
|
||||||
},
|
|
||||||
ifrw: window,
|
|
||||||
common: Cryptpad,
|
|
||||||
};
|
};
|
||||||
var toolbar = info.realtime.toolbar = Toolbar.create(APP.$bar, info.myID, info.realtime, info.getLag, userList, config);
|
var $templateButton = Cryptpad.createButton('template', true, templateObj);
|
||||||
|
$rightside.append($templateButton);
|
||||||
var $bar = APP.$bar;
|
}
|
||||||
var $rightside = $bar.find('.' + Toolbar.constants.rightside);
|
};
|
||||||
var $userBlock = $bar.find('.' + Toolbar.constants.username);
|
|
||||||
var $editShare = $bar.find('.' + Toolbar.constants.editShare);
|
|
||||||
var $viewShare = $bar.find('.' + Toolbar.constants.viewShare);
|
|
||||||
var $usernameButton = APP.$userNameButton = $($bar.find('.' + Toolbar.constants.changeUsername));
|
|
||||||
|
|
||||||
/* add a forget button */
|
|
||||||
var forgetCb = function (err, title) {
|
|
||||||
if (err) { return; }
|
|
||||||
disconnect();
|
|
||||||
};
|
|
||||||
var $forgetPad = Cryptpad.createButton('forget', true, {}, forgetCb);
|
|
||||||
$rightside.append($forgetPad);
|
|
||||||
|
|
||||||
// set the hash
|
|
||||||
if (!readOnly) { Cryptpad.replaceHash(editHash); }
|
|
||||||
|
|
||||||
/* save as template */
|
|
||||||
if (!Cryptpad.isTemplate(window.location.href)) {
|
|
||||||
var templateObj = {
|
|
||||||
rt: info.realtime,
|
|
||||||
Crypt: Cryptget,
|
|
||||||
getTitle: function () { return document.title; }
|
|
||||||
};
|
|
||||||
var $templateButton = Cryptpad.createButton('template', true, templateObj);
|
|
||||||
$rightside.append($templateButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cryptpad.onDisplayNameChanged(setName);
|
|
||||||
};
|
|
||||||
|
|
||||||
// don't initialize until the store is ready.
|
// don't initialize until the store is ready.
|
||||||
Cryptpad.ready(function () {
|
Cryptpad.ready(function () {
|
||||||
@@ -814,6 +687,7 @@ define([
|
|||||||
if (!userid) { userid = Render.coluid(); }
|
if (!userid) { userid = Render.coluid(); }
|
||||||
APP.userid = userid;
|
APP.userid = userid;
|
||||||
Cryptpad.setPadAttribute('userid', userid, function (e) {
|
Cryptpad.setPadAttribute('userid', userid, function (e) {
|
||||||
|
if (e) { console.error(e); }
|
||||||
ready(info, userid, readOnly);
|
ready(info, userid, readOnly);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ var Renderer = function (Cryptpad) {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
var getCoordinates = Render.getCoordinates = function (id) {
|
Render.getCoordinates = function (id) {
|
||||||
return id.split('_');
|
return id.split('_');
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ var Renderer = function (Cryptpad) {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
var createColumn = Render.createColumn = function (obj, cb, id, value) {
|
Render.createColumn = function (obj, cb, id, value) {
|
||||||
var order = Cryptpad.find(obj, ['table', 'colsOrder']);
|
var order = Cryptpad.find(obj, ['table', 'colsOrder']);
|
||||||
if (!order) { throw new Error("Uninitialized realtime object!"); }
|
if (!order) { throw new Error("Uninitialized realtime object!"); }
|
||||||
id = id || coluid();
|
id = id || coluid();
|
||||||
@@ -101,7 +101,7 @@ var Renderer = function (Cryptpad) {
|
|||||||
if (typeof(cb) === 'function') { cb(void 0, id); }
|
if (typeof(cb) === 'function') { cb(void 0, id); }
|
||||||
};
|
};
|
||||||
|
|
||||||
var removeColumn = Render.removeColumn = function (obj, id, cb) {
|
Render.removeColumn = function (obj, id, cb) {
|
||||||
var order = Cryptpad.find(obj, ['table', 'colsOrder']);
|
var order = Cryptpad.find(obj, ['table', 'colsOrder']);
|
||||||
var parent = Cryptpad.find(obj, ['table', 'cols']);
|
var parent = Cryptpad.find(obj, ['table', 'cols']);
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ var Renderer = function (Cryptpad) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var createRow = Render.createRow = function (obj, cb, id, value) {
|
Render.createRow = function (obj, cb, id, value) {
|
||||||
var order = Cryptpad.find(obj, ['table', 'rowsOrder']);
|
var order = Cryptpad.find(obj, ['table', 'rowsOrder']);
|
||||||
if (!order) { throw new Error("Uninitialized realtime object!"); }
|
if (!order) { throw new Error("Uninitialized realtime object!"); }
|
||||||
id = id || rowuid();
|
id = id || rowuid();
|
||||||
@@ -136,7 +136,7 @@ var Renderer = function (Cryptpad) {
|
|||||||
if (typeof(cb) === 'function') { cb(void 0, id); }
|
if (typeof(cb) === 'function') { cb(void 0, id); }
|
||||||
};
|
};
|
||||||
|
|
||||||
var removeRow = Render.removeRow = function (obj, id, cb) {
|
Render.removeRow = function (obj, id, cb) {
|
||||||
var order = Cryptpad.find(obj, ['table', 'rowsOrder']);
|
var order = Cryptpad.find(obj, ['table', 'rowsOrder']);
|
||||||
var parent = Cryptpad.find(obj, ['table', 'rows']);
|
var parent = Cryptpad.find(obj, ['table', 'rows']);
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ var Renderer = function (Cryptpad) {
|
|||||||
if (typeof(cb) === 'function') { cb(); }
|
if (typeof(cb) === 'function') { cb(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
var setValue = Render.setValue = function (obj, id, value) {
|
Render.setValue = function (obj, id, value) {
|
||||||
var type = typeofId(id);
|
var type = typeofId(id);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -167,7 +167,7 @@ var Renderer = function (Cryptpad) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var getValue = Render.getValue = function (obj, id) {
|
Render.getValue = function (obj, id) {
|
||||||
switch (typeofId(id)) {
|
switch (typeofId(id)) {
|
||||||
case 'row': return getRowValue(obj, id);
|
case 'row': return getRowValue(obj, id);
|
||||||
case 'col': return getColumnValue(obj, id);
|
case 'col': return getColumnValue(obj, id);
|
||||||
@@ -219,7 +219,7 @@ var Renderer = function (Cryptpad) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
if (i === rows.length) {
|
if (i === rows.length) {
|
||||||
return [null].concat(cols.map(function (col) {
|
return [null].concat(cols.map(function () {
|
||||||
return {
|
return {
|
||||||
'class': 'lastRow',
|
'class': 'lastRow',
|
||||||
};
|
};
|
||||||
@@ -359,7 +359,7 @@ var Renderer = function (Cryptpad) {
|
|||||||
return ['TABLE', {id:'table'}, [head, foot, body]];
|
return ['TABLE', {id:'table'}, [head, foot, body]];
|
||||||
};
|
};
|
||||||
|
|
||||||
var asHTML = Render.asHTML = function (obj, rows, cols, readOnly) {
|
Render.asHTML = function (obj, rows, cols, readOnly) {
|
||||||
return Hyperjson.toDOM(toHyperjson(cellMatrix(obj, rows, cols, readOnly), readOnly));
|
return Hyperjson.toDOM(toHyperjson(cellMatrix(obj, rows, cols, readOnly), readOnly));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -384,9 +384,7 @@ var Renderer = function (Cryptpad) {
|
|||||||
var op = TextPatcher.diff(o, n);
|
var op = TextPatcher.diff(o, n);
|
||||||
|
|
||||||
info.selection = ['selectionStart', 'selectionEnd'].map(function (attr) {
|
info.selection = ['selectionStart', 'selectionEnd'].map(function (attr) {
|
||||||
var before = element[attr];
|
return TextPatcher.transformCursor(element[attr], op);
|
||||||
var after = TextPatcher.transformCursor(element[attr], op);
|
|
||||||
return after;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -432,7 +430,7 @@ var Renderer = function (Cryptpad) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var updateTable = Render.updateTable = function (table, obj, conf) {
|
Render.updateTable = function (table, obj, conf) {
|
||||||
var DD = new DiffDOM(diffOptions);
|
var DD = new DiffDOM(diffOptions);
|
||||||
|
|
||||||
var rows = conf ? conf.rows : null;
|
var rows = conf ? conf.rows : null;
|
||||||
|
|||||||
@@ -2,14 +2,8 @@ define([
|
|||||||
'jquery',
|
'jquery',
|
||||||
'/common/login.js',
|
'/common/login.js',
|
||||||
'/common/cryptpad-common.js',
|
'/common/cryptpad-common.js',
|
||||||
'/common/cryptget.js',
|
'/common/credential.js' // preloaded for login.js
|
||||||
'/common/credential.js'
|
], function ($, Login, Cryptpad) {
|
||||||
], function ($, Login, Cryptpad, Crypt) {
|
|
||||||
|
|
||||||
var APP = window.APP = {
|
|
||||||
Login: Login,
|
|
||||||
};
|
|
||||||
|
|
||||||
var Messages = Cryptpad.Messages;
|
var Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
@@ -107,57 +101,63 @@ define([
|
|||||||
function (yes) {
|
function (yes) {
|
||||||
if (!yes) { return; }
|
if (!yes) { return; }
|
||||||
|
|
||||||
Cryptpad.addLoadingScreen(Messages.login_hashing);
|
// setTimeout 100ms to remove the keyboard on mobile devices before the loading screen pops up
|
||||||
Login.loginOrRegister(uname, passwd, true, function (err, result) {
|
window.setTimeout(function () {
|
||||||
var proxy = result.proxy;
|
Cryptpad.addLoadingScreen(Messages.login_hashing);
|
||||||
|
// We need a setTimeout(cb, 0) otherwise the loading screen is only displayed after hashing the password
|
||||||
|
window.setTimeout(function () {
|
||||||
|
Login.loginOrRegister(uname, passwd, true, function (err, result) {
|
||||||
|
var proxy = result.proxy;
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case 'NO_SUCH_USER':
|
case 'NO_SUCH_USER':
|
||||||
Cryptpad.removeLoadingScreen(function () {
|
Cryptpad.removeLoadingScreen(function () {
|
||||||
Cryptpad.alert(Messages.login_noSuchUser);
|
Cryptpad.alert(Messages.login_noSuchUser);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'INVAL_USER':
|
case 'INVAL_USER':
|
||||||
Cryptpad.removeLoadingScreen(function () {
|
Cryptpad.removeLoadingScreen(function () {
|
||||||
Cryptpad.alert(Messages.login_invalUser);
|
Cryptpad.alert(Messages.login_invalUser);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'INVAL_PASS':
|
case 'INVAL_PASS':
|
||||||
Cryptpad.removeLoadingScreen(function () {
|
Cryptpad.removeLoadingScreen(function () {
|
||||||
Cryptpad.alert(Messages.login_invalPass);
|
Cryptpad.alert(Messages.login_invalPass);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'ALREADY_REGISTERED':
|
case 'ALREADY_REGISTERED':
|
||||||
Cryptpad.removeLoadingScreen(function () {
|
Cryptpad.removeLoadingScreen(function () {
|
||||||
Cryptpad.confirm(Messages.register_alreadyRegistered, function (yes) {
|
Cryptpad.confirm(Messages.register_alreadyRegistered, function (yes) {
|
||||||
if (!yes) { return; }
|
if (!yes) { return; }
|
||||||
proxy.login_name = uname;
|
proxy.login_name = uname;
|
||||||
|
|
||||||
if (!proxy[Cryptpad.displayNameKey]) {
|
if (!proxy[Cryptpad.displayNameKey]) {
|
||||||
proxy[Cryptpad.displayNameKey] = uname;
|
proxy[Cryptpad.displayNameKey] = uname;
|
||||||
}
|
}
|
||||||
Cryptpad.eraseTempSessionValues();
|
Cryptpad.eraseTempSessionValues();
|
||||||
logMeIn(result);
|
logMeIn(result);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default: // UNHANDLED ERROR
|
default: // UNHANDLED ERROR
|
||||||
Cryptpad.errorLoadingScreen(Messages.login_unhandledError);
|
Cryptpad.errorLoadingScreen(Messages.login_unhandledError);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Cryptpad.eraseTempSessionValues();
|
Cryptpad.eraseTempSessionValues();
|
||||||
if (shouldImport) {
|
if (shouldImport) {
|
||||||
sessionStorage.migrateAnonDrive = 1;
|
sessionStorage.migrateAnonDrive = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy.login_name = uname;
|
proxy.login_name = uname;
|
||||||
proxy[Cryptpad.displayNameKey] = uname;
|
proxy[Cryptpad.displayNameKey] = uname;
|
||||||
sessionStorage.createReadme = 1;
|
sessionStorage.createReadme = 1;
|
||||||
|
|
||||||
logMeIn(result);
|
logMeIn(result);
|
||||||
});
|
});
|
||||||
|
}, 0);
|
||||||
|
}, 100);
|
||||||
}, {
|
}, {
|
||||||
ok: Messages.register_writtenPassword,
|
ok: Messages.register_writtenPassword,
|
||||||
cancel: Messages.register_cancel,
|
cancel: Messages.register_cancel,
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li class="title" data-localization="footer_contact"><li>
|
<li class="title" data-localization="footer_contact"><li>
|
||||||
<li><a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" target="_blank" rel="noopener noreferrer">IRC</a></li>
|
<li><a href="https://riot.im/app/#/room/!cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
|
||||||
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
|
||||||
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
|
||||||
<li><a href="/contact.html">Email</a></li>
|
<li><a href="/contact.html">Email</a></li>
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ define([
|
|||||||
|
|
||||||
var Messages = Cryptpad.Messages;
|
var Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
var redirectToMain = function () {
|
|
||||||
window.location.href = '/';
|
|
||||||
};
|
|
||||||
|
|
||||||
// Manage changes in the realtime object made from another page
|
// Manage changes in the realtime object made from another page
|
||||||
var onRefresh = function (h) {
|
var onRefresh = function (h) {
|
||||||
if (typeof(h) !== "function") { return; }
|
if (typeof(h) !== "function") { return; }
|
||||||
@@ -71,7 +67,7 @@ define([
|
|||||||
var createDisplayNameInput = function (store) {
|
var createDisplayNameInput = function (store) {
|
||||||
var obj = store.proxy;
|
var obj = store.proxy;
|
||||||
var $div = $('<div>', {'class': 'displayName'});
|
var $div = $('<div>', {'class': 'displayName'});
|
||||||
var $label = $('<label>', {'for' : 'displayName'}).text(Messages.user_displayName).appendTo($div);
|
$('<label>', {'for' : 'displayName'}).text(Messages.user_displayName).appendTo($div);
|
||||||
$('<br>').appendTo($div);
|
$('<br>').appendTo($div);
|
||||||
var $input = $('<input>', {
|
var $input = $('<input>', {
|
||||||
'type': 'text',
|
'type': 'text',
|
||||||
@@ -114,7 +110,7 @@ define([
|
|||||||
};
|
};
|
||||||
var createResetTips = function () {
|
var createResetTips = function () {
|
||||||
var $div = $('<div>', {'class': 'resetTips'});
|
var $div = $('<div>', {'class': 'resetTips'});
|
||||||
var $label = $('<label>', {'for' : 'resetTips'}).text(Messages.settings_resetTips).appendTo($div);
|
$('<label>', {'for' : 'resetTips'}).text(Messages.settings_resetTips).appendTo($div);
|
||||||
$('<br>').appendTo($div);
|
$('<br>').appendTo($div);
|
||||||
var $button = $('<button>', {'id': 'resetTips', 'class': 'btn btn-primary'})
|
var $button = $('<button>', {'id': 'resetTips', 'class': 'btn btn-primary'})
|
||||||
.text(Messages.settings_resetTipsButton).appendTo($div);
|
.text(Messages.settings_resetTipsButton).appendTo($div);
|
||||||
@@ -145,7 +141,7 @@ define([
|
|||||||
saveAs(blob, filename);
|
saveAs(blob, filename);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var importFile = function (content, file) {
|
var importFile = function (content) {
|
||||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).appendTo($div);
|
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).appendTo($div);
|
||||||
Crypt.put(Cryptpad.getUserHash() || localStorage[Cryptpad.fileHashKey], content, function (e) {
|
Crypt.put(Cryptpad.getUserHash() || localStorage[Cryptpad.fileHashKey], content, function (e) {
|
||||||
if (e) { console.error(e); }
|
if (e) { console.error(e); }
|
||||||
@@ -153,7 +149,7 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var $label = $('<label>', {'for' : 'exportDrive'}).text(Messages.settings_backupTitle).appendTo($div);
|
$('<label>', {'for' : 'exportDrive'}).text(Messages.settings_backupTitle).appendTo($div);
|
||||||
$('<br>').appendTo($div);
|
$('<br>').appendTo($div);
|
||||||
/* add an export button */
|
/* add an export button */
|
||||||
var $export = Cryptpad.createButton('export', true, {}, exportFile);
|
var $export = Cryptpad.createButton('export', true, {}, exportFile);
|
||||||
@@ -170,7 +166,7 @@ define([
|
|||||||
|
|
||||||
var createResetDrive = function (obj) {
|
var createResetDrive = function (obj) {
|
||||||
var $div = $('<div>', {'class': 'resetDrive'});
|
var $div = $('<div>', {'class': 'resetDrive'});
|
||||||
var $label = $('<label>', {'for' : 'resetDrive'}).text(Messages.settings_resetTitle).appendTo($div);
|
$('<label>', {'for' : 'resetDrive'}).text(Messages.settings_resetTitle).appendTo($div);
|
||||||
$('<br>').appendTo($div);
|
$('<br>').appendTo($div);
|
||||||
var $button = $('<button>', {'id': 'resetDrive', 'class': 'btn btn-danger'})
|
var $button = $('<button>', {'id': 'resetDrive', 'class': 'btn btn-danger'})
|
||||||
.text(Messages.settings_reset).appendTo($div);
|
.text(Messages.settings_reset).appendTo($div);
|
||||||
@@ -256,10 +252,47 @@ define([
|
|||||||
return $div;
|
return $div;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var createLogoutEverywhere = function (obj) {
|
||||||
|
var proxy = obj.proxy;
|
||||||
|
var $div = $('<div>', { 'class': 'logoutEverywhere', });
|
||||||
|
$('<label>', { 'for': 'logoutEverywhere'})
|
||||||
|
.text(Messages.settings_logoutEverywhereTitle).appendTo($div);
|
||||||
|
$('<br>').appendTo($div);
|
||||||
|
var $button = $('<button>', { id: 'logoutEverywhere', 'class': 'btn btn-primary' })
|
||||||
|
.text(Messages.settings_logoutEverywhere)
|
||||||
|
.appendTo($div);
|
||||||
|
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
|
||||||
|
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
|
||||||
|
|
||||||
|
$button.click(function () {
|
||||||
|
var realtime = obj.info.realtime;
|
||||||
|
console.log(realtime);
|
||||||
|
|
||||||
|
Cryptpad.confirm(Messages.settings_logoutEverywhereConfirm, function (yes) {
|
||||||
|
if (!yes) { return; }
|
||||||
|
$spinner.show();
|
||||||
|
$ok.hide();
|
||||||
|
|
||||||
|
var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
|
||||||
|
localStorage.setItem('loginToken', token);
|
||||||
|
proxy.loginToken = token;
|
||||||
|
|
||||||
|
Cryptpad.whenRealtimeSyncs(realtime, function () {
|
||||||
|
$spinner.hide();
|
||||||
|
$ok.show();
|
||||||
|
window.setTimeout(function () {
|
||||||
|
$ok.fadeOut(1500);
|
||||||
|
}, 2500);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return $div;
|
||||||
|
};
|
||||||
|
|
||||||
var createImportLocalPads = function (obj) {
|
var createImportLocalPads = function (obj) {
|
||||||
if (!Cryptpad.isLoggedIn()) { return; }
|
if (!Cryptpad.isLoggedIn()) { return; }
|
||||||
var $div = $('<div>', {'class': 'importLocalPads'});
|
var $div = $('<div>', {'class': 'importLocalPads'});
|
||||||
var $label = $('<label>', {'for' : 'importLocalPads'}).text(Messages.settings_importTitle).appendTo($div);
|
$('<label>', {'for' : 'importLocalPads'}).text(Messages.settings_importTitle).appendTo($div);
|
||||||
$('<br>').appendTo($div);
|
$('<br>').appendTo($div);
|
||||||
var $button = $('<button>', {'id': 'importLocalPads', 'class': 'btn btn-primary'})
|
var $button = $('<button>', {'id': 'importLocalPads', 'class': 'btn btn-primary'})
|
||||||
.text(Messages.settings_import).appendTo($div);
|
.text(Messages.settings_import).appendTo($div);
|
||||||
@@ -284,7 +317,7 @@ define([
|
|||||||
|
|
||||||
var createLanguageSelector = function () {
|
var createLanguageSelector = function () {
|
||||||
var $div = $('<div>', {'class': 'importLocalPads'});
|
var $div = $('<div>', {'class': 'importLocalPads'});
|
||||||
var $label = $('<label>').text(Messages.language).appendTo($div);
|
$('<label>').text(Messages.language).appendTo($div);
|
||||||
$('<br>').appendTo($div);
|
$('<br>').appendTo($div);
|
||||||
var $b = Cryptpad.createLanguageSelector().appendTo($div);
|
var $b = Cryptpad.createLanguageSelector().appendTo($div);
|
||||||
$b.find('button').addClass('btn btn-secondary');
|
$b.find('button').addClass('btn btn-secondary');
|
||||||
@@ -296,6 +329,10 @@ define([
|
|||||||
APP.$container.append(createInfoBlock(obj));
|
APP.$container.append(createInfoBlock(obj));
|
||||||
APP.$container.append(createDisplayNameInput(obj));
|
APP.$container.append(createDisplayNameInput(obj));
|
||||||
APP.$container.append(createLanguageSelector());
|
APP.$container.append(createLanguageSelector());
|
||||||
|
|
||||||
|
if (Cryptpad.isLoggedIn()) {
|
||||||
|
APP.$container.append(createLogoutEverywhere(obj));
|
||||||
|
}
|
||||||
APP.$container.append(createResetTips());
|
APP.$container.append(createResetTips());
|
||||||
APP.$container.append(createBackupDrive(obj));
|
APP.$container.append(createBackupDrive(obj));
|
||||||
APP.$container.append(createImportLocalPads(obj));
|
APP.$container.append(createImportLocalPads(obj));
|
||||||
@@ -343,7 +380,6 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('storage', function (e) {
|
window.addEventListener('storage', function (e) {
|
||||||
var key = e.key;
|
|
||||||
if (e.key !== Cryptpad.userHashKey) { return; }
|
if (e.key !== Cryptpad.userHashKey) { return; }
|
||||||
var o = e.oldValue;
|
var o = e.oldValue;
|
||||||
var n = e.newValue;
|
var n = e.newValue;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>CryptPad</title>
|
<title>CryptPad</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
|
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
|
||||||
<link rel="icon" type="image/png"
|
<link rel="icon" type="image/png"
|
||||||
href="/customize/main-favicon.png"
|
href="/customize/main-favicon.png"
|
||||||
|
|||||||
@@ -3,20 +3,13 @@ define([
|
|||||||
'/bower_components/chainpad-crypto/crypto.js',
|
'/bower_components/chainpad-crypto/crypto.js',
|
||||||
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
||||||
'/bower_components/textpatcher/TextPatcher.js',
|
'/bower_components/textpatcher/TextPatcher.js',
|
||||||
'/common/toolbar.js',
|
'/common/toolbar2.js',
|
||||||
'json.sortify',
|
'json.sortify',
|
||||||
'/bower_components/chainpad-json-validator/json-ot.js',
|
'/bower_components/chainpad-json-validator/json-ot.js',
|
||||||
'/common/cryptpad-common.js',
|
'/common/cryptpad-common.js',
|
||||||
'/common/cryptget.js',
|
'/common/cryptget.js',
|
||||||
'/common/modes.js',
|
|
||||||
'/common/themes.js',
|
|
||||||
'/common/visible.js',
|
|
||||||
'/common/notify.js',
|
|
||||||
'/slide/slide.js',
|
'/slide/slide.js',
|
||||||
'/bower_components/file-saver/FileSaver.min.js'
|
], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Cryptget, Slide) {
|
||||||
], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Cryptget, Modes, Themes, Visible, Notify, Slide) {
|
|
||||||
var saveAs = window.saveAs;
|
|
||||||
|
|
||||||
var Messages = Cryptpad.Messages;
|
var Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
var module = window.APP = {
|
var module = window.APP = {
|
||||||
@@ -30,23 +23,15 @@ define([
|
|||||||
var SLIDE_COLOR_ID = "cryptpad-color";
|
var SLIDE_COLOR_ID = "cryptpad-color";
|
||||||
|
|
||||||
|
|
||||||
var stringify = function (obj) {
|
|
||||||
return JSONSortify(obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
var setTabTitle = function () {
|
|
||||||
var slideNumber = '';
|
|
||||||
if (Slide.index && Slide.content.length) {
|
|
||||||
slideNumber = ' (' + Slide.index + '/' + Slide.content.length + ')';
|
|
||||||
}
|
|
||||||
document.title = APP.title + slideNumber;
|
|
||||||
};
|
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
Cryptpad.addLoadingScreen();
|
Cryptpad.addLoadingScreen();
|
||||||
|
|
||||||
|
var stringify = function (obj) {
|
||||||
|
return JSONSortify(obj);
|
||||||
|
};
|
||||||
var ifrw = module.ifrw = $('#pad-iframe')[0].contentWindow;
|
var ifrw = module.ifrw = $('#pad-iframe')[0].contentWindow;
|
||||||
var toolbar;
|
var toolbar;
|
||||||
|
var editor;
|
||||||
|
|
||||||
var secret = Cryptpad.getSecrets();
|
var secret = Cryptpad.getSecrets();
|
||||||
var readOnly = secret.keys && !secret.keys.editKeyStr;
|
var readOnly = secret.keys && !secret.keys.editKeyStr;
|
||||||
@@ -57,82 +42,37 @@ define([
|
|||||||
|
|
||||||
var presentMode = Slide.isPresentURL();
|
var presentMode = Slide.isPresentURL();
|
||||||
|
|
||||||
var onConnectError = function (info) {
|
var onConnectError = function () {
|
||||||
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
||||||
};
|
};
|
||||||
|
|
||||||
var andThen = function (CMeditor) {
|
var andThen = function (CMeditor) {
|
||||||
var CodeMirror = module.CodeMirror = CMeditor;
|
var CodeMirror = Cryptpad.createCodemirror(CMeditor, ifrw, Cryptpad);
|
||||||
CodeMirror.modeURL = "/bower_components/codemirror/mode/%N/%N.js";
|
editor = CodeMirror.editor;
|
||||||
var $pad = $('#pad-iframe');
|
|
||||||
var $textarea = $pad.contents().find('#editor1');
|
|
||||||
|
|
||||||
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
|
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
|
||||||
var parsedHash = Cryptpad.parsePadUrl(window.location.href);
|
var $pad = $('#pad-iframe');
|
||||||
var defaultName = Cryptpad.getDefaultName(parsedHash);
|
|
||||||
var initialState = Messages.slideInitialState;
|
|
||||||
|
|
||||||
var isHistoryMode = false;
|
var isHistoryMode = false;
|
||||||
|
|
||||||
var editor = module.editor = CMeditor.fromTextArea($textarea[0], {
|
var setEditable = module.setEditable = function (bool) {
|
||||||
lineNumbers: true,
|
if (readOnly && bool) { return; }
|
||||||
lineWrapping: true,
|
editor.setOption('readOnly', !bool);
|
||||||
autoCloseBrackets: true,
|
|
||||||
matchBrackets : true,
|
|
||||||
showTrailingSpace : true,
|
|
||||||
styleActiveLine : true,
|
|
||||||
search: true,
|
|
||||||
highlightSelectionMatches: {showToken: /\w+/},
|
|
||||||
extraKeys: {"Shift-Ctrl-R": undefined},
|
|
||||||
foldGutter: true,
|
|
||||||
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
|
|
||||||
mode: "javascript",
|
|
||||||
readOnly: true
|
|
||||||
});
|
|
||||||
editor.setValue(initialState);
|
|
||||||
|
|
||||||
var setMode = module.setMode = function (mode, $select) {
|
|
||||||
module.highlightMode = mode;
|
|
||||||
if (mode === 'text') {
|
|
||||||
editor.setOption('mode', 'text');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CodeMirror.autoLoadMode(editor, mode);
|
|
||||||
editor.setOption('mode', mode);
|
|
||||||
if ($select && $select.val) { $select.val(mode); }
|
|
||||||
};
|
};
|
||||||
setMode('markdown');
|
|
||||||
|
|
||||||
var setTheme = module.setTheme = (function () {
|
var Title;
|
||||||
var path = '/common/theme/';
|
var UserList;
|
||||||
|
var Metadata;
|
||||||
|
|
||||||
var $head = $(ifrw.document.head);
|
var setTabTitle = function (title) {
|
||||||
|
var slideNumber = '';
|
||||||
|
if (Slide.index && Slide.content.length) {
|
||||||
|
slideNumber = ' (' + Slide.index + '/' + Slide.content.length + ')';
|
||||||
|
}
|
||||||
|
document.title = title + slideNumber;
|
||||||
|
};
|
||||||
|
|
||||||
var themeLoaded = module.themeLoaded = function (theme) {
|
var initialState = Messages.slideInitialState;
|
||||||
return $head.find('link[href*="'+theme+'"]').length;
|
|
||||||
};
|
|
||||||
|
|
||||||
var loadTheme = module.loadTheme = function (theme) {
|
|
||||||
$head.append($('<link />', {
|
|
||||||
rel: 'stylesheet',
|
|
||||||
href: path + theme + '.css',
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return function (theme, $select) {
|
|
||||||
if (!theme) {
|
|
||||||
editor.setOption('theme', 'default');
|
|
||||||
} else {
|
|
||||||
if (!themeLoaded(theme)) {
|
|
||||||
loadTheme(theme);
|
|
||||||
}
|
|
||||||
editor.setOption('theme', theme);
|
|
||||||
}
|
|
||||||
if ($select) {
|
|
||||||
$select.setValue(theme || 'Theme');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}());
|
|
||||||
|
|
||||||
var $modal = $pad.contents().find('#modal');
|
var $modal = $pad.contents().find('#modal');
|
||||||
var $content = $pad.contents().find('#content');
|
var $content = $pad.contents().find('#content');
|
||||||
@@ -141,65 +81,21 @@ define([
|
|||||||
|
|
||||||
Slide.setModal(APP, $modal, $content, $pad, ifrw, slideOptions, initialState);
|
Slide.setModal(APP, $modal, $content, $pad, ifrw, slideOptions, initialState);
|
||||||
|
|
||||||
var setStyleState = function (state) {
|
|
||||||
$pad.contents().find('#print, #content').find('style').each(function (i, el) {
|
|
||||||
el.disabled = !state;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var enterPresentationMode = function (shouldLog) {
|
var enterPresentationMode = function (shouldLog) {
|
||||||
Slide.show(true, editor.getValue());
|
Slide.show(true, editor.getValue());
|
||||||
if (shouldLog) {
|
if (shouldLog) {
|
||||||
Cryptpad.log(Messages.presentSuccess);
|
Cryptpad.log(Messages.presentSuccess);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var leavePresentationMode = function () {
|
|
||||||
setStyleState(false);
|
|
||||||
Slide.show(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (presentMode) {
|
if (presentMode) {
|
||||||
enterPresentationMode(true);
|
enterPresentationMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var setEditable = module.setEditable = function (bool) {
|
|
||||||
if (readOnly && bool) { return; }
|
|
||||||
editor.setOption('readOnly', !bool);
|
|
||||||
};
|
|
||||||
|
|
||||||
var userData = module.userData = {}; // List of pretty name of all users (mapped with their server ID)
|
|
||||||
var userList; // List of users still connected to the channel (server IDs)
|
|
||||||
var addToUserData = function(data) {
|
|
||||||
var users = module.users;
|
|
||||||
for (var attrname in data) { userData[attrname] = data[attrname]; }
|
|
||||||
|
|
||||||
if (users && users.length) {
|
|
||||||
for (var userKey in userData) {
|
|
||||||
if (users.indexOf(userKey) === -1) {
|
|
||||||
delete userData[userKey];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(userList && typeof userList.onChange === "function") {
|
|
||||||
userList.onChange(userData);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var textColor;
|
var textColor;
|
||||||
var backColor;
|
var backColor;
|
||||||
|
|
||||||
var myData = {};
|
|
||||||
var myUserName = ''; // My "pretty name"
|
|
||||||
var myID; // My server ID
|
|
||||||
|
|
||||||
var setMyID = function(info) {
|
|
||||||
myID = info.myID || null;
|
|
||||||
myUserName = myID;
|
|
||||||
};
|
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
//initialState: Messages.codeInitialState,
|
|
||||||
initialState: '{}',
|
initialState: '{}',
|
||||||
websocketURL: Cryptpad.getWebsocketURL(),
|
websocketURL: Cryptpad.getWebsocketURL(),
|
||||||
channel: secret.channel,
|
channel: secret.channel,
|
||||||
@@ -207,7 +103,6 @@ define([
|
|||||||
validateKey: secret.keys.validateKey || undefined,
|
validateKey: secret.keys.validateKey || undefined,
|
||||||
readOnly: readOnly,
|
readOnly: readOnly,
|
||||||
crypto: Crypto.createEncryptor(secret.keys),
|
crypto: Crypto.createEncryptor(secret.keys),
|
||||||
setMyID: setMyID,
|
|
||||||
transformFunction: JsonOT.validate,
|
transformFunction: JsonOT.validate,
|
||||||
network: Cryptpad.getNetwork()
|
network: Cryptpad.getNetwork()
|
||||||
};
|
};
|
||||||
@@ -222,24 +117,19 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var isDefaultTitle = function () {
|
|
||||||
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
|
||||||
return Cryptpad.isDefaultName(parsed, APP.title);
|
|
||||||
};
|
|
||||||
|
|
||||||
var initializing = true;
|
var initializing = true;
|
||||||
|
|
||||||
var stringifyInner = function (textValue) {
|
var stringifyInner = function (textValue) {
|
||||||
var obj = {
|
var obj = {
|
||||||
content: textValue,
|
content: textValue,
|
||||||
metadata: {
|
metadata: {
|
||||||
users: userData,
|
users: UserList.userData,
|
||||||
defaultTitle: defaultName,
|
defaultTitle: Title.defaultTitle,
|
||||||
slideOptions: slideOptions
|
slideOptions: slideOptions
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (!initializing) {
|
if (!initializing) {
|
||||||
obj.metadata.title = APP.title;
|
obj.metadata.title = Title.title;
|
||||||
}
|
}
|
||||||
if (textColor) {
|
if (textColor) {
|
||||||
obj.metadata.color = textColor;
|
obj.metadata.color = textColor;
|
||||||
@@ -258,7 +148,7 @@ define([
|
|||||||
|
|
||||||
editor.save();
|
editor.save();
|
||||||
|
|
||||||
var textValue = canonicalize($textarea.val());
|
var textValue = canonicalize(CodeMirror.$textarea.val());
|
||||||
var shjson = stringifyInner(textValue);
|
var shjson = stringifyInner(textValue);
|
||||||
|
|
||||||
module.patchText(shjson);
|
module.patchText(shjson);
|
||||||
@@ -269,120 +159,16 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var setName = module.setName = function (newName) {
|
var metadataCfg = {
|
||||||
if (typeof(newName) !== 'string') { return; }
|
slideOptions: function (newOpt) {
|
||||||
var myUserNameTemp = newName.trim();
|
if (stringify(newOpt) !== stringify(slideOptions)) {
|
||||||
if(newName.trim().length > 32) {
|
$.extend(slideOptions, newOpt);
|
||||||
myUserNameTemp = myUserNameTemp.substr(0, 32);
|
// TODO: manage realtime + cursor in the "options" modal ??
|
||||||
}
|
Slide.updateOptions();
|
||||||
myUserName = myUserNameTemp;
|
|
||||||
myData[myID] = {
|
|
||||||
name: myUserName,
|
|
||||||
uid: Cryptpad.getUid(),
|
|
||||||
};
|
|
||||||
addToUserData(myData);
|
|
||||||
Cryptpad.setAttribute('username', myUserName, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.log("Couldn't set username");
|
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
onLocal();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var getHeadingText = function () {
|
|
||||||
var lines = editor.getValue().split(/\n/);
|
|
||||||
|
|
||||||
var text = '';
|
|
||||||
lines.some(function (line) {
|
|
||||||
// lines beginning with a hash are potentially valuable
|
|
||||||
// works for markdown, python, bash, etc.
|
|
||||||
var hash = /^#(.*?)$/;
|
|
||||||
if (hash.test(line)) {
|
|
||||||
line.replace(hash, function (a, one) {
|
|
||||||
text = one;
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return text.trim();
|
|
||||||
};
|
|
||||||
|
|
||||||
var suggestName = function () {
|
|
||||||
if (APP.title === defaultName) {
|
|
||||||
return getHeadingText() || "";
|
|
||||||
} else {
|
|
||||||
return APP.title || getHeadingText() || defaultName;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
var updateColors = metadataCfg.slideColors = function (text, back) {
|
||||||
var exportText = module.exportText = function () {
|
|
||||||
var text = editor.getValue();
|
|
||||||
|
|
||||||
var ext = Modes.extensionOf(module.highlightMode);
|
|
||||||
|
|
||||||
var title = Cryptpad.fixFileName(suggestName()) + ext;
|
|
||||||
|
|
||||||
Cryptpad.prompt(Messages.exportPrompt, title, function (filename) {
|
|
||||||
if (filename === null) { return; }
|
|
||||||
var blob = new Blob([text], {
|
|
||||||
type: 'text/plain;charset=utf-8'
|
|
||||||
});
|
|
||||||
saveAs(blob, filename);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
var importText = function (content, file) {
|
|
||||||
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
|
|
||||||
var mode;
|
|
||||||
var mime = CodeMirror.findModeByMIME(file.type);
|
|
||||||
|
|
||||||
if (!mime) {
|
|
||||||
var ext = /.+\.([^.]+)$/.exec(file.name);
|
|
||||||
if (ext[1]) {
|
|
||||||
mode = CodeMirror.findModeByExtension(ext[1]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mode = mime && mime.mode || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode && Modes.list.some(function (o) { return o.mode === mode; })) {
|
|
||||||
setMode(mode);
|
|
||||||
$bar.find('#language-mode').val(mode);
|
|
||||||
} else {
|
|
||||||
console.log("Couldn't find a suitable highlighting mode: %s", mode);
|
|
||||||
setMode('text');
|
|
||||||
$bar.find('#language-mode').val('text');
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.setValue(content);
|
|
||||||
onLocal();
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateTitle = function (newTitle) {
|
|
||||||
if (newTitle === APP.title) { return; }
|
|
||||||
// Change the title now, and set it back to the old value if there is an error
|
|
||||||
var oldTitle = APP.title;
|
|
||||||
APP.title = newTitle;
|
|
||||||
setTabTitle();
|
|
||||||
Cryptpad.renamePad(newTitle, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.log("Couldn't set pad title");
|
|
||||||
console.error(err);
|
|
||||||
APP.title = oldTitle;
|
|
||||||
setTabTitle();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
APP.title = data;
|
|
||||||
setTabTitle();
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('span.title').text(data);
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('input').val(data);
|
|
||||||
if (slideOptions.title) { Slide.updateOptions(); }
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateColors = function (text, back) {
|
|
||||||
if (text) {
|
if (text) {
|
||||||
textColor = text;
|
textColor = text;
|
||||||
$modal.css('color', text);
|
$modal.css('color', text);
|
||||||
@@ -397,56 +183,12 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var updateOptions = function (newOpt) {
|
|
||||||
if (stringify(newOpt) !== stringify(slideOptions)) {
|
|
||||||
$.extend(slideOptions, newOpt);
|
|
||||||
// TODO: manage realtime + cursor in the "options" modal ??
|
|
||||||
Slide.updateOptions();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateDefaultTitle = function (defaultTitle) {
|
|
||||||
defaultName = defaultTitle;
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('input').attr("placeholder", defaultName);
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateMetadata = function(shjson) {
|
|
||||||
// Extract the user list (metadata) from the hyperjson
|
|
||||||
var json = (shjson === "") ? "" : JSON.parse(shjson);
|
|
||||||
var titleUpdated = false;
|
|
||||||
if (json && json.metadata) {
|
|
||||||
if (json.metadata.users) {
|
|
||||||
var userData = json.metadata.users;
|
|
||||||
// Update the local user data
|
|
||||||
addToUserData(userData);
|
|
||||||
}
|
|
||||||
if (json.metadata.defaultTitle) {
|
|
||||||
updateDefaultTitle(json.metadata.defaultTitle);
|
|
||||||
}
|
|
||||||
if (typeof json.metadata.title !== "undefined") {
|
|
||||||
updateTitle(json.metadata.title || defaultName);
|
|
||||||
titleUpdated = true;
|
|
||||||
}
|
|
||||||
updateOptions(json.metadata.slideOptions);
|
|
||||||
updateColors(json.metadata.color, json.metadata.backColor);
|
|
||||||
}
|
|
||||||
if (!titleUpdated) {
|
|
||||||
updateTitle(defaultName);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var renameCb = function (err, title) {
|
|
||||||
if (err) { return; }
|
|
||||||
APP.title = title;
|
|
||||||
setTabTitle();
|
|
||||||
onLocal();
|
|
||||||
};
|
|
||||||
|
|
||||||
var createPrintDialog = function () {
|
var createPrintDialog = function () {
|
||||||
var slideOptionsTmp = {
|
var slideOptionsTmp = {
|
||||||
title: false,
|
title: false,
|
||||||
slide: false,
|
slide: false,
|
||||||
date: false,
|
date: false,
|
||||||
|
transition: true,
|
||||||
style: ''
|
style: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -479,10 +221,20 @@ define([
|
|||||||
}).appendTo($p).css('width', 'auto');
|
}).appendTo($p).css('width', 'auto');
|
||||||
$('<label>', {'for': 'checkTitle'}).text(Messages.printTitle).appendTo($p);
|
$('<label>', {'for': 'checkTitle'}).text(Messages.printTitle).appendTo($p);
|
||||||
$p.append($('<br>'));
|
$p.append($('<br>'));
|
||||||
|
// Transition
|
||||||
|
$('<input>', {type: 'checkbox', id: 'checkTransition', checked: slideOptionsTmp.transition}).on('change', function () {
|
||||||
|
var c = this.checked;
|
||||||
|
slideOptionsTmp.transition = c;
|
||||||
|
}).appendTo($p).css('width', 'auto');
|
||||||
|
$('<label>', {'for': 'checkTransition'}).text(Messages.printTransition).appendTo($p);
|
||||||
|
$p.append($('<br>'));
|
||||||
// CSS
|
// CSS
|
||||||
$('<label>', {'for': 'cssPrint'}).text(Messages.printCSS).appendTo($p);
|
$('<label>', {'for': 'cssPrint'}).text(Messages.printCSS).appendTo($p);
|
||||||
$p.append($('<br>'));
|
$p.append($('<br>'));
|
||||||
var $textarea = $('<textarea>', {'id':'cssPrint'}).css({'width':'100%', 'height':'100px'}).appendTo($p);
|
var $textarea = $('<textarea>', {'id':'cssPrint'}).css({'width':'100%', 'height':'100px'}).appendTo($p)
|
||||||
|
.on('keydown keyup', function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
$textarea.val(slideOptionsTmp.style);
|
$textarea.val(slideOptionsTmp.style);
|
||||||
window.setTimeout(function () { $textarea.focus(); }, 0);
|
window.setTimeout(function () { $textarea.focus(); }, 0);
|
||||||
|
|
||||||
@@ -503,73 +255,63 @@ define([
|
|||||||
h = Cryptpad.listenForKeys(todo, todoCancel);
|
h = Cryptpad.listenForKeys(todo, todoCancel);
|
||||||
|
|
||||||
var $nav = $('<nav>').appendTo($div);
|
var $nav = $('<nav>').appendTo($div);
|
||||||
var $cancel = $('<button>', {'class': 'cancel'}).text(Messages.cancelButton).appendTo($nav).click(todoCancel);
|
$('<button>', {'class': 'cancel'}).text(Messages.cancelButton).appendTo($nav).click(todoCancel);
|
||||||
var $ok = $('<button>', {'class': 'ok'}).text(Messages.slideOptionsButton).appendTo($nav).click(todo);
|
$('<button>', {'class': 'ok'}).text(Messages.settings_save).appendTo($nav).click(todo);
|
||||||
|
|
||||||
return $container;
|
return $container;
|
||||||
};
|
};
|
||||||
|
|
||||||
var onInit = config.onInit = function (info) {
|
config.onInit = function (info) {
|
||||||
userList = info.userList;
|
UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad);
|
||||||
|
|
||||||
|
var titleCfg = {
|
||||||
|
updateLocalTitle: setTabTitle,
|
||||||
|
getHeadingText: CodeMirror.getHeadingText
|
||||||
|
};
|
||||||
|
Title = Cryptpad.createTitle(titleCfg, config.onLocal, Cryptpad);
|
||||||
|
|
||||||
|
Metadata = Cryptpad.createMetadata(UserList, Title, metadataCfg);
|
||||||
|
|
||||||
var configTb = {
|
var configTb = {
|
||||||
displayed: ['useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad'],
|
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'],
|
||||||
userData: userData,
|
userList: UserList.getToolbarConfig(),
|
||||||
readOnly: readOnly,
|
|
||||||
ifrw: ifrw,
|
|
||||||
share: {
|
share: {
|
||||||
secret: secret,
|
secret: secret,
|
||||||
channel: info.channel
|
channel: info.channel
|
||||||
},
|
},
|
||||||
title: {
|
title: Title.getTitleConfig(),
|
||||||
onRename: renameCb,
|
common: Cryptpad,
|
||||||
defaultName: defaultName,
|
readOnly: readOnly,
|
||||||
suggestName: suggestName
|
ifrw: ifrw,
|
||||||
},
|
realtime: info.realtime,
|
||||||
common: Cryptpad
|
network: info.network,
|
||||||
|
$container: $bar
|
||||||
};
|
};
|
||||||
toolbar = module.toolbar = Toolbar.create($bar, info.myID, info.realtime, info.getLag, info.userList, configTb);
|
toolbar = module.toolbar = Toolbar.create(configTb);
|
||||||
|
|
||||||
var $rightside = $bar.find('.' + Toolbar.constants.rightside);
|
Title.setToolbar(toolbar);
|
||||||
var $userBlock = $bar.find('.' + Toolbar.constants.username);
|
CodeMirror.init(config.onLocal, Title, toolbar);
|
||||||
var $usernameButton = module.$userNameButton = $($bar.find('.' + Toolbar.constants.changeUsername));
|
|
||||||
|
var $rightside = toolbar.$rightside;
|
||||||
|
|
||||||
var editHash;
|
var editHash;
|
||||||
var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys);
|
|
||||||
|
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add a history button */
|
/* add a history button */
|
||||||
var histConfig = {};
|
var histConfig = {
|
||||||
histConfig.onRender = function (val) {
|
onLocal: config.onLocal(),
|
||||||
if (typeof val === "undefined") { return; }
|
onRemote: config.onRemote(),
|
||||||
try {
|
setHistory: setHistory,
|
||||||
var hjson = JSON.parse(val || '{}');
|
applyVal: function (val) {
|
||||||
var remoteDoc = hjson.content;
|
var remoteDoc = JSON.parse(val || '{}').content;
|
||||||
editor.setValue(remoteDoc || '');
|
editor.setValue(remoteDoc || '');
|
||||||
editor.save();
|
editor.save();
|
||||||
} catch (e) {
|
},
|
||||||
// Probably a parse error
|
$toolbar: $bar
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
histConfig.onClose = function () {
|
|
||||||
// Close button clicked
|
|
||||||
setHistory(false, true);
|
|
||||||
};
|
|
||||||
histConfig.onRevert = function () {
|
|
||||||
// Revert button clicked
|
|
||||||
setHistory(false, false);
|
|
||||||
config.onLocal();
|
|
||||||
config.onRemote();
|
|
||||||
};
|
|
||||||
histConfig.onReady = function () {
|
|
||||||
// Called when the history is loaded and the UI displayed
|
|
||||||
setHistory(true);
|
|
||||||
};
|
|
||||||
histConfig.$toolbar = $bar;
|
|
||||||
var $hist = Cryptpad.createButton('history', true, {histConfig: histConfig});
|
var $hist = Cryptpad.createButton('history', true, {histConfig: histConfig});
|
||||||
$rightside.append($hist);
|
$rightside.append($hist);
|
||||||
|
|
||||||
@@ -585,21 +327,17 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* add an export button */
|
/* add an export button */
|
||||||
var $export = Cryptpad.createButton('export', true, {}, exportText);
|
var $export = Cryptpad.createButton('export', true, {}, CodeMirror.exportText);
|
||||||
$rightside.append($export);
|
$rightside.append($export);
|
||||||
|
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
/* add an import button */
|
/* add an import button */
|
||||||
var $import = Cryptpad.createButton('import', true, {}, importText);
|
var $import = Cryptpad.createButton('import', true, {}, CodeMirror.importText);
|
||||||
$rightside.append($import);
|
$rightside.append($import);
|
||||||
|
|
||||||
/* add a rename button */
|
|
||||||
//var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb);
|
|
||||||
//$rightside.append($setTitle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add a forget button */
|
/* add a forget button */
|
||||||
var forgetCb = function (err, title) {
|
var forgetCb = function (err) {
|
||||||
if (err) { return; }
|
if (err) { return; }
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
};
|
};
|
||||||
@@ -641,50 +379,6 @@ define([
|
|||||||
}
|
}
|
||||||
$rightside.append($present);
|
$rightside.append($present);
|
||||||
|
|
||||||
var $leavePresent = Cryptpad.createButton('source', true)
|
|
||||||
.click(leavePresentationMode);
|
|
||||||
if (!presentMode) {
|
|
||||||
$leavePresent.hide();
|
|
||||||
}
|
|
||||||
$rightside.append($leavePresent);
|
|
||||||
|
|
||||||
var configureTheme = function () {
|
|
||||||
/* Remember the user's last choice of theme using localStorage */
|
|
||||||
var themeKey = 'CRYPTPAD_CODE_THEME';
|
|
||||||
var lastTheme = localStorage.getItem(themeKey) || 'default';
|
|
||||||
|
|
||||||
var options = [];
|
|
||||||
Themes.forEach(function (l) {
|
|
||||||
options.push({
|
|
||||||
tag: 'a',
|
|
||||||
attributes: {
|
|
||||||
'data-value': l.name,
|
|
||||||
'href': '#',
|
|
||||||
},
|
|
||||||
content: l.name // Pretty name of the language value
|
|
||||||
});
|
|
||||||
});
|
|
||||||
var dropdownConfig = {
|
|
||||||
text: 'Theme', // Button initial text
|
|
||||||
options: options, // Entries displayed in the menu
|
|
||||||
left: true, // Open to the left of the button
|
|
||||||
isSelect: true,
|
|
||||||
initialValue: lastTheme
|
|
||||||
};
|
|
||||||
var $block = module.$theme = Cryptpad.createDropdown(dropdownConfig);
|
|
||||||
var $button = $block.find('.buttonTitle');
|
|
||||||
|
|
||||||
setTheme(lastTheme, $block);
|
|
||||||
|
|
||||||
$block.find('a').click(function (e) {
|
|
||||||
var theme = $(this).attr('data-value');
|
|
||||||
setTheme(theme, $block);
|
|
||||||
localStorage.setItem(themeKey, theme);
|
|
||||||
});
|
|
||||||
|
|
||||||
$rightside.append($block);
|
|
||||||
};
|
|
||||||
|
|
||||||
var configureColors = function () {
|
var configureColors = function () {
|
||||||
var $back = $('<button>', {
|
var $back = $('<button>', {
|
||||||
id: SLIDE_BACKCOLOR_ID,
|
id: SLIDE_BACKCOLOR_ID,
|
||||||
@@ -731,7 +425,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
configureColors();
|
configureColors();
|
||||||
configureTheme();
|
CodeMirror.configureTheme();
|
||||||
|
|
||||||
if (presentMode) {
|
if (presentMode) {
|
||||||
$('#top-bar').hide();
|
$('#top-bar').hide();
|
||||||
@@ -741,27 +435,9 @@ define([
|
|||||||
if (!window.location.hash || window.location.hash === '#') {
|
if (!window.location.hash || window.location.hash === '#') {
|
||||||
Cryptpad.replaceHash(editHash);
|
Cryptpad.replaceHash(editHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
Cryptpad.onDisplayNameChanged(setName);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var unnotify = module.unnotify = function () {
|
config.onReady = function (info) {
|
||||||
if (module.tabNotification &&
|
|
||||||
typeof(module.tabNotification.cancel) === 'function') {
|
|
||||||
module.tabNotification.cancel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var notify = module.notify = function () {
|
|
||||||
if (Visible.isSupported() && !Visible.currently()) {
|
|
||||||
unnotify();
|
|
||||||
module.tabNotification = Notify.tab(1000, 10);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var onReady = config.onReady = function (info) {
|
|
||||||
module.users = info.userList.users;
|
|
||||||
|
|
||||||
if (module.realtime !== info.realtime) {
|
if (module.realtime !== info.realtime) {
|
||||||
var realtime = module.realtime = info.realtime;
|
var realtime = module.realtime = info.realtime;
|
||||||
module.patchText = TextPatcher.create({
|
module.patchText = TextPatcher.create({
|
||||||
@@ -787,142 +463,64 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hjson.highlightMode) {
|
if (hjson.highlightMode) {
|
||||||
setMode(hjson.highlightMode, module.$language);
|
CodeMirror.setMode(hjson.highlightMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!CodeMirror.highlightMode) {
|
||||||
if (!module.highlightMode) {
|
CodeMirror.setMode('markdown');
|
||||||
setMode('javascript', module.$language);
|
|
||||||
console.log("%s => %s", module.highlightMode, module.$language.val());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the user list (metadata) from the hyperjson
|
// Update the user list (metadata) from the hyperjson
|
||||||
updateMetadata(userDoc);
|
Metadata.update(userDoc);
|
||||||
|
|
||||||
editor.setValue(newDoc || initialState);
|
editor.setValue(newDoc || initialState);
|
||||||
|
|
||||||
if (Cryptpad.initialName && APP.title === defaultName) {
|
if (Cryptpad.initialName && Title.isDefaultTitle()) {
|
||||||
updateTitle(Cryptpad.initialName);
|
Title.updateTitle(Cryptpad.initialName);
|
||||||
onLocal();
|
onLocal();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Visible.isSupported()) {
|
|
||||||
Visible.onChange(function (yes) {
|
|
||||||
if (yes) { unnotify(); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Slide.onChange(function (o, n, l) {
|
Slide.onChange(function (o, n, l) {
|
||||||
if (n !== null) {
|
if (n !== null) {
|
||||||
document.title = APP.title + ' (' + (++n) + '/' + l + ')';
|
document.title = Title.title + ' (' + (++n) + '/' + l + ')';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("Exiting presentation mode");
|
console.log("Exiting presentation mode");
|
||||||
document.title = APP.title;
|
document.title = Title.title;
|
||||||
});
|
});
|
||||||
|
|
||||||
Cryptpad.removeLoadingScreen();
|
Cryptpad.removeLoadingScreen();
|
||||||
setEditable(true);
|
setEditable(true);
|
||||||
initializing = false;
|
initializing = false;
|
||||||
//Cryptpad.log("Your document is ready");
|
|
||||||
|
|
||||||
onLocal(); // push local state to avoid parse errors later.
|
onLocal(); // push local state to avoid parse errors later.
|
||||||
Cryptpad.getLastName(function (err, lastName) {
|
|
||||||
if (err) {
|
if (readOnly) { return; }
|
||||||
console.log("Could not get previous name");
|
UserList.getLastName(toolbar.$userNameButton, isNew);
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Update the toolbar list:
|
|
||||||
// Add the current user in the metadata if he has edit rights
|
|
||||||
if (readOnly) { return; }
|
|
||||||
if (typeof(lastName) === 'string') {
|
|
||||||
setName(lastName);
|
|
||||||
} else {
|
|
||||||
myData[myID] = {
|
|
||||||
name: "",
|
|
||||||
uid: Cryptpad.getUid(),
|
|
||||||
};
|
|
||||||
addToUserData(myData);
|
|
||||||
onLocal();
|
|
||||||
module.$userNameButton.click();
|
|
||||||
}
|
|
||||||
if (isNew) {
|
|
||||||
Cryptpad.selectTemplate('slide', info.realtime, Cryptget);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var cursorToPos = function(cursor, oldText) {
|
config.onRemote = function () {
|
||||||
var cLine = cursor.line;
|
|
||||||
var cCh = cursor.ch;
|
|
||||||
var pos = 0;
|
|
||||||
var textLines = oldText.split("\n");
|
|
||||||
for (var line = 0; line <= cLine; line++) {
|
|
||||||
if(line < cLine) {
|
|
||||||
pos += textLines[line].length+1;
|
|
||||||
}
|
|
||||||
else if(line === cLine) {
|
|
||||||
pos += cCh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
};
|
|
||||||
|
|
||||||
var posToCursor = function(position, newText) {
|
|
||||||
var cursor = {
|
|
||||||
line: 0,
|
|
||||||
ch: 0
|
|
||||||
};
|
|
||||||
var textLines = newText.substr(0, position).split("\n");
|
|
||||||
cursor.line = textLines.length - 1;
|
|
||||||
cursor.ch = textLines[cursor.line].length;
|
|
||||||
return cursor;
|
|
||||||
};
|
|
||||||
|
|
||||||
var onRemote = config.onRemote = function () {
|
|
||||||
if (initializing) { return; }
|
if (initializing) { return; }
|
||||||
if (isHistoryMode) { return; }
|
if (isHistoryMode) { return; }
|
||||||
var scroll = editor.getScrollInfo();
|
|
||||||
|
|
||||||
var oldDoc = canonicalize($textarea.val());
|
var oldDoc = canonicalize(CodeMirror.$textarea.val());
|
||||||
var shjson = module.realtime.getUserDoc();
|
var shjson = module.realtime.getUserDoc();
|
||||||
|
|
||||||
// Update the user list (metadata) from the hyperjson
|
// Update the user list (metadata) from the hyperjson
|
||||||
updateMetadata(shjson);
|
Metadata.update(shjson);
|
||||||
|
|
||||||
var hjson = JSON.parse(shjson);
|
var hjson = JSON.parse(shjson);
|
||||||
var remoteDoc = hjson.content;
|
var remoteDoc = hjson.content;
|
||||||
|
|
||||||
var highlightMode = hjson.highlightMode;
|
var highlightMode = hjson.highlightMode;
|
||||||
if (highlightMode && highlightMode !== module.highlightMode) {
|
if (highlightMode && highlightMode !== CodeMirror.highlightMode) {
|
||||||
setMode(highlightMode, module.$language);
|
CodeMirror.setMode(highlightMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
//get old cursor here
|
CodeMirror.setValueAndCursor(oldDoc, remoteDoc, TextPatcher);
|
||||||
var oldCursor = {};
|
|
||||||
oldCursor.selectionStart = cursorToPos(editor.getCursor('from'), oldDoc);
|
|
||||||
oldCursor.selectionEnd = cursorToPos(editor.getCursor('to'), oldDoc);
|
|
||||||
|
|
||||||
editor.setValue(remoteDoc);
|
|
||||||
editor.save();
|
|
||||||
|
|
||||||
var op = TextPatcher.diff(oldDoc, remoteDoc);
|
|
||||||
var selects = ['selectionStart', 'selectionEnd'].map(function (attr) {
|
|
||||||
return TextPatcher.transformCursor(oldCursor[attr], op);
|
|
||||||
});
|
|
||||||
|
|
||||||
if(selects[0] === selects[1]) {
|
|
||||||
editor.setCursor(posToCursor(selects[0], remoteDoc));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
editor.setSelection(posToCursor(selects[0], remoteDoc), posToCursor(selects[1], remoteDoc));
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.scrollTo(scroll.left, scroll.top);
|
|
||||||
|
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
var textValue = canonicalize($textarea.val());
|
var textValue = canonicalize(CodeMirror.$textarea.val());
|
||||||
var shjson2 = stringifyInner(textValue);
|
var shjson2 = stringifyInner(textValue);
|
||||||
if (shjson2 !== shjson) {
|
if (shjson2 !== shjson) {
|
||||||
console.error("shjson2 !== shjson");
|
console.error("shjson2 !== shjson");
|
||||||
@@ -933,18 +531,18 @@ define([
|
|||||||
Slide.update(remoteDoc);
|
Slide.update(remoteDoc);
|
||||||
|
|
||||||
if (oldDoc !== remoteDoc) {
|
if (oldDoc !== remoteDoc) {
|
||||||
notify();
|
Cryptpad.notify();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var onAbort = config.onAbort = function (info) {
|
config.onAbort = function () {
|
||||||
// inform of network disconnect
|
// inform of network disconnect
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
toolbar.failed();
|
toolbar.failed();
|
||||||
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
var onConnectionChange = config.onConnectionChange = function (info) {
|
config.onConnectionChange = function (info) {
|
||||||
setEditable(info.state);
|
setEditable(info.state);
|
||||||
toolbar.failed();
|
toolbar.failed();
|
||||||
if (info.state) {
|
if (info.state) {
|
||||||
@@ -956,9 +554,9 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var onError = config.onError = onConnectError;
|
config.onError = onConnectError;
|
||||||
|
|
||||||
var realtime = module.realtime = Realtime.start(config);
|
module.realtime = Realtime.start(config);
|
||||||
|
|
||||||
editor.on('change', onLocal);
|
editor.on('change', onLocal);
|
||||||
|
|
||||||
@@ -968,7 +566,7 @@ define([
|
|||||||
var interval = 100;
|
var interval = 100;
|
||||||
|
|
||||||
var second = function (CM) {
|
var second = function (CM) {
|
||||||
Cryptpad.ready(function (err, env) {
|
Cryptpad.ready(function () {
|
||||||
andThen(CM);
|
andThen(CM);
|
||||||
Cryptpad.reportAppUsage();
|
Cryptpad.reportAppUsage();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -168,13 +168,16 @@ body .CodeMirror-focused .cm-matchhighlight {
|
|||||||
}
|
}
|
||||||
.cp div.modal #content,
|
.cp div.modal #content,
|
||||||
.cp div#modal #content {
|
.cp div#modal #content {
|
||||||
transition: margin-left 1s;
|
|
||||||
font-size: 20vh;
|
font-size: 20vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
white-space: nowrap;
|
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,
|
||||||
.cp div#modal #content .slide-frame {
|
.cp div#modal #content .slide-frame {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -198,10 +201,11 @@ body .CodeMirror-focused .cm-matchhighlight {
|
|||||||
}
|
}
|
||||||
.cp div.modal #content .slide-container,
|
.cp div.modal #content .slide-container,
|
||||||
.cp div#modal #content .slide-container {
|
.cp div#modal #content .slide-container {
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
.cp div.modal .center,
|
.cp div.modal .center,
|
||||||
.cp div#modal .center {
|
.cp div#modal .center {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ define([
|
|||||||
|
|
||||||
var checkedTaskItemPtn = /^\s*\[x\]\s*/;
|
var checkedTaskItemPtn = /^\s*\[x\]\s*/;
|
||||||
var uncheckedTaskItemPtn = /^\s*\[ \]\s*/;
|
var uncheckedTaskItemPtn = /^\s*\[ \]\s*/;
|
||||||
renderer.listitem = function (text, level) {
|
renderer.listitem = function (text) {
|
||||||
var isCheckedTaskItem = checkedTaskItemPtn.test(text);
|
var isCheckedTaskItem = checkedTaskItemPtn.test(text);
|
||||||
var isUncheckedTaskItem = uncheckedTaskItemPtn.test(text);
|
var isUncheckedTaskItem = uncheckedTaskItemPtn.test(text);
|
||||||
if (isCheckedTaskItem) {
|
if (isCheckedTaskItem) {
|
||||||
@@ -28,8 +28,6 @@ define([
|
|||||||
renderer: renderer
|
renderer: renderer
|
||||||
});
|
});
|
||||||
|
|
||||||
var truthy = function (x) { return x; };
|
|
||||||
|
|
||||||
var Slide = {
|
var Slide = {
|
||||||
index: 0,
|
index: 0,
|
||||||
lastIndex: 0,
|
lastIndex: 0,
|
||||||
@@ -59,8 +57,7 @@ define([
|
|||||||
|
|
||||||
var change = function (oldIndex, newIndex) {
|
var change = function (oldIndex, newIndex) {
|
||||||
if (Slide.changeHandlers.length) {
|
if (Slide.changeHandlers.length) {
|
||||||
Slide.changeHandlers.some(function (f, i) {
|
Slide.changeHandlers.some(function (f) {
|
||||||
// HERE
|
|
||||||
f(oldIndex, newIndex, getNumberOfSlides());
|
f(oldIndex, newIndex, getNumberOfSlides());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -187,6 +184,10 @@ define([
|
|||||||
$('<div>', {'class': 'slideTitle'}).text(APP.title).appendTo($(el));
|
$('<div>', {'class': 'slideTitle'}).text(APP.title).appendTo($(el));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$content.removeClass('transition');
|
||||||
|
if (options.transition) {
|
||||||
|
$content.addClass('transition');
|
||||||
|
}
|
||||||
//$content.find('.' + slideClass).hide();
|
//$content.find('.' + slideClass).hide();
|
||||||
//$content.find('.' + slideClass + ':eq( ' + i + ' )').show();
|
//$content.find('.' + slideClass + ':eq( ' + i + ' )').show();
|
||||||
$content.css('margin-left', -(i*100)+'vw');
|
$content.css('margin-left', -(i*100)+'vw');
|
||||||
@@ -194,7 +195,7 @@ define([
|
|||||||
change(Slide.lastIndex, Slide.index);
|
change(Slide.lastIndex, Slide.index);
|
||||||
};
|
};
|
||||||
|
|
||||||
var updateOptions = Slide.updateOptions = function () {
|
Slide.updateOptions = function () {
|
||||||
draw(Slide.index);
|
draw(Slide.index);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -214,7 +215,10 @@ define([
|
|||||||
$(ifrw).focus();
|
$(ifrw).focus();
|
||||||
change(null, Slide.index);
|
change(null, Slide.index);
|
||||||
if (!isPresentURL()) {
|
if (!isPresentURL()) {
|
||||||
window.location.hash += '/present';
|
if (window.location.href.slice(-1) !== '/') {
|
||||||
|
window.location.hash += '/';
|
||||||
|
}
|
||||||
|
window.location.hash += 'present';
|
||||||
}
|
}
|
||||||
$pad.contents().find('.cryptpad-present-button').hide();
|
$pad.contents().find('.cryptpad-present-button').hide();
|
||||||
$pad.contents().find('.cryptpad-source-button').show();
|
$pad.contents().find('.cryptpad-source-button').show();
|
||||||
@@ -223,7 +227,7 @@ define([
|
|||||||
$('.top-bar').hide();
|
$('.top-bar').hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
window.location.hash = window.location.hash.replace(/\/present$/, '');
|
window.location.hash = window.location.hash.replace(/\/present$/, '/');
|
||||||
change(Slide.index, null);
|
change(Slide.index, null);
|
||||||
$pad.contents().find('.cryptpad-present-button').show();
|
$pad.contents().find('.cryptpad-present-button').show();
|
||||||
$pad.contents().find('.cryptpad-source-button').hide();
|
$pad.contents().find('.cryptpad-source-button').hide();
|
||||||
@@ -233,7 +237,7 @@ define([
|
|||||||
$modal.removeClass('shown');
|
$modal.removeClass('shown');
|
||||||
};
|
};
|
||||||
|
|
||||||
var update = Slide.update = function (content, init) {
|
Slide.update = function (content, init) {
|
||||||
if (!Slide.shown && !init) { return; }
|
if (!Slide.shown && !init) { return; }
|
||||||
if (!content) { content = ''; }
|
if (!content) { content = ''; }
|
||||||
var old = Slide.content;
|
var old = Slide.content;
|
||||||
@@ -245,7 +249,7 @@ define([
|
|||||||
change(Slide.lastIndex, Slide.index);
|
change(Slide.lastIndex, Slide.index);
|
||||||
};
|
};
|
||||||
|
|
||||||
var left = Slide.left = function () {
|
Slide.left = function () {
|
||||||
console.log('left');
|
console.log('left');
|
||||||
Slide.lastIndex = Slide.index;
|
Slide.lastIndex = Slide.index;
|
||||||
|
|
||||||
@@ -253,7 +257,7 @@ define([
|
|||||||
Slide.draw(i);
|
Slide.draw(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
var right = Slide.right = function () {
|
Slide.right = function () {
|
||||||
console.log('right');
|
console.log('right');
|
||||||
Slide.lastIndex = Slide.index;
|
Slide.lastIndex = Slide.index;
|
||||||
|
|
||||||
@@ -261,7 +265,7 @@ define([
|
|||||||
Slide.draw(i);
|
Slide.draw(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
var first = Slide.first = function () {
|
Slide.first = function () {
|
||||||
console.log('first');
|
console.log('first');
|
||||||
Slide.lastIndex = Slide.index;
|
Slide.lastIndex = Slide.index;
|
||||||
|
|
||||||
@@ -269,7 +273,7 @@ define([
|
|||||||
Slide.draw(i);
|
Slide.draw(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
var last = Slide.last = function () {
|
Slide.last = function () {
|
||||||
console.log('end');
|
console.log('end');
|
||||||
Slide.lastIndex = Slide.index;
|
Slide.lastIndex = Slide.index;
|
||||||
|
|
||||||
@@ -279,7 +283,7 @@ define([
|
|||||||
|
|
||||||
var addEvent = function () {
|
var addEvent = function () {
|
||||||
var icon_to;
|
var icon_to;
|
||||||
$modal.mousemove(function (e) {
|
$modal.mousemove(function () {
|
||||||
var $buttons = $modal.find('.button');
|
var $buttons = $modal.find('.button');
|
||||||
$buttons.show();
|
$buttons.show();
|
||||||
if (icon_to) { window.clearTimeout(icon_to); }
|
if (icon_to) { window.clearTimeout(icon_to); }
|
||||||
@@ -287,17 +291,17 @@ define([
|
|||||||
$buttons.fadeOut();
|
$buttons.fadeOut();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
$modal.find('#button_exit').click(function (e) {
|
$modal.find('#button_exit').click(function () {
|
||||||
var ev = $.Event("keyup");
|
var ev = $.Event("keyup");
|
||||||
ev.which = 27;
|
ev.which = 27;
|
||||||
$modal.trigger(ev);
|
$modal.trigger(ev);
|
||||||
});
|
});
|
||||||
$modal.find('#button_left').click(function (e) {
|
$modal.find('#button_left').click(function () {
|
||||||
var ev = $.Event("keyup");
|
var ev = $.Event("keyup");
|
||||||
ev.which = 37;
|
ev.which = 37;
|
||||||
$modal.trigger(ev);
|
$modal.trigger(ev);
|
||||||
});
|
});
|
||||||
$modal.find('#button_right').click(function (e) {
|
$modal.find('#button_right').click(function () {
|
||||||
var ev = $.Event("keyup");
|
var ev = $.Event("keyup");
|
||||||
ev.which = 39;
|
ev.which = 39;
|
||||||
$modal.trigger(ev);
|
$modal.trigger(ev);
|
||||||
|
|||||||
@@ -161,7 +161,9 @@ div.modal, div#modal {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
#content {
|
#content {
|
||||||
transition: margin-left 1s;
|
&.transition {
|
||||||
|
transition: margin-left 1s;
|
||||||
|
}
|
||||||
font-size: 20vh;
|
font-size: 20vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -191,9 +193,10 @@ div.modal, div#modal {
|
|||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
.slide-container {
|
.slide-container {
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
height: 100%; width: 100vw;
|
height: 100%; width: 100vw;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,19 +3,17 @@ define([
|
|||||||
'/api/config',
|
'/api/config',
|
||||||
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
||||||
'/bower_components/chainpad-crypto/crypto.js',
|
'/bower_components/chainpad-crypto/crypto.js',
|
||||||
'/common/toolbar.js',
|
'/common/toolbar2.js',
|
||||||
'/bower_components/textpatcher/TextPatcher.amd.js',
|
'/bower_components/textpatcher/TextPatcher.amd.js',
|
||||||
'json.sortify',
|
'json.sortify',
|
||||||
'/bower_components/chainpad-json-validator/json-ot.js',
|
'/bower_components/chainpad-json-validator/json-ot.js',
|
||||||
'/common/cryptpad-common.js',
|
'/common/cryptpad-common.js',
|
||||||
'/common/cryptget.js',
|
'/common/cryptget.js',
|
||||||
'/whiteboard/colors.js',
|
'/whiteboard/colors.js',
|
||||||
'/common/visible.js',
|
|
||||||
'/common/notify.js',
|
|
||||||
'/customize/application_config.js',
|
'/customize/application_config.js',
|
||||||
'/bower_components/secure-fabric.js/dist/fabric.min.js',
|
'/bower_components/secure-fabric.js/dist/fabric.min.js',
|
||||||
'/bower_components/file-saver/FileSaver.min.js',
|
'/bower_components/file-saver/FileSaver.min.js',
|
||||||
], function ($, Config, Realtime, Crypto, Toolbar, TextPatcher, JSONSortify, JsonOT, Cryptpad, Cryptget, Colors, Visible, Notify, AppConfig) {
|
], function ($, Config, Realtime, Crypto, Toolbar, TextPatcher, JSONSortify, JsonOT, Cryptpad, Cryptget, Colors, AppConfig) {
|
||||||
var saveAs = window.saveAs;
|
var saveAs = window.saveAs;
|
||||||
var Messages = Cryptpad.Messages;
|
var Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
@@ -24,7 +22,7 @@ define([
|
|||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
Cryptpad.addLoadingScreen();
|
Cryptpad.addLoadingScreen();
|
||||||
var onConnectError = function (info) {
|
var onConnectError = function () {
|
||||||
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
||||||
};
|
};
|
||||||
var toolbar;
|
var toolbar;
|
||||||
@@ -212,35 +210,10 @@ window.canvas = canvas;
|
|||||||
var initializing = true;
|
var initializing = true;
|
||||||
|
|
||||||
var $bar = $('#toolbar');
|
var $bar = $('#toolbar');
|
||||||
var parsedHash = Cryptpad.parsePadUrl(window.location.href);
|
|
||||||
var defaultName = Cryptpad.getDefaultName(parsedHash);
|
|
||||||
var userData = module.userData = {}; // List of pretty name of all users (mapped with their server ID)
|
|
||||||
var userList; // List of users still connected to the channel (server IDs)
|
|
||||||
var addToUserData = function(data) {
|
|
||||||
var users = module.users;
|
|
||||||
for (var attrname in data) { userData[attrname] = data[attrname]; }
|
|
||||||
|
|
||||||
if (users && users.length) {
|
var Title;
|
||||||
for (var userKey in userData) {
|
var UserList;
|
||||||
if (users.indexOf(userKey) === -1) {
|
var Metadata;
|
||||||
delete userData[userKey];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(userList && typeof userList.onChange === "function") {
|
|
||||||
userList.onChange(userData);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var myData = {};
|
|
||||||
var myUserName = ''; // My "pretty name"
|
|
||||||
var myID; // My server ID
|
|
||||||
|
|
||||||
var setMyID = function(info) {
|
|
||||||
myID = info.myID || null;
|
|
||||||
myUserName = myID;
|
|
||||||
};
|
|
||||||
|
|
||||||
var config = module.config = {
|
var config = module.config = {
|
||||||
initialState: '{}',
|
initialState: '{}',
|
||||||
@@ -249,7 +222,6 @@ window.canvas = canvas;
|
|||||||
readOnly: readOnly,
|
readOnly: readOnly,
|
||||||
channel: secret.channel,
|
channel: secret.channel,
|
||||||
crypto: Crypto.createEncryptor(secret.keys),
|
crypto: Crypto.createEncryptor(secret.keys),
|
||||||
setMyID: setMyID,
|
|
||||||
transformFunction: JsonOT.transform,
|
transformFunction: JsonOT.transform,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -280,28 +252,14 @@ window.canvas = canvas;
|
|||||||
$colors.append($color);
|
$colors.append($color);
|
||||||
};
|
};
|
||||||
|
|
||||||
var updatePalette = function (newPalette) {
|
var metadataCfg = {};
|
||||||
|
var updatePalette = metadataCfg.updatePalette = function (newPalette) {
|
||||||
palette = newPalette;
|
palette = newPalette;
|
||||||
$colors.html('<div class="hidden"> </div>');
|
$colors.html('<div class="hidden"> </div>');
|
||||||
palette.forEach(addColorToPalette);
|
palette.forEach(addColorToPalette);
|
||||||
};
|
};
|
||||||
updatePalette(palette);
|
updatePalette(palette);
|
||||||
|
|
||||||
var suggestName = function (fallback) {
|
|
||||||
if (document.title === defaultName) {
|
|
||||||
return fallback || "";
|
|
||||||
} else {
|
|
||||||
return document.title || defaultName;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var renameCb = function (err, title) {
|
|
||||||
if (err) { return; }
|
|
||||||
document.title = title;
|
|
||||||
config.onLocal();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var makeColorButton = function ($container) {
|
var makeColorButton = function ($container) {
|
||||||
var $testColor = $('<input>', { type: 'color', value: '!' });
|
var $testColor = $('<input>', { type: 'color', value: '!' });
|
||||||
|
|
||||||
@@ -330,31 +288,34 @@ window.canvas = canvas;
|
|||||||
return $color;
|
return $color;
|
||||||
};
|
};
|
||||||
|
|
||||||
var editHash;
|
config.onInit = function (info) {
|
||||||
var onInit = config.onInit = function (info) {
|
UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad);
|
||||||
userList = info.userList;
|
|
||||||
var config = {
|
Title = Cryptpad.createTitle({}, config.onLocal, Cryptpad);
|
||||||
displayed: ['useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad'],
|
|
||||||
userData: userData,
|
Metadata = Cryptpad.createMetadata(UserList, Title, metadataCfg);
|
||||||
readOnly: readOnly,
|
|
||||||
|
var configTb = {
|
||||||
|
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'],
|
||||||
|
userList: UserList.getToolbarConfig(),
|
||||||
share: {
|
share: {
|
||||||
secret: secret,
|
secret: secret,
|
||||||
channel: info.channel
|
channel: info.channel
|
||||||
},
|
},
|
||||||
|
title: Title.getTitleConfig(),
|
||||||
|
common: Cryptpad,
|
||||||
|
readOnly: readOnly,
|
||||||
ifrw: window,
|
ifrw: window,
|
||||||
title: {
|
realtime: info.realtime,
|
||||||
onRename: renameCb,
|
network: info.network,
|
||||||
defaultName: defaultName,
|
$container: $bar
|
||||||
suggestName: suggestName
|
|
||||||
},
|
|
||||||
common: Cryptpad
|
|
||||||
};
|
};
|
||||||
if (readOnly) {delete config.changeNameID; }
|
|
||||||
|
|
||||||
toolbar = module.toolbar = Toolbar.create($bar, info.myID, info.realtime, info.getLag, userList, config);
|
toolbar = module.toolbar = Toolbar.create(configTb);
|
||||||
|
|
||||||
var $rightside = $bar.find('.' + Toolbar.constants.rightside);
|
Title.setToolbar(toolbar);
|
||||||
module.$userNameButton = $($bar.find('.' + Toolbar.constants.changeUsername));
|
|
||||||
|
var $rightside = toolbar.$rightside;
|
||||||
|
|
||||||
/* save as template */
|
/* save as template */
|
||||||
if (!Cryptpad.isTemplate(window.location.href)) {
|
if (!Cryptpad.isTemplate(window.location.href)) {
|
||||||
@@ -370,7 +331,7 @@ window.canvas = canvas;
|
|||||||
var $export = Cryptpad.createButton('export', true, {}, saveImage);
|
var $export = Cryptpad.createButton('export', true, {}, saveImage);
|
||||||
$rightside.append($export);
|
$rightside.append($export);
|
||||||
|
|
||||||
var $forget = Cryptpad.createButton('forget', true, {}, function (err, title) {
|
var $forget = Cryptpad.createButton('forget', true, {}, function (err) {
|
||||||
if (err) { return; }
|
if (err) { return; }
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
toolbar.failed();
|
toolbar.failed();
|
||||||
@@ -380,14 +341,11 @@ window.canvas = canvas;
|
|||||||
makeColorButton($rightside);
|
makeColorButton($rightside);
|
||||||
|
|
||||||
var editHash;
|
var editHash;
|
||||||
var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys);
|
|
||||||
|
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
||||||
}
|
}
|
||||||
if (!readOnly) { Cryptpad.replaceHash(editHash); }
|
if (!readOnly) { Cryptpad.replaceHash(editHash); }
|
||||||
|
|
||||||
Cryptpad.onDisplayNameChanged(module.setName);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// used for debugging, feel free to remove
|
// used for debugging, feel free to remove
|
||||||
@@ -401,75 +359,11 @@ window.canvas = canvas;
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var updateTitle = function (newTitle) {
|
|
||||||
if (newTitle === document.title) { return; }
|
|
||||||
// Change the title now, and set it back to the old value if there is an error
|
|
||||||
var oldTitle = document.title;
|
|
||||||
document.title = newTitle;
|
|
||||||
Cryptpad.renamePad(newTitle, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.log("Couldn't set pad title");
|
|
||||||
console.error(err);
|
|
||||||
document.title = oldTitle;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
document.title = data;
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('span.title').text(data);
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('input').val(data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateDefaultTitle = function (defaultTitle) {
|
|
||||||
defaultName = defaultTitle;
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('input').attr("placeholder", defaultName);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var updateMetadata = function(shjson) {
|
|
||||||
// Extract the user list (metadata) from the hyperjson
|
|
||||||
var json = (shjson === "") ? "" : JSON.parse(shjson);
|
|
||||||
var titleUpdated = false;
|
|
||||||
if (json && json.metadata) {
|
|
||||||
if (json.metadata.users) {
|
|
||||||
var userData = json.metadata.users;
|
|
||||||
// Update the local user data
|
|
||||||
addToUserData(userData);
|
|
||||||
}
|
|
||||||
if (json.metadata.defaultTitle) {
|
|
||||||
updateDefaultTitle(json.metadata.defaultTitle);
|
|
||||||
}
|
|
||||||
if (typeof json.metadata.title !== "undefined") {
|
|
||||||
updateTitle(json.metadata.title || defaultName);
|
|
||||||
titleUpdated = true;
|
|
||||||
}
|
|
||||||
if (typeof(json.metadata.palette) !== 'undefined') {
|
|
||||||
updatePalette(json.metadata.palette);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!titleUpdated) {
|
|
||||||
updateTitle(defaultName);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var unnotify = function () {
|
|
||||||
if (module.tabNotification &&
|
|
||||||
typeof(module.tabNotification.cancel) === 'function') {
|
|
||||||
module.tabNotification.cancel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var notify = function () {
|
|
||||||
if (Visible.isSupported() && !Visible.currently()) {
|
|
||||||
unnotify();
|
|
||||||
module.tabNotification = Notify.tab(1000, 10);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var onRemote = config.onRemote = Catch(function () {
|
var onRemote = config.onRemote = Catch(function () {
|
||||||
if (initializing) { return; }
|
if (initializing) { return; }
|
||||||
var userDoc = module.realtime.getUserDoc();
|
var userDoc = module.realtime.getUserDoc();
|
||||||
|
|
||||||
updateMetadata(userDoc);
|
Metadata.update(userDoc);
|
||||||
var json = JSON.parse(userDoc);
|
var json = JSON.parse(userDoc);
|
||||||
var remoteDoc = json.content;
|
var remoteDoc = json.content;
|
||||||
|
|
||||||
@@ -479,7 +373,7 @@ window.canvas = canvas;
|
|||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
|
|
||||||
var content = canvas.toDatalessJSON();
|
var content = canvas.toDatalessJSON();
|
||||||
if (content !== remoteDoc) { notify(); }
|
if (content !== remoteDoc) { Cryptpad.notify(); }
|
||||||
if (readOnly) { setEditable(false); }
|
if (readOnly) { setEditable(false); }
|
||||||
});
|
});
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
@@ -488,13 +382,13 @@ window.canvas = canvas;
|
|||||||
var obj = {
|
var obj = {
|
||||||
content: textValue,
|
content: textValue,
|
||||||
metadata: {
|
metadata: {
|
||||||
users: userData,
|
users: UserList.userData,
|
||||||
palette: palette,
|
palette: palette,
|
||||||
defaultTitle: defaultName
|
defaultTitle: Title.defaultTitle
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (!initializing) {
|
if (!initializing) {
|
||||||
obj.metadata.title = document.title;
|
obj.metadata.title = Title.title;
|
||||||
}
|
}
|
||||||
// stringify the json and send it into chainpad
|
// stringify the json and send it into chainpad
|
||||||
return JSONSortify(obj);
|
return JSONSortify(obj);
|
||||||
@@ -510,29 +404,7 @@ window.canvas = canvas;
|
|||||||
module.patchText(content);
|
module.patchText(content);
|
||||||
});
|
});
|
||||||
|
|
||||||
var setName = module.setName = function (newName) {
|
config.onReady = function (info) {
|
||||||
if (typeof(newName) !== 'string') { return; }
|
|
||||||
var myUserNameTemp = newName.trim();
|
|
||||||
if(newName.trim().length > 32) {
|
|
||||||
myUserNameTemp = myUserNameTemp.substr(0, 32);
|
|
||||||
}
|
|
||||||
myUserName = myUserNameTemp;
|
|
||||||
myData[myID] = {
|
|
||||||
name: myUserName,
|
|
||||||
uid: Cryptpad.getUid(),
|
|
||||||
};
|
|
||||||
addToUserData(myData);
|
|
||||||
Cryptpad.setAttribute('username', myUserName, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.log("Couldn't set username");
|
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onLocal();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var onReady = config.onReady = function (info) {
|
|
||||||
var realtime = module.realtime = info.realtime;
|
var realtime = module.realtime = info.realtime;
|
||||||
module.patchText = TextPatcher.create({
|
module.patchText = TextPatcher.create({
|
||||||
realtime: realtime
|
realtime: realtime
|
||||||
@@ -547,45 +419,20 @@ window.canvas = canvas;
|
|||||||
initializing = false;
|
initializing = false;
|
||||||
onRemote();
|
onRemote();
|
||||||
|
|
||||||
if (Visible.isSupported()) {
|
|
||||||
Visible.onChange(function (yes) { if (yes) { unnotify(); } });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: restore palette from metadata.palette */
|
/* TODO: restore palette from metadata.palette */
|
||||||
Cryptpad.getLastName(function (err, lastName) {
|
|
||||||
if (err) {
|
if (readOnly) { return; }
|
||||||
console.log("Could not get previous name");
|
UserList.getLastName(toolbar.$userNameButton, isNew);
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Update the toolbar list:
|
|
||||||
// Add the current user in the metadata if he has edit rights
|
|
||||||
if (readOnly) { return; }
|
|
||||||
if (typeof(lastName) === 'string') {
|
|
||||||
setName(lastName);
|
|
||||||
} else {
|
|
||||||
myData[myID] = {
|
|
||||||
name: "",
|
|
||||||
uid: Cryptpad.getUid(),
|
|
||||||
};
|
|
||||||
addToUserData(myData);
|
|
||||||
onLocal();
|
|
||||||
module.$userNameButton.click();
|
|
||||||
}
|
|
||||||
if (isNew) {
|
|
||||||
Cryptpad.selectTemplate('whiteboard', info.realtime, Cryptget);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var onAbort = config.onAbort = function (info) {
|
config.onAbort = function () {
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
toolbar.failed();
|
toolbar.failed();
|
||||||
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO onConnectionStateChange
|
// TODO onConnectionStateChange
|
||||||
var onConnectionChange = config.onConnectionChange = function (info) {
|
config.onConnectionChange = function (info) {
|
||||||
setEditable(info.state);
|
setEditable(info.state);
|
||||||
toolbar.failed();
|
toolbar.failed();
|
||||||
if (info.state) {
|
if (info.state) {
|
||||||
@@ -597,7 +444,7 @@ window.canvas = canvas;
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var rt = Realtime.start(config);
|
module.rt = Realtime.start(config);
|
||||||
|
|
||||||
canvas.on('mouse:up', onLocal);
|
canvas.on('mouse:up', onLocal);
|
||||||
|
|
||||||
@@ -611,7 +458,7 @@ window.canvas = canvas;
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Cryptpad.ready(function (err, env) {
|
Cryptpad.ready(function () {
|
||||||
andThen();
|
andThen();
|
||||||
Cryptpad.reportAppUsage();
|
Cryptpad.reportAppUsage();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user