Fix merge error

pull/1/head
Caleb James DeLisle 8 years ago
commit f8ac3a6ad3

@ -14,7 +14,12 @@ let historyKeeperKeys = {};
const now = function () { return (new Date()).getTime(); }; const now = function () { return (new Date()).getTime(); };
const socketSendable = function (socket) {
return socket && socket.readyState === 1;
};
const sendMsg = function (ctx, user, msg) { const sendMsg = function (ctx, user, msg) {
if (!socketSendable(user.socket)) { return; }
try { try {
if (ctx.config.logToStdout) { console.log('<' + JSON.stringify(msg)); } if (ctx.config.logToStdout) { console.log('<' + JSON.stringify(msg)); }
user.socket.send(JSON.stringify(msg)); user.socket.send(JSON.stringify(msg));
@ -272,7 +277,7 @@ let run = module.exports.run = function (storage, socketServer, config) {
}); });
}, 5000); }, 5000);
socketServer.on('connection', function(socket) { socketServer.on('connection', function(socket) {
if(socket.upgradeReq.url !== '/cryptpad_websocket') { return; } if(socket.upgradeReq.url !== (config.websocketPath || '/cryptpad_websocket')) { return; }
let conn = socket.upgradeReq.connection; let conn = socket.upgradeReq.connection;
let user = { let user = {
addr: conn.remoteAddress + '|' + conn.remotePort, addr: conn.remoteAddress + '|' + conn.remotePort,

@ -9,8 +9,22 @@ module.exports = {
// the port on which your httpd will listen // the port on which your httpd will listen
httpPort: 3000, httpPort: 3000,
// the port used for websockets
websocketPort: 3000, /* your server's websocket url is configurable
(default: '/cryptpad_websocket')
websocketPath can be relative, of the form '/path/to/websocket'
or absolute, specifying a particular URL
'wss://cryptpad.fr:3000/cryptpad_websocket'
*/
websocketPath: '/cryptpad_websocket',
/* it is assumed that your websocket will bind to the same port as http
you can override this behaviour by supplying a number via websocketPort
*/
//websocketPort: 3000,
/* Cryptpad can log activity to stdout /* Cryptpad can log activity to stdout
* This may be useful for debugging * This may be useful for debugging

@ -4,7 +4,7 @@ define(function() {
/* Select the buttons displayed on the main page to create new collaborative sessions /* Select the buttons displayed on the main page to create new collaborative sessions
* Existing types : pad, code, poll, slide * Existing types : pad, code, poll, slide
*/ */
config.availablePadTypes = ['pad', 'code', 'poll']; config.availablePadTypes = ['pad', 'code', 'poll', 'slide'];
return config; return config;
}); });

@ -57,8 +57,12 @@
<center> <center>
<noscript> <noscript>
<p data-localization="main_oops"> <p>
<!-- JS required --> <strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.
</p>
<hr>
<p>
<strong>OUPS</strong> Afin de pouvoir réaliser le cryptage depuis votre navigateur, Javascript est <strong>vraiment</strong> requis.
</p> </p>
</noscript> </noscript>
<h5 id="tryit" data-localization="tryIt"></h5> <h5 id="tryit" data-localization="tryIt"></h5>
@ -87,8 +91,6 @@
</div> </div>
<div id="bottom-bar"></div>
</body> </body>
</html> </html>

@ -154,6 +154,9 @@ tr {
margin-bottom: 12px; margin-bottom: 12px;
white-space: nowrap; white-space: nowrap;
} }
.alertify button {
margin: 3px 0px;
}
/* Tables */ /* Tables */
table { table {
border-collapse: collapse; border-collapse: collapse;

@ -12,9 +12,6 @@ define([
Cryptpad: Cryptpad, Cryptpad: Cryptpad,
}; };
DecorateToolbar.main($('#bottom-bar'));
Cryptpad.styleAlerts();
var padTypes = { var padTypes = {
'/pad/': Messages.type.pad, '/pad/': Messages.type.pad,
'/code/': Messages.type.code, '/code/': Messages.type.code,
@ -22,9 +19,9 @@ define([
'/slide/': Messages.type.slide, '/slide/': Messages.type.slide,
}; };
var $table = $('table.scroll'); var $table;
var $tbody = $table.find('tbody'); var $tbody;
var $tryit = $('#tryit'); var $tryit;
var now = new Date(); var now = new Date();
var hasRecent = false; var hasRecent = false;
@ -138,11 +135,18 @@ define([
}); });
}; };
displayCreateButtons();
Cryptpad.ready(function () { Cryptpad.ready(function () {
console.log("ready"); console.log("ready");
refreshTable();
$table = $('table.scroll');
$tbody = $table.find('tbody');
$tryit = $('#tryit');
DecorateToolbar.main($('#bottom-bar'));
Cryptpad.styleAlerts();
displayCreateButtons();
refreshTable();
if (Cryptpad.store && Cryptpad.store.change) { if (Cryptpad.store && Cryptpad.store.change) {
Cryptpad.store.change(function (data) { Cryptpad.store.change(function (data) {
if (data.key === 'CryptPad_RECENTPADS') { if (data.key === 'CryptPad_RECENTPADS') {

@ -74,8 +74,6 @@
</div> </div>
<div id="bottom-bar"></div>
</body> </body>
</html> </html>

@ -187,6 +187,9 @@ p, pre, td, a, table, tr {
margin-bottom: 2 * 6px; margin-bottom: 2 * 6px;
white-space: nowrap; white-space: nowrap;
} }
.alertify button {
margin: 3px 0px;
}
/* Tables */ /* Tables */
table { table {

@ -17,8 +17,12 @@
<center> <center>
<noscript> <noscript>
<p data-localization="main_oops"> <p>
<!-- JS required --> <strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.
</p>
<hr>
<p>
<strong>OUPS</strong> Afin de pouvoir réaliser le cryptage depuis votre navigateur, Javascript est <strong>vraiment</strong> requis.
</p> </p>
</noscript> </noscript>
<h5 id="tryit" data-localization="tryIt"></h5> <h5 id="tryit" data-localization="tryIt"></h5>

@ -20,8 +20,6 @@
<div id="main"> <div id="main">
{{main}} {{main}}
</div> </div>
<div id="bottom-bar"></div>
</body> </body>
</html> </html>

@ -51,8 +51,6 @@
</div> </div>
<div id="bottom-bar"></div>
</body> </body>
</html> </html>

@ -34,7 +34,7 @@ define(function () {
out.anonymous = "Vous êtes actuellement anonyme"; out.anonymous = "Vous êtes actuellement anonyme";
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.importButton = 'IMPORTER'; out.importButton = 'IMPORTER';
@ -74,7 +74,10 @@ define(function () {
out.getViewButton = 'LECTURE SEULE'; out.getViewButton = 'LECTURE SEULE';
out.getViewButtonTitle = "Obtenir l'adresse d'accès à ce document en lecture seule"; out.getViewButtonTitle = "Obtenir l'adresse d'accès à ce document en lecture seule";
out.readonlyUrl = 'URL de lecture seule'; out.readonlyUrl = 'Document en lecture seule';
out.copyReadOnly = "Copier l'URL dans le presse-papiers";
out.openReadOnly = "Ouvrir dans un nouvel onglet";
out.disconnectAlert = 'Perte de la connexion au réseau !'; out.disconnectAlert = 'Perte de la connexion au réseau !';
@ -142,7 +145,6 @@ define(function () {
out.main_about = 'À propos'; out.main_about = 'À propos';
out.main_about_p1 = 'Vous pouvez en apprendre davantage sur notre <a href="/privacy.html" title="">politique de confidentialité</a> et nos <a href="/terms.html">conditions d\'utilisation</a>.'; out.main_about_p1 = 'Vous pouvez en apprendre davantage sur notre <a href="/privacy.html" title="">politique de confidentialité</a> et nos <a href="/terms.html">conditions d\'utilisation</a>.';
out.main_about_p2 = 'Si vous avez des questions ou commentaires, vous pouvez <a href="https://twitter.com/cryptpad">nous tweeter</a>, ouvrir une issue sur <a href="https://github.com/xwiki-labs/cryptpad/issues/" title="our issue tracker">Github</a>, venir dire bonjour sur IRC (<a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" title="freenode webchat">irc.freenode.net</a>), ou <a href="mailto:research@xwiki.com">nous envoyer un email</a>.'; out.main_about_p2 = 'Si vous avez des questions ou commentaires, vous pouvez <a href="https://twitter.com/cryptpad">nous tweeter</a>, ouvrir une issue sur <a href="https://github.com/xwiki-labs/cryptpad/issues/" title="our issue tracker">Github</a>, venir dire bonjour sur IRC (<a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" title="freenode webchat">irc.freenode.net</a>), ou <a href="mailto:research@xwiki.com">nous envoyer un email</a>.';
out.main_oops = '<strong>OUPS</strong> Afin de pouvoir réaliser le cryptage depuis votre navigateur, Javascript est <strong>vraiment</strong> requis.';
out.table_type = 'Type'; out.table_type = 'Type';
out.table_link = 'Lien'; out.table_link = 'Lien';

@ -57,7 +57,7 @@ define(function () {
out.renameConflict = 'Another pad already has that title'; out.renameConflict = 'Another pad already has that title';
out.forgetButton = 'FORGET'; out.forgetButton = 'FORGET';
out.forgetButtonTitle = 'remove this document from your home page listings'; out.forgetButtonTitle = 'Remove this document from your home page listings';
out.forgetPrompt = 'Clicking OK will remove the URL for this pad from localStorage, are you sure?'; out.forgetPrompt = 'Clicking OK will remove the URL for this pad from localStorage, are you sure?';
out.shareButton = 'SHARE'; out.shareButton = 'SHARE';
@ -75,7 +75,9 @@ define(function () {
out.getViewButton = 'READ-ONLY URL'; out.getViewButton = 'READ-ONLY URL';
out.getViewButtonTitle = 'Get the read-only URL for this document'; out.getViewButtonTitle = 'Get the read-only URL for this document';
out.readonlyUrl = 'Read only URL'; out.readonlyUrl = 'Read only document';
out.copyReadOnly = "Copy URL to clipboard";
out.openReadOnly = "Open in a new tab";
out.disconnectAlert = 'Network connection lost!'; out.disconnectAlert = 'Network connection lost!';
@ -142,8 +144,8 @@ define(function () {
out.main_howitworks_p1 = 'CryptPad uses a variant of the <a href="https://en.wikipedia.org/wiki/Operational_transformation">Operational transformation</a> algorithm which is able to find distributed consensus using a Nakamoto Blockchain, a construct popularized by <a href="https://en.wikipedia.org/wiki/Bitcoin">Bitcoin</a>. This way the algorithm can avoid the need for a central server to resolve Operational Transform Edit Conflicts and without the need for resolving conflicts, the server can be kept unaware of the content which is being edited on the pad.'; out.main_howitworks_p1 = 'CryptPad uses a variant of the <a href="https://en.wikipedia.org/wiki/Operational_transformation">Operational transformation</a> algorithm which is able to find distributed consensus using a Nakamoto Blockchain, a construct popularized by <a href="https://en.wikipedia.org/wiki/Bitcoin">Bitcoin</a>. This way the algorithm can avoid the need for a central server to resolve Operational Transform Edit Conflicts and without the need for resolving conflicts, the server can be kept unaware of the content which is being edited on the pad.';
out.main_about = 'About'; out.main_about = 'About';
out.main_about_p1 = 'You can read more about our <a href="/privacy.html" title="">privacy policy</a> and <a href="/terms.html">terms of service</a>.'; out.main_about_p1 = 'You can read more about our <a href="/privacy.html" title="">privacy policy</a> and <a href="/terms.html">terms of service</a>.';
out.main_about_p2 = 'If you have any questions or comments, you can <a href="https://twitter.com/cryptpad">tweet us</a>, open an issue <a href="https://github.com/xwiki-labs/cryptpad/issues/" title="our issue tracker">on github</a>, come say hi on irc (<a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" title="freenode webchat">irc.freenode.net</a>), or <a href="mailto:research@xwiki.com">send us an email</a>.'; out.main_about_p2 = 'If you have any questions or comments, you can <a href="https://twitter.com/cryptpad">tweet us</a>, open an issue <a href="https://github.com/xwiki-labs/cryptpad/issues/" title="our issue tracker">on github</a>, come say hi on irc (<a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" title="freenode webchat">irc.freenode.net</a>), or <a href="mailto:research@xwiki.com">send us an email</a>.';
out.main_oops = '<strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.';
out.table_type = 'Type'; out.table_type = 'Type';
out.table_link = 'Link'; out.table_link = 'Link';

@ -83,10 +83,9 @@ app.get('/api/config', function(req, res){
var host = req.headers.host.replace(/\:[0-9]+/, ''); var host = req.headers.host.replace(/\:[0-9]+/, '');
res.setHeader('Content-Type', 'text/javascript'); res.setHeader('Content-Type', 'text/javascript');
res.send('define(' + JSON.stringify({ res.send('define(' + JSON.stringify({
websocketPath: config.websocketPath,
websocketURL:'ws' + ((httpsOpts) ? 's' : '') + '://' + host + ':' + websocketURL:'ws' + ((httpsOpts) ? 's' : '') + '://' + host + ':' +
websocketPort + '/cryptpad_websocket', websocketPort + '/cryptpad_websocket',
webrtcURL:'ws' + ((httpsOpts) ? 's' : '') + '://' + host + ':' +
websocketPort + '/cryptpad_webrtc',
}) + ');'); }) + ');');
}); });

@ -1,6 +1,5 @@
require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
define([ define([
'/api/config?cb=' + Math.random().toString(16).substring(2),
'/customize/messages.js?app=code', '/customize/messages.js?app=code',
'/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',
@ -16,7 +15,7 @@ define([
'/bower_components/file-saver/FileSaver.min.js', '/bower_components/file-saver/FileSaver.min.js',
'/bower_components/jquery/dist/jquery.min.js', '/bower_components/jquery/dist/jquery.min.js',
'/customize/pad.js' '/customize/pad.js'
], function (Config, /*RTCode,*/ Messages, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Modes, Themes, Visible, Notify) { ], function (Messages, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Modes, Themes, Visible, Notify) {
var $ = window.jQuery; var $ = window.jQuery;
var saveAs = window.saveAs; var saveAs = window.saveAs;
@ -134,7 +133,7 @@ define([
var config = { var config = {
//initialState: Messages.codeInitialState, //initialState: Messages.codeInitialState,
initialState: '{}', initialState: '{}',
websocketURL: Config.websocketURL, websocketURL: Cryptpad.getWebsocketURL(),
channel: secret.channel, channel: secret.channel,
// our public key // our public key
validateKey: secret.keys.validateKey || undefined, validateKey: secret.keys.validateKey || undefined,
@ -146,6 +145,11 @@ define([
var canonicalize = function (t) { return t.replace(/\r\n/g, '\n'); }; var canonicalize = function (t) { return t.replace(/\r\n/g, '\n'); };
var isDefaultTitle = function () {
var parsed = Cryptpad.parsePadUrl(window.location.href);
return Cryptpad.isDefaultName(parsed, document.title);
};
var initializing = true; var initializing = true;
var onLocal = config.onLocal = function () { var onLocal = config.onLocal = function () {
@ -158,9 +162,11 @@ define([
// append the userlist to the hyperjson structure // append the userlist to the hyperjson structure
obj.metadata = { obj.metadata = {
users: userList, users: userList
title: document.title
}; };
if (!isDefaultTitle()) {
obj.metadata.title = document.title;
}
// set mode too... // set mode too...
obj.highlightMode = module.highlightMode; obj.highlightMode = module.highlightMode;
@ -243,7 +249,7 @@ define([
var parsed = Cryptpad.parsePadUrl(window.location.href); var parsed = Cryptpad.parsePadUrl(window.location.href);
var name = Cryptpad.getDefaultName(parsed, []); var name = Cryptpad.getDefaultName(parsed, []);
if (document.title.slice(0, name.length) === name) { if (Cryptpad.isDefaultName(parsed, document.title)) {
return getHeadingText() || document.title; return getHeadingText() || document.title;
} else { } else {
return document.title || getHeadingText() || name; return document.title || getHeadingText() || name;
@ -265,6 +271,32 @@ define([
saveAs(blob, filename); 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 onInit = config.onInit = function (info) { var onInit = config.onInit = function (info) {
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox'); var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
@ -285,113 +317,45 @@ define([
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
} }
/* add a "change username" button */
getLastName(function (err, lastName) { getLastName(function (err, lastName) {
var $username = Cryptpad.createButton('username', true) var usernameCb = function (newName) {
.click(function() { setName (newName);
Cryptpad.prompt(Messages.changeNamePrompt, lastName, function (newName) { };
setName(newName); var $username = Cryptpad.createButton('username', true, {lastName: lastName}, usernameCb);
});
});
$rightside.append($username); $rightside.append($username);
}); });
/* add an export button */ /* add an export button */
var $export = Cryptpad.createButton('export', true).click(exportText); var $export = Cryptpad.createButton('export', true, {}, 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) var $import = Cryptpad.createButton('import', true, {}, importText);
.click(Cryptpad.importContent('text/plain', function (content, file) {
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();
}));
$rightside.append($import); $rightside.append($import);
}
/* add a rename button */ /* add a rename button */
var $setTitle = Cryptpad.createButton('rename', true) var renameCb = function (err, title) {
.click(function () { if (err) { return; }
var suggestion = suggestName();
Cryptpad.prompt(Messages.renamePrompt,
suggestion, function (title, ev) {
if (title === null) { return; }
Cryptpad.causesNamingConflict(title, function (err, conflicts) {
if (err) {
console.log("Unable to determine if name caused a conflict");
console.error(err);
return;
}
if (conflicts) {
Cryptpad.alert(Messages.renameConflict);
return;
}
Cryptpad.setPadTitle(title, function (err, data) {
if (err) {
console.log("unable to set pad title");
console.log(err);
return;
}
document.title = title; document.title = title;
onLocal(); onLocal();
}); };
}); var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb);
});
});
$rightside.append($setTitle); $rightside.append($setTitle);
}
/* add a forget button */ /* add a forget button */
var $forgetPad = Cryptpad.createButton('forget', true) var forgetCb = function (err, title) {
.click(function () { if (err) { return; }
var href = window.location.href; document.title = title;
Cryptpad.confirm(Messages.forgetPrompt, function (yes) { };
if (!yes) { return; } var $forgetPad = Cryptpad.createButton('forget', true, {}, forgetCb);
Cryptpad.forgetPad(href, function (err, data) {
if (err) {
console.log("unable to forget pad");
console.error(err);
return;
}
var parsed = Cryptpad.parsePadUrl(href);
document.title = Cryptpad.getDefaultName(parsed, []);
});
});
});
$rightside.append($forgetPad); $rightside.append($forgetPad);
if (!readOnly && viewHash) { if (!readOnly && viewHash) {
/* add a 'links' button */ /* add a 'links' button */
var $links = Cryptpad.createButton('readonly', true) var $links = Cryptpad.createButton('readonly', true, {viewHash: viewHash});
.click(function () {
var baseUrl = window.location.origin + window.location.pathname + '#';
var content = '<b>' + Messages.readonlyUrl + '</b><br><a>' + baseUrl + viewHash + '</a><br>';
Cryptpad.alert(content);
});
$rightside.append($links); $rightside.append($links);
} }
@ -470,7 +434,7 @@ define([
return; return;
} }
document.title = title || info.channel.slice(0, 8); document.title = title || info.channel.slice(0, 8);
Cryptpad.rememberPad(title, function (err, data) { Cryptpad.setPadTitle(title, function (err, data) {
if (err) { if (err) {
console.log("Unable to set pad title"); console.log("Unable to set pad title");
console.error(err); console.error(err);
@ -657,11 +621,13 @@ define([
var hjson2 = { var hjson2 = {
content: localDoc, content: localDoc,
metadata: { metadata: {
users: userList, users: userList
title: document.title
}, },
highlightMode: highlightMode, highlightMode: highlightMode,
}; };
if (!isDefaultTitle()) {
hjson2.metadata.title = document.title;
}
var shjson2 = stringify(hjson2); var shjson2 = stringify(hjson2);
if (shjson2 !== shjson) { if (shjson2 !== shjson) {
console.error("shjson2 !== shjson"); console.error("shjson2 !== shjson");

@ -1,14 +1,16 @@
define([ define([
'/api/config?cb=' + Math.random().toString(16).slice(2),
'/customize/messages.js', '/customize/messages.js',
'/customize/store.js', '/customize/store.js',
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/bower_components/alertifyjs/dist/js/alertify.js', '/bower_components/alertifyjs/dist/js/alertify.js',
'/bower_components/spin.js/spin.min.js', '/bower_components/spin.js/spin.min.js',
'/common/clipboard.js',
'/customize/user.js', '/customize/user.js',
'/bower_components/jquery/dist/jquery.min.js', '/bower_components/jquery/dist/jquery.min.js',
], function (Messages, Store, Crypto, Alertify, Spinner, User) { ], function (Config, Messages, Store, Crypto, Alertify, Spinner, Clipboard, User) {
/* 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
about pads to your local storage for future use and improved usability. about pads to your local storage for future use and improved usability.
@ -36,6 +38,18 @@ define([
throw new Error("Store is not ready!"); throw new Error("Store is not ready!");
}; };
var getWebsocketURL = common.getWebsocketURL = function () {
if (!Config.websocketPath) { return Config.websocketURL; }
var path = Config.websocketPath;
if (/^ws{1,2}:\/\//.test(path)) { return path; }
var protocol = window.location.protocol.replace(/http/, 'ws');
var host = window.location.host;
var url = protocol + '//' + host + path;
return url;
};
/* /*
* cb(err, proxy); * cb(err, proxy);
*/ */
@ -137,6 +151,26 @@ define([
}; };
var parseHash = common.parseHash = function (hash) {
var parsed = {};
if (hash.slice(0,1) !== '/' && hash.length >= 56) {
// Old hash
parsed.channel = hash.slice(0, 32);
parsed.key = hash.slice(32);
parsed.version = 0;
return parsed;
}
var hashArr = hash.split('/');
if (hashArr[1] && hashArr[1] === '1') {
parsed.version = 1;
parsed.mode = hashArr[2];
parsed.channel = hashArr[3];
parsed.key = hashArr[4];
parsed.present = hashArr[5] && hashArr[5] === 'present';
return parsed;
}
return;
};
var getEditHashFromKeys = common.getEditHashFromKeys = function (chanKey, keys) { var getEditHashFromKeys = common.getEditHashFromKeys = function (chanKey, keys) {
if (typeof keys === 'string') { if (typeof keys === 'string') {
return chanKey + keys; return chanKey + keys;
@ -295,6 +329,10 @@ define([
while (!isNameAvailable(name + ' - ' + untitledIndex, parsed, recentPads)) { untitledIndex++; } while (!isNameAvailable(name + ' - ' + untitledIndex, parsed, recentPads)) { untitledIndex++; }
return name + ' - ' + untitledIndex; return name + ' - ' + untitledIndex;
}; };
var isDefaultName = common.isDefaultName = function (parsed, title) {
var name = getDefaultName(parsed, []);
return title.slice(0, name.length) === name;
};
var makePad = function (href, title) { var makePad = function (href, title) {
var now = ''+new Date(); var now = ''+new Date();
@ -397,51 +435,10 @@ define([
}, legacy); }, legacy);
}; };
// STORAGE
var rememberPad = common.rememberPad = window.rememberPad = function (title, cb) {
// bail out early
if (!/#/.test(window.location.hash)) { return; }
getRecentPads(function (err, pads) {
if (err) {
cb(err);
return;
}
var now = ''+new Date();
var href = window.location.href;
var parsed = parsePadUrl(window.location.href);
var isUpdate = false;
var out = pads.map(function (pad) {
var p = parsePadUrl(pad.href);
if (p.hash === parsed.hash && p.type === parsed.type) {
isUpdate = true;
// bump the atime
pad.atime = now;
pad.title = title;
pad.href = href;
}
return pad;
});
if (!isUpdate) {
// href, ctime, atime, title
out.push(makePad(href, title));
}
setRecentPads(out, function (err, data) {
cb(err, data);
});
});
};
// STORAGE // STORAGE
var setPadTitle = common.setPadTitle = function (name, cb) { var setPadTitle = common.setPadTitle = function (name, cb) {
var href = window.location.href; var href = window.location.href;
var parsed = parsePadUrl(href); var parsed = parsePadUrl(href);
getRecentPads(function (err, recent) { getRecentPads(function (err, recent) {
if (err) { if (err) {
cb(err); cb(err);
@ -449,10 +446,29 @@ define([
} }
var contains; var contains;
var renamed = recent.map(function (pad) { var renamed = recent.map(function (pad) {
var p = parsePadUrl(pad.href); var p = parsePadUrl(pad.href);
if (p.hash === parsed.hash && p.type === parsed.type) {
if (p.type !== parsed.type) { return pad; }
var shouldUpdate = p.hash === parsed.hash;
// Version 1 : we have up to 4 differents hash for 1 pad, keep the strongest :
// Edit > Edit (present) > View > View (present)
var pHash = parseHash(p.hash);
var parsedHash = parseHash(parsed.hash);
if (!shouldUpdate && pHash.version === 1 && parsedHash.version === 1 && pHash.channel === parsedHash.channel) {
if (pHash.mode === 'view' && parsedHash.mode === 'edit') { shouldUpdate = true; }
else if (pHash.mode === parsedHash.mode && pHash.present) { shouldUpdate = true; }
else {
// Editing a "weaker" version of a stored hash : update the date and do not push the current hash
pad.atime = new Date().toISOString();
contains = true;
return pad;
}
}
if (shouldUpdate) {
contains = true; contains = true;
// update the atime // update the atime
pad.atime = new Date().toISOString(); pad.atime = new Date().toISOString();
@ -541,7 +557,23 @@ define([
Store.ready(function (err, store) { Store.ready(function (err, store) {
common.store = env.store = store; common.store = env.store = store;
$(function() {
// 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"
Alertify.reset();
if($('#pad-iframe').length) {
var $iframe = $('#pad-iframe');
var iframe = $iframe[0];
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
if (iframeDoc.readyState === 'complete') {
cb();
return;
}
$iframe.load(cb);
return;
}
cb(); cb();
});
return; return;
/* /*
authorize(function (err, proxy) { authorize(function (err, proxy) {
@ -610,7 +642,7 @@ define([
/* /*
* Buttons * Buttons
*/ */
var createButton = common.createButton = function (type, rightside) { var createButton = common.createButton = function (type, rightside, data, callback) {
var button; var button;
var size = "17px"; var size = "17px";
switch (type) { switch (type) {
@ -620,6 +652,9 @@ define([
'class': "fa fa-download", 'class': "fa fa-download",
style: 'font:'+size+' FontAwesome' style: 'font:'+size+' FontAwesome'
}); });
if (callback) {
button.click(callback);
}
break; break;
case 'import': case 'import':
button = $('<button>', { button = $('<button>', {
@ -627,6 +662,11 @@ define([
'class': "fa fa-upload", 'class': "fa fa-upload",
style: 'font:'+size+' FontAwesome' style: 'font:'+size+' FontAwesome'
}); });
if (callback) {
button.click(common.importContent('text/plain', function (content, file) {
callback(content, file);
}));
}
break; break;
case 'rename': case 'rename':
button = $('<button>', { button = $('<button>', {
@ -635,6 +675,40 @@ define([
'class': "fa fa-bookmark cryptpad-rename", 'class': "fa fa-bookmark cryptpad-rename",
style: 'font:'+size+' FontAwesome' style: 'font:'+size+' FontAwesome'
}); });
if (data && data.suggestName && callback) {
var suggestName = data.suggestName;
button.click(function() {
var suggestion = suggestName();
common.prompt(Messages.renamePrompt,
suggestion, function (title, ev) {
if (title === null) { return; }
common.causesNamingConflict(title, function (err, conflicts) {
if (err) {
console.log("Unable to determine if name caused a conflict");
console.error(err);
callback(err, title);
return;
}
if (conflicts) {
common.alert(Messages.renameConflict);
return;
}
common.setPadTitle(title, function (err, data) {
if (err) {
console.log("unable to set pad title");
console.log(err);
return;
}
callback(null, title);
});
});
});
});
}
break; break;
case 'forget': case 'forget':
button = $('<button>', { button = $('<button>', {
@ -643,6 +717,25 @@ define([
'class': "fa fa-trash cryptpad-forget", 'class': "fa fa-trash cryptpad-forget",
style: 'font:'+size+' FontAwesome' style: 'font:'+size+' FontAwesome'
}); });
if (callback) {
button.click(function() {
var href = window.location.href;
common.confirm(Messages.forgetPrompt, function (yes) {
if (!yes) { return; }
common.forgetPad(href, function (err, data) {
if (err) {
console.log("unable to forget pad");
console.error(err);
callback(err, null);
return;
}
var parsed = common.parsePadUrl(href);
callback(null, common.getDefaultName(parsed, []));
});
});
});
}
break; break;
case 'username': case 'username':
button = $('<button>', { button = $('<button>', {
@ -650,6 +743,14 @@ define([
'class': "fa fa-user", 'class': "fa fa-user",
style: 'font:'+size+' FontAwesome' style: 'font:'+size+' FontAwesome'
}); });
if (data && typeof data.lastName !== "undefined" && callback) {
var lastName = data.lastName;
button.click(function() {
common.prompt(Messages.changeNamePrompt, lastName, function (newName) {
callback(newName);
});
});
}
break; break;
case 'readonly': case 'readonly':
button = $('<button>', { button = $('<button>', {
@ -657,6 +758,37 @@ define([
'class': "fa fa-eye", 'class': "fa fa-eye",
style: 'font:'+size+' FontAwesome' style: 'font:'+size+' FontAwesome'
}); });
if (data && data.viewHash) {
var viewHash = data.viewHash;
button.click(function() {
var baseUrl = window.location.origin + window.location.pathname + '#';
var url = baseUrl + viewHash;
var $content = $('<div>').text(Messages.readonlyUrl);
var $copy = $('<button>', {
id: "cryptpad-readonly-copy",
'class': "button action"
}).text(Messages.copyReadOnly);
var $open = $('<button>', {
id: "cryptpad-readonly-open",
'class': "button action"
}).text(Messages.openReadOnly);
$content.append('<br>').append($copy).append($open);
common.alert($content.html());
$("#cryptpad-readonly-copy").click(function() {
var success = Clipboard.copy(url);
if (success) {
common.log(Messages.shareSuccess);
common.findOKButton().click();
return;
}
});
$("#cryptpad-readonly-open").click(function() {
window.open(url);
});
if (callback) { callback(); }
});
}
break; break;
case 'present': case 'present':
button = $('<button>', { button = $('<button>', {
@ -679,7 +811,7 @@ define([
}); });
} }
if (rightside) { if (rightside) {
button.addClass('rightside-button') button.addClass('rightside-button');
} }
return button; return button;
}; };

@ -180,9 +180,10 @@ define([
var lagLight = $('<div>', { var lagLight = $('<div>', {
'class': 'lag' 'class': 'lag'
}); });
var title;
if(lag) { if(lag) {
firstConnection = false; firstConnection = false;
var title = Messages.lag + ' : ' + lag + ' ms\n'; title = Messages.lag + ' : ' + lag + ' ms\n';
if (lag.waiting || lag > 1000) { if (lag.waiting || lag > 1000) {
lagLight.addClass('lag-orange'); lagLight.addClass('lag-orange');
title += Messages.orangeLight; title += Messages.orangeLight;

@ -1,6 +1,5 @@
require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
define([ define([
'/api/config?cb=' + Math.random().toString(16).substring(2),
'/customize/messages.js?app=pad', '/customize/messages.js?app=pad',
'/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',
@ -18,7 +17,7 @@ define([
'/bower_components/diff-dom/diffDOM.js', '/bower_components/diff-dom/diffDOM.js',
'/bower_components/jquery/dist/jquery.min.js', '/bower_components/jquery/dist/jquery.min.js',
'/customize/pad.js' '/customize/pad.js'
], function (Config, Messages, Crypto, realtimeInput, Hyperjson, ], function (Messages, Crypto, realtimeInput, Hyperjson,
Toolbar, Cursor, JsonOT, TypingTest, JSONSortify, TextPatcher, Cryptpad, Toolbar, Cursor, JsonOT, TypingTest, JSONSortify, TextPatcher, Cryptpad,
Visible, Notify) { Visible, Notify) {
var $ = window.jQuery; var $ = window.jQuery;
@ -294,6 +293,33 @@ define([
}); });
}; };
var isDefaultTitle = function () {
var parsed = Cryptpad.parsePadUrl(window.location.href);
return Cryptpad.isDefaultName(parsed, document.title);
};
var getHeadingText = function () {
var text;
if (['h1', 'h2', 'h3'].some(function (t) {
var $header = $(inner).find(t + ':first-of-type');
if ($header.length && $header.text()) {
text = $header.text();
return true;
}
})) { return text; }
};
var suggestName = function () {
var parsed = Cryptpad.parsePadUrl(window.location.href);
var name = Cryptpad.getDefaultName(parsed, []);
if (Cryptpad.isDefaultName(parsed, document.title)) {
return getHeadingText() || document.title;
} else {
return document.title || getHeadingText() || name;
}
};
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!
@ -312,9 +338,11 @@ define([
hjson[3] = { hjson[3] = {
metadata: { metadata: {
users: userList, users: userList,
title: document.title
} }
}; };
if (!isDefaultTitle()) {
hjson[3].metadata.title = document.title;
}
return stringify(hjson); return stringify(hjson);
}; };
@ -323,7 +351,7 @@ define([
initialState: stringifyDOM(inner) || '{}', initialState: stringifyDOM(inner) || '{}',
// the websocket URL // the websocket URL
websocketURL: Config.websocketURL, websocketURL: Cryptpad.getWebsocketURL(),
// the channel we will communicate over // the channel we will communicate over
channel: secret.channel, channel: secret.channel,
@ -461,28 +489,6 @@ define([
return new DOMParser().parseFromString(html, 'text/html'); return new DOMParser().parseFromString(html, 'text/html');
}; };
var getHeadingText = function () {
var text;
if (['h1', 'h2', 'h3'].some(function (t) {
var $header = $(inner).find(t + ':first-of-type');
if ($header.length && $header.text()) {
text = $header.text();
return true;
}
})) { return text; }
};
var suggestName = module.suggestName = function () {
var parsed = Cryptpad.parsePadUrl(window.location.href);
var name = Cryptpad.getDefaultName(parsed, []);
if (document.title.slice(0, name.length) === name) {
return getHeadingText() || document.title;
} else {
return document.title || getHeadingText() || name;
}
};
var exportFile = function () { var exportFile = function () {
var html = getHTML(); var html = getHTML();
var suggestion = suggestName(); var suggestion = suggestName();
@ -493,6 +499,11 @@ define([
saveAs(blob, filename); saveAs(blob, filename);
}); });
}; };
var importFile = function (content, file) {
var shjson = stringify(Hyperjson.fromDOM(domFromHTML(content).body));
applyHjson(shjson);
realtimeOptions.onLocal();
};
var onInit = realtimeOptions.onInit = function (info) { var onInit = realtimeOptions.onInit = function (info) {
var $bar = $('#pad-iframe')[0].contentWindow.$('#cke_1_toolbox'); var $bar = $('#pad-iframe')[0].contentWindow.$('#cke_1_toolbox');
@ -513,80 +524,45 @@ define([
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
} }
/* add a "change username" button */
getLastName(function (err, lastName) { getLastName(function (err, lastName) {
var $username = Cryptpad.createButton('username', true) var usernameCb = function (newName) {
.click(function() { setName (newName);
Cryptpad.prompt(Messages.changeNamePrompt, lastName, function (newName) { };
setName(newName); var $username = Cryptpad.createButton('username', true, {lastName: lastName}, usernameCb);
});
});
$rightside.append($username); $rightside.append($username);
}); });
/* add an export button */ /* add an export button */
var $export = Cryptpad.createButton('export', true).click(exportFile); var $export = Cryptpad.createButton('export', true, {}, exportFile);
$rightside.append($export); $rightside.append($export);
if (!readOnly) { if (!readOnly) {
/* add an import button */ /* add an import button */
var $import = Cryptpad.createButton('import', true) var $import = Cryptpad.createButton('import', true, {}, importFile);
.click(Cryptpad.importContent('text/plain', function (content) {
var shjson = stringify(Hyperjson.fromDOM(domFromHTML(content).body));
applyHjson(shjson);
realtimeOptions.onLocal();
}));
$rightside.append($import); $rightside.append($import);
}
/* add a rename button */ /* add a rename button */
var $rename = Cryptpad.createButton('rename', true) var renameCb = function (err, title) {
.click(function () { if (err) { return; }
var suggestion = suggestName();
Cryptpad.prompt(Messages.renamePrompt, suggestion, function (title) {
if (title === null) { return; }
Cryptpad.causesNamingConflict(title, function (err, conflicts) {
if (conflicts) {
Cryptpad.alert(Messages.renameConflict);
return;
}
Cryptpad.setPadTitle(title, function (err, data) {
if (err) {
console.log("Couldn't set pad title");
console.error(err);
return;
}
document.title = title; document.title = title;
editor.fire('change'); editor.fire('change');
}); };
}); var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb);
}); $rightside.append($setTitle);
}); }
$rightside.append($rename);
/* add a forget button */ /* add a forget button */
var $forgetPad = Cryptpad.createButton('forget', true) var forgetCb = function (err, title) {
.click(function () { if (err) { return; }
var href = window.location.href; document.title = title;
Cryptpad.confirm(Messages.forgetPrompt, function (yes) { };
if (!yes) { return; } var $forgetPad = Cryptpad.createButton('forget', true, {}, forgetCb);
Cryptpad.forgetPad(href, function (err, data) {
var parsed = Cryptpad.parsePadUrl(href);
document.title = Cryptpad.getDefaultName(parsed, []);
});
});
});
$rightside.append($forgetPad); $rightside.append($forgetPad);
if (!readOnly && viewHash) { if (!readOnly && viewHash) {
/* add a 'links' button */ /* add a 'links' button */
var $links = Cryptpad.createButton('readonly', true) var $links = Cryptpad.createButton('readonly', true, {viewHash: viewHash});
.click(function () {
var baseUrl = window.location.origin + window.location.pathname + '#';
var content = '<b>' + Messages.readonlyUrl + '</b><br><a>' + baseUrl + viewHash + '</a><br>';
Cryptpad.alert(content);
});
$rightside.append($links); $rightside.append($links);
} }
@ -602,7 +578,7 @@ define([
return; return;
} }
document.title = title || info.channel.slice(0, 8); document.title = title || info.channel.slice(0, 8);
Cryptpad.rememberPad(title, function (err, data) { Cryptpad.setPadTitle(title, function (err, data) {
if (err) { if (err) {
console.log("Couldn't remember pad"); console.log("Couldn't remember pad");
console.error(err); console.error(err);

@ -456,6 +456,21 @@ define([
module.tabNotification = Notify.tab(1000, 10); module.tabNotification = Notify.tab(1000, 10);
}; };
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.setPadTitle(newTitle, function (err, data) {
if (err) {
console.log("Couldn't set pad title");
console.error(err);
document.title = oldTitle;
return;
}
});
};
// don't make changes until the interface is ready // don't make changes until the interface is ready
setEditable(false); setEditable(false);
@ -464,9 +479,12 @@ define([
module.ready = true; module.ready = true;
var proxy = module.rt.proxy; var proxy = module.rt.proxy;
var First = false; var First = false;
if (proxy.metadata && proxy.metadata.title) {
updateTitle(proxy.metadata.title);
}
// ensure that proxy.info and proxy.table exist // ensure that proxy.info and proxy.table exist
['info', 'table'].forEach(function (k) { ['info', 'table'].forEach(function (k) {
if (typeof(proxy[k]) === 'undefined') { if (typeof(proxy[k]) === 'undefined') {
@ -627,19 +645,8 @@ define([
} }
}) })
.on('change', ['metadata'], function (o, n, p) { .on('change', ['metadata'], function (o, n, p) {
var newTitle = n.title; var newTitle = proxy.metadata.title;
if (newTitle === document.title) { return; } updateTitle(newTitle);
// 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.setPadTitle(newTitle, function (err, data) {
if (err) {
console.log("Couldn't set pad title");
console.error(err);
document.title = oldTitle;
return;
}
});
}) })
.on('remove', [], function (o, p, root) { .on('remove', [], function (o, p, root) {
//console.log("remove: (%s, [%s])", o, p.join(', ')); //console.log("remove: (%s, [%s])", o, p.join(', '));
@ -704,49 +711,21 @@ define([
} }
}; };
$toolbar.append(Button({ /* add a forget button */
id: 'forget', var forgetCb = function (err, title) {
'class': 'forget button action', if (err) { return; }
title: Messages.forgetButtonTitle, document.title = title;
}).text(Messages.forgetButton).click(function () { };
var href = window.location.href; var $forgetPad = Cryptpad.createButton('forget', false, {}, forgetCb)
Cryptpad.confirm(Messages.forgetPrompt, function (yes) { .text(Messages.forgetButton)
if (!yes) { return; } .removeAttr('style')
Cryptpad.forgetPad(href, function (err, data) { .attr('class', 'action button forget');
if (err) { $toolbar.append($forgetPad);
console.log("unable to forget pad");
console.error(err); /* add a rename button */
return; var renameCb = function (err, title) {
} if (err) { return; }
var parsed = Cryptpad.parsePadUrl(href);
document.title = Cryptpad.getDefaultName(parsed, []);
});
});
}));
$toolbar.append(Button({
id: 'rename',
'class': 'rename button action',
title: Messages.renameButtonTitle,
}).text(Messages.renameButton).click(function () {
var suggestion = suggestName();
Cryptpad.prompt(Messages.renamePrompt,
suggestion, function (title, ev) {
if (title === null) { return; }
Cryptpad.causesNamingConflict(title, function (err, conflicts) {
if (conflicts) {
Cryptpad.alert(Messages.renameConflict);
return;
}
Cryptpad.setPadTitle(title, function (err, data) {
if (err) {
console.log("unable to set pad title");
console.error(err);
return;
}
document.title = title; document.title = title;
module.tabNotification.update(title);
var proxy = module.rt.proxy; var proxy = module.rt.proxy;
if (proxy.metadata) { if (proxy.metadata) {
proxy.metadata.title = title; proxy.metadata.title = title;
@ -754,10 +733,12 @@ define([
else { else {
proxy.metadata = {title: title}; proxy.metadata = {title: title};
} }
}); };
}); var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb)
}); .text(Messages.renameButton)
})); .removeAttr('style')
.attr('class', 'action button rename');
$toolbar.append($setTitle);
if (!readOnly) { if (!readOnly) {
$toolbar.append(Button({ $toolbar.append(Button({
@ -774,16 +755,10 @@ define([
if (!readOnly && module.viewHash) { if (!readOnly && module.viewHash) {
/* add a 'links' button */ /* add a 'links' button */
var $links = $('<button>', { var $links = Cryptpad.createButton('readonly', true, {viewHash: module.viewHash})
title: Messages.getViewButtonTitle
})
.text(Messages.getViewButton) .text(Messages.getViewButton)
.addClass('button action') .removeAttr('style')
.click(function () { .attr('class', 'action button readonly');
var baseUrl = window.location.origin + window.location.pathname + '#';
var content = '<b>' + Messages.readonlyUrl + '</b><br><a>' + baseUrl + module.viewHash + '</a><br>';
Cryptpad.alert(content);
});
$toolbar.append($links); $toolbar.append($links);
} }
@ -890,7 +865,7 @@ define([
}; };
var config = { var config = {
websocketURL: Config.websocketURL, websocketURL: Cryptpad.getWebsocketURL(),
channel: secret.channel, channel: secret.channel,
data: {}, data: {},
// our public key // our public key
@ -923,7 +898,7 @@ define([
Cryptpad.getPadTitle(function (err, title) { Cryptpad.getPadTitle(function (err, title) {
title = document.title = title || info.channel.slice(0, 8); title = document.title = title || info.channel.slice(0, 8);
Cryptpad.rememberPad(title, function (err, data) { Cryptpad.setPadTitle(title, function (err, data) {
if (err) { if (err) {
console.log("unable to remember pad"); console.log("unable to remember pad");
console.log(err); console.log(err);

@ -1,6 +1,5 @@
require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
define([ define([
'/api/config?cb=' + Math.random().toString(16).substring(2),
'/customize/messages.js?app=code', '/customize/messages.js?app=code',
'/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',
@ -14,11 +13,10 @@ define([
'/common/visible.js', '/common/visible.js',
'/common/notify.js', '/common/notify.js',
'/slide/slide.js', '/slide/slide.js',
'/common/clipboard.js',
'/bower_components/file-saver/FileSaver.min.js', '/bower_components/file-saver/FileSaver.min.js',
'/bower_components/jquery/dist/jquery.min.js', '/bower_components/jquery/dist/jquery.min.js',
'/customize/pad.js' '/customize/pad.js'
], function (Config, /*RTCode,*/ Messages, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Modes, Themes, Visible, Notify, Slide, Clipboard) { ], function (Messages, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Modes, Themes, Visible, Notify, Slide) {
var $ = window.jQuery; var $ = window.jQuery;
var saveAs = window.saveAs; var saveAs = window.saveAs;
@ -28,12 +26,12 @@ define([
TextPatcher: TextPatcher, TextPatcher: TextPatcher,
Slide: Slide, Slide: Slide,
}; };
var APP = window.APP;
Cryptpad.styleAlerts(); Cryptpad.styleAlerts();
module.spinner.show(); module.spinner.show();
var ifrw = module.ifrw = $('#pad-iframe')[0].contentWindow;
var stringify = function (obj) { var stringify = function (obj) {
return JSONSortify(obj); return JSONSortify(obj);
}; };
@ -47,6 +45,7 @@ define([
}; };
$(function () { $(function () {
var ifrw = module.ifrw = $('#pad-iframe')[0].contentWindow;
var toolbar; var toolbar;
var secret = Cryptpad.getSecrets(); var secret = Cryptpad.getSecrets();
@ -127,7 +126,7 @@ define([
var $modal = $pad.contents().find('#modal'); var $modal = $pad.contents().find('#modal');
var $content = $pad.contents().find('#content'); var $content = $pad.contents().find('#content');
Slide.setModal($modal, $content, $pad); Slide.setModal($modal, $content, $pad, ifrw);
var enterPresentationMode = function (shouldLog) { var enterPresentationMode = function (shouldLog) {
Slide.show(true, $textarea.val()); Slide.show(true, $textarea.val());
@ -169,7 +168,7 @@ define([
var config = { var config = {
//initialState: Messages.codeInitialState, //initialState: Messages.codeInitialState,
initialState: '{}', initialState: '{}',
websocketURL: Config.websocketURL, websocketURL: Cryptpad.getWebsocketURL(),
channel: secret.channel, channel: secret.channel,
// our public key // our public key
validateKey: secret.keys.validateKey || undefined, validateKey: secret.keys.validateKey || undefined,
@ -181,6 +180,11 @@ define([
var canonicalize = function (t) { return t.replace(/\r\n/g, '\n'); }; var canonicalize = function (t) { return t.replace(/\r\n/g, '\n'); };
var isDefaultTitle = function () {
var parsed = Cryptpad.parsePadUrl(window.location.href);
return Cryptpad.isDefaultName(parsed, APP.title);
};
var initializing = true; var initializing = true;
var onLocal = config.onLocal = function () { var onLocal = config.onLocal = function () {
@ -193,10 +197,11 @@ define([
// append the userlist to the hyperjson structure // append the userlist to the hyperjson structure
obj.metadata = { obj.metadata = {
users: userList, users: userList
title: APP.title
}; };
if (!isDefaultTitle()) {
obj.metadata.title = APP.title;
}
// stringify the json and send it into chainpad // stringify the json and send it into chainpad
var shjson = stringify(obj); var shjson = stringify(obj);
@ -258,7 +263,7 @@ define([
var parsed = Cryptpad.parsePadUrl(window.location.href); var parsed = Cryptpad.parsePadUrl(window.location.href);
var name = Cryptpad.getDefaultName(parsed, []); var name = Cryptpad.getDefaultName(parsed, []);
if (APP.title.slice(0, name.length) === name) { if (Cryptpad.isDefaultName(parsed, APP.title)) {
return getHeadingText() || APP.title; return getHeadingText() || APP.title;
} else { } else {
return APP.title || getHeadingText() || name; return APP.title || getHeadingText() || name;
@ -280,6 +285,32 @@ define([
saveAs(blob, filename); 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 onInit = config.onInit = function (info) { var onInit = config.onInit = function (info) {
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox'); var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
@ -300,116 +331,47 @@ define([
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
} }
/* add a "change username" button */
getLastName(function (err, lastName) { getLastName(function (err, lastName) {
var $username = Cryptpad.createButton('username', true) var usernameCb = function (newName) {
.click(function() { setName (newName);
Cryptpad.prompt(Messages.changeNamePrompt, lastName, function (newName) { };
setName(newName); var $username = Cryptpad.createButton('username', true, {lastName: lastName}, usernameCb);
});
});
$rightside.append($username); $rightside.append($username);
}); });
/* add an export button */ /* add an export button */
var $export = Cryptpad.createButton('export', true).click(exportText); var $export = Cryptpad.createButton('export', true, {}, 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) var $import = Cryptpad.createButton('import', true, {}, importText);
.click(Cryptpad.importContent('text/plain', function (content, file) {
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();
}));
$rightside.append($import); $rightside.append($import);
}
/* add a rename button */ /* add a rename button */
var $setTitle = Cryptpad.createButton('rename', true) var renameCb = function (err, title) {
.click(function () { if (err) { return; }
var suggestion = suggestName();
Cryptpad.prompt(Messages.renamePrompt,
suggestion, function (title, ev) {
if (title === null) { return; }
Cryptpad.causesNamingConflict(title, function (err, conflicts) {
if (err) {
console.log("Unable to determine if name caused a conflict");
console.error(err);
return;
}
if (conflicts) {
Cryptpad.alert(Messages.renameConflict);
return;
}
Cryptpad.setPadTitle(title, function (err, data) {
if (err) {
console.log("unable to set pad title");
console.log(err);
return;
}
APP.title = title; APP.title = title;
setTabTitle(); setTabTitle();
onLocal(); onLocal();
}); };
}); var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb);
});
});
$rightside.append($setTitle); $rightside.append($setTitle);
}
/* add a forget button */ /* add a forget button */
var $forgetPad = Cryptpad.createButton('forget', true) var forgetCb = function (err, title) {
.click(function () { if (err) { return; }
var href = window.location.href; APP.title = title;
Cryptpad.confirm(Messages.forgetPrompt, function (yes) {
if (!yes) { return; }
Cryptpad.forgetPad(href, function (err, data) {
if (err) {
console.log("unable to forget pad");
console.error(err);
return;
}
var parsed = Cryptpad.parsePadUrl(href);
APP.title = Cryptpad.getDefaultName(parsed, []);
setTabTitle(); setTabTitle();
}); };
}); var $forgetPad = Cryptpad.createButton('forget', true, {}, forgetCb);
});
$rightside.append($forgetPad); $rightside.append($forgetPad);
if (!readOnly && viewHash) { if (!readOnly && viewHash) {
/* add a 'links' button */ /* add a 'links' button */
var $links = Cryptpad.createButton('readonly', true) var $links = Cryptpad.createButton('readonly', true, {viewHash: viewHash + '/present'});
.click(function () {
var baseUrl = window.location.origin + window.location.pathname + '#';
var url = baseUrl + viewHash + '/present';
var content = '<b>' + Messages.readonlyUrl + '</b><br><a href="' + url + '" target="_blank" rel="noopener noreferrer">' + url + '</a><br>';
Cryptpad.alert(content);
});
$rightside.append($links); $rightside.append($links);
} }
@ -429,7 +391,7 @@ define([
} }
$rightside.append($leavePresent); $rightside.append($leavePresent);
$language = $('<span>', { var $language = $('<span>', {
'style': "margin-right: 10px;" 'style': "margin-right: 10px;"
}).text(Messages.type.slide + " (Markdown)"); }).text(Messages.type.slide + " (Markdown)");
$rightside.append($language); $rightside.append($language);
@ -485,7 +447,7 @@ define([
return; return;
} }
document.title = APP.title = title || info.channel.slice(0, 8); document.title = APP.title = title || info.channel.slice(0, 8);
Cryptpad.rememberPad(title, function (err, data) { Cryptpad.setPadTitle(title, function (err, data) {
if (err) { if (err) {
console.log("Unable to set pad title"); console.log("Unable to set pad title");
console.error(err); console.error(err);
@ -684,11 +646,13 @@ define([
var hjson2 = { var hjson2 = {
content: localDoc, content: localDoc,
metadata: { metadata: {
users: userList, users: userList
title: APP.title
}, },
highlightMode: highlightMode, highlightMode: highlightMode,
}; };
if (!isDefaultTitle()) {
hjson2.metadata.title = APP.title;
}
var shjson2 = stringify(hjson2); var shjson2 = stringify(hjson2);
if (shjson2 !== shjson) { if (shjson2 !== shjson) {
console.error("shjson2 !== shjson"); console.error("shjson2 !== shjson");

@ -14,14 +14,15 @@ define([
content: [], content: [],
changeHandlers: [], changeHandlers: [],
}; };
var ifrw = $('#pad-iframe')[0].contentWindow; var ifrw;
var $modal; var $modal;
var $content; var $content;
var $pad; var $pad;
Slide.setModal = function ($m, $c, $p) { Slide.setModal = function ($m, $c, $p, iframe) {
$modal = Slide.$modal = $m; $modal = Slide.$modal = $m;
$content = Slide.$content = $c; $content = Slide.$content = $c;
$pad = Slide.$pad = $p; $pad = Slide.$pad = $p;
ifrw = Slide.ifrw = iframe;
}; };
Slide.onChange = function (f) { Slide.onChange = function (f) {

Loading…
Cancel
Save