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

pull/1/head
ansuz 8 years ago
commit a338b23f40

@ -8,13 +8,7 @@ define([
'/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;
@ -42,85 +37,21 @@ define([
}; };
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 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 Title;
var UserList; var UserList;
var Metadata;
var config = { var config = {
initialState: '{}', initialState: '{}',
@ -144,11 +75,6 @@ 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) {
@ -156,14 +82,14 @@ define([
content: textValue, content: textValue,
metadata: { metadata: {
users: UserList.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);
@ -176,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);
@ -186,150 +112,15 @@ define([
} }
}; };
var 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();
};
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) { config.onInit = function (info) {
if (filename === null) { return; } UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad);
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) { var titleCfg = { getHeadingText: CodeMirror.getHeadingText };
// Extract the user list (metadata) from the hyperjson Title = Cryptpad.createTitle(titleCfg, config.onLocal, Cryptpad);
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
UserList.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);
}
};
config.onInit = function (info) { Metadata = Cryptpad.createMetadata(UserList, Title);
UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad);
var configTb = { var configTb = {
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'], displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'],
@ -338,11 +129,7 @@ define([
secret: secret, secret: secret,
channel: info.channel channel: info.channel
}, },
title: { title: Title.getTitleConfig(),
onRename: renameCb,
defaultName: defaultName,
suggestName: suggestName
},
common: Cryptpad, common: Cryptpad,
readOnly: readOnly, readOnly: readOnly,
ifrw: ifrw, ifrw: ifrw,
@ -352,6 +139,9 @@ define([
}; };
toolbar = module.toolbar = Toolbar.create(configTb); toolbar = module.toolbar = Toolbar.create(configTb);
Title.setToolbar(toolbar);
CodeMirror.init(config.onLocal, Title, toolbar);
var $rightside = toolbar.$rightside; var $rightside = toolbar.$rightside;
var editHash; var editHash;
@ -396,24 +186,20 @@ 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 */
@ -424,98 +210,17 @@ define([
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);
$block.find('a').click(function () {
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);
setTheme(lastTheme, $block);
$block.find('a').click(function () {
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); }
}; };
var unnotify = module.unnotify = function () {
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);
}
};
config.onReady = function (info) { config.onReady = function (info) {
if (module.realtime !== info.realtime) { if (module.realtime !== info.realtime) {
var realtime = module.realtime = info.realtime; var realtime = module.realtime = info.realtime;
@ -543,31 +248,24 @@ 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();
@ -580,76 +278,28 @@ define([
UserList.getLastName(toolbar.$userNameButton, isNew); UserList.getLastName(toolbar.$userNameButton, isNew);
}; };
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;
};
config.onRemote = function () { 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
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); CodeMirror.setValueAndCursor(oldDoc, remoteDoc, TextPatcher);
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");
@ -657,9 +307,7 @@ define([
module.patchText(shjson2); module.patchText(shjson2);
} }
} }
if (oldDoc !== remoteDoc) { if (oldDoc !== remoteDoc) { Cryptpad.notify(); }
notify();
}
}; };
config.onAbort = function () { config.onAbort = function () {

@ -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;
});

@ -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 = {};
@ -204,6 +206,28 @@ define([
$('#' + LOADING).find('p').html(error || Messages.error); $('#' + LOADING).find('p').html(error || Messages.error);
}; };
// 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) { UI.importContent = function (type, f) {
return function () { return function () {
var $files = $('<input type="file">').click(); var $files = $('<input type="file">').click();

@ -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;
});

@ -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;
});

@ -8,11 +8,14 @@ define([
'/common/common-interface.js', '/common/common-interface.js',
'/common/common-history.js', '/common/common-history.js',
'/common/common-userlist.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, UserList, 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
@ -53,6 +56,8 @@ 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
common.find = Util.find; common.find = Util.find;
@ -89,6 +94,15 @@ define([
// Userlist // Userlist
common.createUserList = UserList.create; 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); };

@ -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;
@ -105,8 +102,6 @@ define([
editor.on('instanceReady', Links.addSupportForOpeningLinksInNewTab(Ckeditor)); editor.on('instanceReady', Links.addSupportForOpeningLinksInNewTab(Ckeditor));
editor.on('instanceReady', function () { 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;
@ -277,7 +272,10 @@ define([
}; };
var initializing = true; var initializing = true;
var Title;
var UserList; var UserList;
var Metadata;
var getHeadingText = function () { var getHeadingText = function () {
var text; var text;
@ -290,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!
@ -316,11 +306,11 @@ define([
hjson[3] = { hjson[3] = {
metadata: { metadata: {
users: UserList.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;
} }
@ -369,68 +359,6 @@ define([
} }
}; };
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
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
UserList.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);
}
};
realtimeOptions.onRemote = function () { realtimeOptions.onRemote = function () {
if (initializing) { return; } if (initializing) { return; }
if (isHistoryMode) { return; } if (isHistoryMode) { return; }
@ -443,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;
@ -488,7 +416,7 @@ 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();
} }
}; };
@ -502,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; }
@ -516,15 +444,14 @@ define([
realtimeOptions.onLocal(); realtimeOptions.onLocal();
}; };
var renameCb = function (err, title) {
if (err) { return; }
document.title = title;
editor.fire('change');
};
realtimeOptions.onInit = function (info) { realtimeOptions.onInit = function (info) {
UserList = Cryptpad.createUserList(info, realtimeOptions.onLocal, Cryptget, Cryptpad); UserList = Cryptpad.createUserList(info, realtimeOptions.onLocal, Cryptget, Cryptpad);
var titleCfg = { getHeadingText: getHeadingText };
Title = Cryptpad.createTitle(titleCfg, realtimeOptions.onLocal, Cryptpad);
Metadata = Cryptpad.createMetadata(UserList, Title);
var configTb = { var configTb = {
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'], displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'],
userList: UserList.getToolbarConfig(), userList: UserList.getToolbarConfig(),
@ -532,11 +459,7 @@ define([
secret: secret, secret: secret,
channel: info.channel channel: info.channel
}, },
title: { title: Title.getTitleConfig(),
onRename: renameCb,
defaultName: defaultName,
suggestName: suggestName
},
common: Cryptpad, common: Cryptpad,
readOnly: readOnly, readOnly: readOnly,
ifrw: ifrw, ifrw: ifrw,
@ -546,6 +469,8 @@ define([
}; };
toolbar = info.realtime.toolbar = Toolbar.create(configTb); toolbar = info.realtime.toolbar = Toolbar.create(configTb);
Title.setToolbar(toolbar);
var $rightside = toolbar.$rightside; var $rightside = toolbar.$rightside;
var editHash; var editHash;
@ -666,13 +591,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);
@ -686,7 +605,7 @@ define([
} }
} }
} else { } else {
updateTitle(Cryptpad.initialName || defaultName); Title.updateTitle(Cryptpad.initialName || Title.defaultTitle);
documentBody.innerHTML = Messages.initialState; documentBody.innerHTML = Messages.initialState;
} }

@ -8,10 +8,8 @@ define([
'/bower_components/hyperjson/hyperjson.js', '/bower_components/hyperjson/hyperjson.js',
'render.js', 'render.js',
'/common/toolbar2.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,7 +32,6 @@ define([
if (!DEBUG) { if (!DEBUG) {
debug = function() {}; debug = function() {};
} }
var error = console.error;
Cryptpad.addLoadingScreen(); Cryptpad.addLoadingScreen();
var onConnectError = function () { var onConnectError = function () {
@ -186,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)) {
@ -228,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;
@ -442,43 +425,9 @@ define([
}); });
}; };
var Title;
var UserList; var UserList;
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));
}; };
@ -550,15 +499,15 @@ var ready = function (info, userid, readOnly) {
// Title // Title
if (APP.proxy.info.defaultTitle) { if (APP.proxy.info.defaultTitle) {
updateDefaultTitle(APP.proxy.info.defaultTitle); Title.updateDefaultTitle(APP.proxy.info.defaultTitle);
} else { } else {
APP.proxy.info.defaultTitle = defaultName; APP.proxy.info.defaultTitle = Title.defaultTitle;
} }
if (Cryptpad.initialName && !APP.proxy.info.title) { if (Cryptpad.initialName && !APP.proxy.info.title) {
APP.proxy.info.title = Cryptpad.initialName; APP.proxy.info.title = Cryptpad.initialName;
updateTitle(Cryptpad.initialName); Title.updateTitle(Cryptpad.initialName);
} else { } else {
updateTitle(APP.proxy.info.title || defaultName); Title.updateTitle(APP.proxy.info.title || Title.defaultTitle);
} }
// Description // Description
@ -586,8 +535,8 @@ var ready = function (info, userid, readOnly) {
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") {
UserList.addToUserData(APP.proxy.info.userData); UserList.addToUserData(APP.proxy.info.userData);
} else if (p[1] === 'description') { } else if (p[1] === 'description') {
@ -602,7 +551,7 @@ var ready = function (info, userid, readOnly) {
el.selectionStart = selects[0]; el.selectionStart = selects[0];
el.selectionEnd = selects[1]; el.selectionEnd = selects[1];
} }
notify(); Cryptpad.notify();
} }
debug("change: (%s, %s, [%s])", o, n, p.join(', ')); debug("change: (%s, %s, [%s])", o, n, p.join(', '));
@ -612,13 +561,6 @@ var ready = function (info, userid, readOnly) {
UserList.addToUserData(APP.proxy.info.userData); UserList.addToUserData(APP.proxy.info.userData);
if (Visible.isSupported()) {
Visible.onChange(function (yes) {
if (yes) { unnotify(); }
});
}
APP.ready = true; APP.ready = true;
if (!proxy.published) { if (!proxy.published) {
publish(false); publish(false);
@ -664,6 +606,11 @@ var create = function (info) {
}; };
UserList = Cryptpad.createUserList(info, onLocal, Cryptget, Cryptpad); UserList = Cryptpad.createUserList(info, onLocal, Cryptget, Cryptpad);
var onLocalTitle = function () {
APP.proxy.info.title = Title.isDefaultTitle() ? "" : Title.title;
};
Title = Cryptpad.createTitle({}, onLocalTitle, Cryptpad);
var configTb = { var configTb = {
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'], displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'],
userList: UserList.getToolbarConfig(), userList: UserList.getToolbarConfig(),
@ -671,11 +618,7 @@ var create = function (info) {
secret: secret, secret: secret,
channel: info.channel channel: info.channel
}, },
title: { title: Title.getTitleConfig(),
onRename: renameCb,
defaultName: defaultName,
suggestName: suggestName
},
common: Cryptpad, common: Cryptpad,
readOnly: readOnly, readOnly: readOnly,
ifrw: window, ifrw: window,
@ -685,6 +628,8 @@ var create = function (info) {
}; };
APP.toolbar = Toolbar.create(configTb); APP.toolbar = Toolbar.create(configTb);
Title.setToolbar(APP.toolbar);
var $rightside = APP.toolbar.$rightside; var $rightside = APP.toolbar.$rightside;
/* add a forget button */ /* add a forget button */

@ -8,15 +8,8 @@ define([
'/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;
@ -62,77 +47,32 @@ define([
}; };
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 path = '/common/theme/';
var $head = $(ifrw.document.head); var Title;
var UserList;
var themeLoaded = module.themeLoaded = function (theme) { var Metadata;
return $head.find('link[href*="'+theme+'"]').length;
};
var loadTheme = module.loadTheme = function (theme) { var setTabTitle = function (title) {
$head.append($('<link />', { var slideNumber = '';
rel: 'stylesheet', if (Slide.index && Slide.content.length) {
href: path + theme + '.css', slideNumber = ' (' + Slide.index + '/' + Slide.content.length + ')';
})); }
}; document.title = title + slideNumber;
};
return function (theme, $select) { var initialState = Messages.slideInitialState;
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,39 +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 UserList;
var textColor; var textColor;
var backColor; var backColor;
var config = { var config = {
//initialState: Messages.codeInitialState,
initialState: '{}', initialState: '{}',
websocketURL: Cryptpad.getWebsocketURL(), websocketURL: Cryptpad.getWebsocketURL(),
channel: secret.channel, channel: secret.channel,
@ -202,12 +124,12 @@ define([
content: textValue, content: textValue,
metadata: { metadata: {
users: UserList.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;
@ -226,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);
@ -237,98 +159,16 @@ define([
} }
}; };
var getHeadingText = function () { var metadataCfg = {
var lines = editor.getValue().split(/\n/); slideOptions: function (newOpt) {
if (stringify(newOpt) !== stringify(slideOptions)) {
var text = ''; $.extend(slideOptions, newOpt);
lines.some(function (line) { // TODO: manage realtime + cursor in the "options" modal ??
// lines beginning with a hash are potentially valuable Slide.updateOptions();
// 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);
@ -343,51 +183,6 @@ 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
UserList.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,
@ -461,6 +256,14 @@ define([
config.onInit = function (info) { config.onInit = function (info) {
UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad); 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: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'], displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'],
userList: UserList.getToolbarConfig(), userList: UserList.getToolbarConfig(),
@ -468,11 +271,7 @@ define([
secret: secret, secret: secret,
channel: info.channel channel: info.channel
}, },
title: { title: Title.getTitleConfig(),
onRename: renameCb,
defaultName: defaultName,
suggestName: suggestName
},
common: Cryptpad, common: Cryptpad,
readOnly: readOnly, readOnly: readOnly,
ifrw: ifrw, ifrw: ifrw,
@ -482,6 +281,9 @@ define([
}; };
toolbar = module.toolbar = Toolbar.create(configTb); toolbar = module.toolbar = Toolbar.create(configTb);
Title.setToolbar(toolbar);
CodeMirror.init(config.onLocal, Title, toolbar);
var $rightside = toolbar.$rightside; var $rightside = toolbar.$rightside;
var editHash; var editHash;
@ -534,12 +336,12 @@ 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);
} }
@ -586,49 +388,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);
setTheme(lastTheme, $block);
$block.find('a').click(function () {
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,
@ -675,7 +434,7 @@ define([
}; };
configureColors(); configureColors();
configureTheme(); CodeMirror.configureTheme();
if (presentMode) { if (presentMode) {
$('#top-bar').hide(); $('#top-bar').hide();
@ -687,20 +446,6 @@ define([
} }
}; };
var unnotify = module.unnotify = function () {
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);
}
};
config.onReady = function (info) { config.onReady = function (info) {
if (module.realtime !== info.realtime) { if (module.realtime !== info.realtime) {
var realtime = module.realtime = info.realtime; var realtime = module.realtime = info.realtime;
@ -727,31 +472,23 @@ 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 = APP.title + ' (' + (++n) + '/' + l + ')';
@ -771,76 +508,28 @@ define([
UserList.getLastName(toolbar.$userNameButton, isNew); UserList.getLastName(toolbar.$userNameButton, isNew);
}; };
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;
};
config.onRemote = function () { 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
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); CodeMirror.setValueAndCursor(oldDoc, remoteDoc, TextPatcher);
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");
@ -851,7 +540,7 @@ define([
Slide.update(remoteDoc); Slide.update(remoteDoc);
if (oldDoc !== remoteDoc) { if (oldDoc !== remoteDoc) {
notify(); Cryptpad.notify();
} }
}; };

@ -10,12 +10,10 @@ define([
'/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;
@ -212,9 +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 Title;
var UserList; var UserList;
var Metadata;
var config = module.config = { var config = module.config = {
initialState: '{}', initialState: '{}',
@ -253,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">&nbsp;</div>'); $colors.html('<div class="hidden">&nbsp;</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: '!' });
@ -305,6 +290,11 @@ window.canvas = canvas;
config.onInit = function (info) { config.onInit = function (info) {
UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad); UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad);
Title = Cryptpad.createTitle({}, config.onLocal, Cryptpad);
Metadata = Cryptpad.createMetadata(UserList, Title, metadataCfg);
var configTb = { var configTb = {
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'], displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'],
userList: UserList.getToolbarConfig(), userList: UserList.getToolbarConfig(),
@ -312,11 +302,7 @@ window.canvas = canvas;
secret: secret, secret: secret,
channel: info.channel channel: info.channel
}, },
title: { title: Title.getTitleConfig(),
onRename: renameCb,
defaultName: defaultName,
suggestName: suggestName
},
common: Cryptpad, common: Cryptpad,
readOnly: readOnly, readOnly: readOnly,
ifrw: window, ifrw: window,
@ -327,6 +313,8 @@ window.canvas = canvas;
toolbar = module.toolbar = Toolbar.create(configTb); toolbar = module.toolbar = Toolbar.create(configTb);
Title.setToolbar(toolbar);
var $rightside = toolbar.$rightside; var $rightside = toolbar.$rightside;
/* save as template */ /* save as template */
@ -371,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
UserList.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;
@ -449,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);
@ -460,11 +384,11 @@ window.canvas = canvas;
metadata: { metadata: {
users: UserList.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);
@ -495,10 +419,6 @@ 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 */
if (readOnly) { return; } if (readOnly) { return; }

Loading…
Cancel
Save