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

pull/1/head
ansuz 7 years ago
commit d1beb6493b

@ -1,54 +0,0 @@
define([
'jquery',
'/customize/application_config.js',
'/common/cryptpad-common.js',
'/api/config',
], function ($, Config, Cryptpad, ApiConfig) {
window.APP = {
Cryptpad: Cryptpad,
};
var Messages = Cryptpad.Messages;
$(function () {
// Language selector
var $sel = $('#language-selector');
Cryptpad.createLanguageSelector(undefined, $sel);
$sel.find('button').addClass('btn').addClass('btn-secondary');
$sel.show();
var $upgrade = $('#upgrade');
var showUpgrade = function (text, feedback, url) {
if (ApiConfig.removeDonateButton) { return; }
if (localStorage.plan) { return; }
if (!text) { return; }
$upgrade.text(text).show();
$upgrade.click(function () {
Cryptpad.feedback(feedback);
window.open(url,'_blank');
});
};
// User admin menu
var $userMenu = $('#user-menu');
var userMenuCfg = {
$initBlock: $userMenu,
'static': true
};
var $userAdmin = Cryptpad.createUserAdminMenu(userMenuCfg);
$userAdmin.find('button').addClass('btn').addClass('btn-secondary');
$(window).click(function () {
$('.cp-dropdown-content').hide();
});
if (Cryptpad.isLoggedIn() && ApiConfig.allowSubscriptions) {
showUpgrade(Messages.upgradeAccount, "HOME_UPGRADE_ACCOUNT", Cryptpad.upgradeURL);
} else {
showUpgrade(Messages.supportCryptpad, "HOME_SUPPORT_CRYPTPAD", Cryptpad.donateURL);
}
});
});

@ -2,15 +2,15 @@ define([
'jquery', 'jquery',
'/customize/application_config.js', '/customize/application_config.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/customize/header.js', '/common/common-interface.js',
], function ($, Config, Cryptpad) { '/common/common-realtime.js',
'/customize/messages.js',
], function ($, Config, Cryptpad, UI, Realtime, Messages) {
window.APP = { window.APP = {
Cryptpad: Cryptpad, Cryptpad: Cryptpad,
}; };
var Messages = Cryptpad.Messages;
$(function () { $(function () {
var $main = $('#mainBlock'); var $main = $('#mainBlock');
@ -58,34 +58,6 @@ define([
$('#name').focus(); $('#name').focus();
} }
var displayCreateButtons = function () {
var $parent = $('#buttons');
var options = [];
var $container = $('<div>', {'class': 'cp-dropdown-container'}).appendTo($parent);
Config.availablePadTypes.forEach(function (el) {
if (el === 'drive') { return; }
if (!Cryptpad.isLoggedIn() && Config.registeredOnlyTypes &&
Config.registeredOnlyTypes.indexOf(el) !== -1) { return; }
options.push({
tag: 'a',
attributes: {
'class': 'newdoc',
'href': '/' + el + '/',
'target': '_blank'
},
content: Messages['button_new' + el] // Pretty name of the language value
});
});
var dropdownConfig = {
text: Messages.login_makeAPad, // Button initial text
options: options, // Entries displayed in the menu
container: $container
};
var $block = Cryptpad.createDropdown(dropdownConfig);
$block.find('button').addClass('btn').addClass('btn-primary');
$block.appendTo($parent);
};
/* Log in UI */ /* Log in UI */
var Login; var Login;
// deferred execution to avoid unnecessary asset loading // deferred execution to avoid unnecessary asset loading
@ -116,7 +88,7 @@ define([
$('button.login').click(function () { $('button.login').click(function () {
// setTimeout 100ms to remove the keyboard on mobile devices before the loading screen pops up // setTimeout 100ms to remove the keyboard on mobile devices before the loading screen pops up
window.setTimeout(function () { window.setTimeout(function () {
Cryptpad.addLoadingScreen({loadingText: Messages.login_hashing}); UI.addLoadingScreen({loadingText: Messages.login_hashing});
// We need a setTimeout(cb, 0) otherwise the loading screen is only displayed after hashing the password // We need a setTimeout(cb, 0) otherwise the loading screen is only displayed after hashing the password
window.setTimeout(function () { window.setTimeout(function () {
loginReady(function () { loginReady(function () {
@ -135,7 +107,7 @@ define([
proxy.edPrivate = result.edPrivate; proxy.edPrivate = result.edPrivate;
proxy.edPublic = result.edPublic; proxy.edPublic = result.edPublic;
Cryptpad.whenRealtimeSyncs(result.realtime, function () { Realtime.whenRealtimeSyncs(result.realtime, function () {
Cryptpad.login(result.userHash, result.userName, function () { Cryptpad.login(result.userHash, result.userName, function () {
document.location.href = '/drive/'; document.location.href = '/drive/';
}); });
@ -144,22 +116,22 @@ define([
} }
switch (err) { switch (err) {
case 'NO_SUCH_USER': case 'NO_SUCH_USER':
Cryptpad.removeLoadingScreen(function () { UI.removeLoadingScreen(function () {
Cryptpad.alert(Messages.login_noSuchUser); UI.alert(Messages.login_noSuchUser);
}); });
break; break;
case 'INVAL_USER': case 'INVAL_USER':
Cryptpad.removeLoadingScreen(function () { UI.removeLoadingScreen(function () {
Cryptpad.alert(Messages.login_invalUser); UI.alert(Messages.login_invalUser);
}); });
break; break;
case 'INVAL_PASS': case 'INVAL_PASS':
Cryptpad.removeLoadingScreen(function () { UI.removeLoadingScreen(function () {
Cryptpad.alert(Messages.login_invalPass); UI.alert(Messages.login_invalPass);
}); });
break; break;
default: // UNHANDLED ERROR default: // UNHANDLED ERROR
Cryptpad.errorLoadingScreen(Messages.login_unhandledError); UI.errorLoadingScreen(Messages.login_unhandledError);
} }
}); });
}); });
@ -167,27 +139,6 @@ define([
}, 100); }, 100);
}); });
/* End Log in UI */ /* End Log in UI */
var addButtonHandlers = function () {
$('button.register').click(function () {
var username = $('#name').val();
var passwd = $('#password').val();
sessionStorage.login_user = username;
sessionStorage.login_pass = passwd;
document.location.href = '/register/';
});
$('button.gotodrive').click(function () {
document.location.href = '/drive/';
});
$('button#loggedInLogout').click(function () {
$('#user-menu .logout').click();
});
};
displayCreateButtons();
addButtonHandlers();
console.log("ready"); console.log("ready");
}); });
}); });

@ -30,7 +30,6 @@ if (language && map[language]) { req.push('/customize/translations/messages.' +
define(req, function($, Default, Language) { define(req, function($, Default, Language) {
map.en = 'English'; map.en = 'English';
var defaultLanguage = 'en'; var defaultLanguage = 'en';
console.log(messages);
if (!Language || language === defaultLanguage || !map[language]) { if (!Language || language === defaultLanguage || !map[language]) {
messages = $.extend(true, messages, Default); messages = $.extend(true, messages, Default);
@ -39,7 +38,6 @@ console.log(messages);
// Add the translated keys to the returned object // Add the translated keys to the returned object
messages = $.extend(true, messages, Default, Language); messages = $.extend(true, messages, Default, Language);
} }
console.log(messages);
messages._languages = map; messages._languages = map;
messages._languageUsed = language; messages._languageUsed = language;

@ -43,7 +43,7 @@
display: block; display: block;
} }
a { & > a, & > span {
color: @colortheme_dropdown-color; color: @colortheme_dropdown-color;
padding: 5px 16px; padding: 5px 16px;
text-decoration: none; text-decoration: none;
@ -75,6 +75,31 @@
color: @colortheme_dropdown-color; color: @colortheme_dropdown-color;
} }
} }
&> span {
box-sizing: border-box;
height: 26px;
border-radius: 0;
border: 0;
padding: 0 16px;
.cp-dropdown-content {
margin-top: 26px;
left: 0;
}
button {
padding: 0;
text-align: left;
margin: 0;
border-radius: 0;
border: 0;
width: 100%;
line-height: 1em;
.cp-toolbar-drawer-element {
margin-left: 10px;
display: inline;
vertical-align: top;
}
}
}
hr { hr {
margin: 5px 0px; margin: 5px 0px;

@ -102,6 +102,32 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
display: flex;
justify-content: space-between;
align-items: center;
}
.cp-toolbar-userlist-name-input {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: none;
border: none;
}
.cp-toolbar-userlist-name-value {
overflow: hidden;
flex: 1;
min-width: 0;
min-height: 0;
text-overflow: ellipsis;
}
.cp-toolbar-userlist-name-edit {
width: 20px;
font-size: 16px;
padding: 0;
border: none;
height: 20px;
cursor: pointer;
} }
.cp-toolbar-userlist-friend { .cp-toolbar-userlist-friend {
padding: 0; padding: 0;
@ -115,7 +141,7 @@
} }
} }
.addToolbarColors (@color, @bg-color) { .addToolbarColors (@color, @bg-color, @barWidth: 600px) {
.cp-toolbar-userlist-drawer { .cp-toolbar-userlist-drawer {
background-color: @bgcolor; background-color: @bgcolor;
color: @color; color: @color;
@ -126,6 +152,19 @@
background-color: darken(@bgcolor, 10%); background-color: darken(@bgcolor, 10%);
color: @color; color: @color;
} }
.cp-toolbar-userlist-name-input {
background-color: darken(@bg-color, 10%);
color: @color;
}
.cp-toolbar-userlist-name-edit {
color: contrast(@color,
lighten(@color, 20%),
darken(@color, 20%));
background: transparent;
&:hover {
color: @color;
}
}
.cp-toolbar-userlist-friend { .cp-toolbar-userlist-friend {
&:hover { &:hover {
color: darken(@color, 15%); color: darken(@color, 15%);
@ -148,6 +187,13 @@
background-color: @bgcolor; background-color: @bgcolor;
} }
} }
.cp-toolbar-rightside {
@media screen and (max-width: @barWidth) { // 450px
flex-wrap: wrap;
height: auto;
width: 100%;
}
}
.cp-toolbar-title-hoverable:hover { .cp-toolbar-title-hoverable:hover {
.cp-toolbar-title-editable, .cp-toolbar-title-edit { .cp-toolbar-title-editable, .cp-toolbar-title-edit {
cursor: text; cursor: text;
@ -192,7 +238,7 @@
&.cp-app-slide { &.cp-app-slide {
@bgcolor: @colortheme_slide-bg; @bgcolor: @colortheme_slide-bg;
@color: @colortheme_slide-color; @color: @colortheme_slide-color;
.addToolbarColors(@color, @bgcolor); .addToolbarColors(@color, @bgcolor, 700px);
} }
&.cp-app-poll { &.cp-app-poll {
@bgcolor: @colortheme_poll-bg; @bgcolor: @colortheme_poll-bg;
@ -236,33 +282,6 @@
} }
/* TODO: move to the slide LESS page */
.cp-app-slide {
@media screen and (max-width: @browser_media-medium-screen) {
.cp-toolbar-leftside {
flex-flow: row wrap;
width: 175px;
height: auto;
.cp-toolbar-spinner { order: 0; }
}
.cp-toolbar-rightside {
height: 2*@toolbar_line-height;
}
}
@media screen and (max-width: 320px) {
.cp-toolbar-leftside {
flex-flow: row wrap;
width: 175px;
height: auto;
padding-top: @toolbar_line-height;
.cp-toolbar-spinner { order: 0; }
}
.cp-toolbar-rightside {
height: auto;
}
}
}
.cp-toolbar { .cp-toolbar {
* { * {
outline-width: 0; outline-width: 0;
@ -275,6 +294,9 @@
box-sizing: border-box; box-sizing: border-box;
padding: 0px; padding: 0px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
//background-color: #BBBBFF; //background-color: #BBBBFF;
background-color: @colortheme_default-bg; background-color: @colortheme_default-bg;
@ -437,8 +459,9 @@
flex-flow: row; flex-flow: row;
height: @toolbar_top-height; height: @toolbar_top-height;
position: relative; position: relative;
width: 100%;
.cp-toolbar-top-filler { .cp-toolbar-top-filler {
height: 100%; height: @toolbar_top-height;
display: inline-block; display: inline-block;
order: 4; order: 4;
//flex: 1; //flex: 1;
@ -600,7 +623,7 @@
} }
} }
.cp-toolbar-user { .cp-toolbar-user {
height: 100%; height: @toolbar_top-height;
display: inline-flex; display: inline-flex;
order: 5; order: 5;
line-height: @toolbar_top-height; line-height: @toolbar_top-height;
@ -628,8 +651,9 @@
} }
.cp-dropdown-content { .cp-dropdown-content {
margin: 0; margin: 0;
overflow: visible;
} }
button { & > button {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@ -675,9 +699,10 @@
&:empty { &:empty {
height: 0; height: 0;
} }
float: left;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
max-width: 100%;
flex: 1;
//margin-bottom: -1px; //margin-bottom: -1px;
.cp-toolbar-users { .cp-toolbar-users {
pre { pre {
@ -720,12 +745,6 @@
height: 0; height: 0;
} }
text-align: right; text-align: right;
/*&> button {
height: 100%;
margin: 0;
border-radius: 0;
padding: 0 10px;
}*/
.cp-toolbar-drawer-content:empty ~ .cp-toolbar-drawer-button { .cp-toolbar-drawer-content:empty ~ .cp-toolbar-drawer-button {
display: none; display: none;
} }
@ -773,6 +792,11 @@
.cp-toolbar-spinner { .cp-toolbar-spinner {
line-height: @toolbar_line-height; line-height: @toolbar_line-height;
padding: 0 20px; padding: 0 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 200px;
box-sizing: border-box;
&> span.fa { &> span.fa {
height: 20px; height: 20px;
width: 20px; width: 20px;

@ -201,6 +201,7 @@ define(function () {
out.cancel = "Annuler"; out.cancel = "Annuler";
out.cancelButton = 'Annuler (Échap)'; out.cancelButton = 'Annuler (Échap)';
out.doNotAskAgain = "Ne plus demander (Échap)";
out.historyText = "Historique"; out.historyText = "Historique";
out.historyButton = "Afficher l'historique du document"; out.historyButton = "Afficher l'historique du document";
@ -488,12 +489,12 @@ define(function () {
out.settings_resetTipsButton = "Réinitialiser les astuces visibles dans CryptDrive"; out.settings_resetTipsButton = "Réinitialiser les astuces visibles dans CryptDrive";
out.settings_resetTipsDone = "Toutes les astuces sont de nouveau visibles."; out.settings_resetTipsDone = "Toutes les astuces sont de nouveau visibles.";
out.settings_thumbnails = "Vignettes"; out.settings_thumbnails = "Miniatures";
out.settings_disableThumbnailsAction = "Désactiver la création de vignettes dans CryptDrive"; out.settings_disableThumbnailsAction = "Désactiver la création de miniatures dans CryptDrive";
out.settings_disableThumbnailsDescription = "Des vignettes de vos pads sont automatiquement créées et stockées dans votre navigateur. Vous pouvez désactiver cette fonctionnalité."; out.settings_disableThumbnailsDescription = "Des miniatures de vos pads sont automatiquement créées et stockées dans votre navigateur. Vous pouvez désactiver cette fonctionnalité.";
out.settings_resetThumbnailsAction = "Nettoyer"; out.settings_resetThumbnailsAction = "Nettoyer";
out.settings_resetThumbnailsDescription = "Nettoyer toutes les vignettes stockées dans votre navigateur."; out.settings_resetThumbnailsDescription = "Nettoyer toutes les miniatures stockées dans votre navigateur.";
out.settings_resetThumbnailsDone = "Toutes les vignettes ont été effacées."; out.settings_resetThumbnailsDone = "Toutes les miniatures ont été effacées.";
out.settings_importTitle = "Importer les pads récents de ce navigateur dans votre CryptDrive"; out.settings_importTitle = "Importer les pads récents de ce navigateur dans votre CryptDrive";
out.settings_import = "Importer"; out.settings_import = "Importer";
@ -523,6 +524,9 @@ define(function () {
out.settings_codeUseTabs = "Utiliser des tabulations au lieu d'espaces"; out.settings_codeUseTabs = "Utiliser des tabulations au lieu d'espaces";
out.upload_title = "Hébergement de fichiers"; out.upload_title = "Hébergement de fichiers";
out.upload_rename = "Souhaitez-vous renommer <b>{0}</b> avant son stockage en ligne ?<br>" +
"<em>L'extension du fichier ({1}) sera ajoutée automatiquement. "+
"Ce nom sera permanent et visible par les autres utilisateurs</em>.";
out.upload_serverError = "Erreur interne: impossible d'importer le fichier pour l'instant."; out.upload_serverError = "Erreur interne: impossible d'importer le fichier pour l'instant.";
out.upload_uploadPending = "Vous avez déjà un fichier en cours d'importation. Souhaitez-vous l'annuler et importer ce nouveau fichier ?"; out.upload_uploadPending = "Vous avez déjà un fichier en cours d'importation. Souhaitez-vous l'annuler et importer ce nouveau fichier ?";
out.upload_success = "Votre fichier ({0}) a été importé avec succès et ajouté à votre CryptDrive."; out.upload_success = "Votre fichier ({0}) a été importé avec succès et ajouté à votre CryptDrive.";

@ -204,6 +204,7 @@ define(function () {
out.cancel = "Cancel"; out.cancel = "Cancel";
out.cancelButton = 'Cancel (esc)'; out.cancelButton = 'Cancel (esc)';
out.doNotAskAgain = "Don't ask me again (Esc)";
out.historyText = "History"; out.historyText = "History";
out.historyButton = "Display the document history"; out.historyButton = "Display the document history";
@ -528,6 +529,9 @@ define(function () {
out.settings_codeUseTabs = "Indent using tabs (instead of spaces)"; out.settings_codeUseTabs = "Indent using tabs (instead of spaces)";
out.upload_title = "File upload"; out.upload_title = "File upload";
out.upload_rename = "Do you want to rename <b>{0}</b> before uploading it to the server?<br>" +
"<em>The file extension ({1}) will be added automatically. "+
"This name will be permanent and visible to other users.</em>";
out.upload_serverError = "Server Error: unable to upload your file at this time."; out.upload_serverError = "Server Error: unable to upload your file at this time.";
out.upload_uploadPending = "You already have an upload in progress. Cancel it and upload your new file?"; out.upload_uploadPending = "You already have an upload in progress. Cancel it and upload your new file?";
out.upload_success = "Your file ({0}) has been successfully uploaded and added to your drive."; out.upload_success = "Your file ({0}) has been successfully uploaded and added to your drive.";

@ -3,12 +3,12 @@ define([
'/bower_components/hyperjson/hyperjson.js', '/bower_components/hyperjson/hyperjson.js',
'/bower_components/textpatcher/TextPatcher.amd.js', '/bower_components/textpatcher/TextPatcher.amd.js',
'json.sortify', 'json.sortify',
'/common/cryptpad-common.js',
'/drive/tests.js', '/drive/tests.js',
'/common/test.js', '/common/test.js',
'/common/common-hash.js',
'/common/common-thumbnail.js', '/common/common-thumbnail.js',
'/common/flat-dom.js', '/common/flat-dom.js',
], function ($, Hyperjson, TextPatcher, Sortify, Cryptpad, Drive, Test, Thumb, Flat) { ], function ($, Hyperjson, TextPatcher, Sortify, Drive, Test, Hash, Thumb, Flat) {
window.Hyperjson = Hyperjson; window.Hyperjson = Hyperjson;
window.TextPatcher = TextPatcher; window.TextPatcher = TextPatcher;
window.Sortify = Sortify; window.Sortify = Sortify;
@ -158,7 +158,7 @@ define([
// check that old hashes parse correctly // check that old hashes parse correctly
assert(function (cb) { assert(function (cb) {
var secret = Cryptpad.parsePadUrl('/pad/#67b8385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyHci7LNy'); var secret = Hash.parsePadUrl('/pad/#67b8385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyHci7LNy');
return cb(secret.hashData.channel === "67b8385b07352be53e40746d2be6ccd7" && return cb(secret.hashData.channel === "67b8385b07352be53e40746d2be6ccd7" &&
secret.hashData.key === "XAYSuJYYqa9NfmInyHci7LNy" && secret.hashData.key === "XAYSuJYYqa9NfmInyHci7LNy" &&
secret.hashData.version === 0); secret.hashData.version === 0);
@ -166,7 +166,7 @@ define([
// make sure version 1 hashes parse correctly // make sure version 1 hashes parse correctly
assert(function (cb) { assert(function (cb) {
var secret = Cryptpad.parsePadUrl('/pad/#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI'); var secret = Hash.parsePadUrl('/pad/#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI');
return cb(secret.hashData.version === 1 && return cb(secret.hashData.version === 1 &&
secret.hashData.mode === "edit" && secret.hashData.mode === "edit" &&
secret.hashData.channel === "3Ujt4F2Sjnjbis6CoYWpoQ" && secret.hashData.channel === "3Ujt4F2Sjnjbis6CoYWpoQ" &&
@ -176,7 +176,7 @@ define([
// test support for present mode in hashes // test support for present mode in hashes
assert(function (cb) { assert(function (cb) {
var secret = Cryptpad.parsePadUrl('/pad/#/1/edit/CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G/present'); var secret = Hash.parsePadUrl('/pad/#/1/edit/CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G/present');
return cb(secret.hashData.version === 1 return cb(secret.hashData.version === 1
&& secret.hashData.mode === "edit" && secret.hashData.mode === "edit"
&& secret.hashData.channel === "CmN5+YJkrHFS3NSBg-P7Sg" && secret.hashData.channel === "CmN5+YJkrHFS3NSBg-P7Sg"
@ -186,7 +186,7 @@ define([
// test support for present mode in hashes // test support for present mode in hashes
assert(function (cb) { assert(function (cb) {
var secret = Cryptpad.parsePadUrl('/pad/#/1/edit//CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G//present'); var secret = Hash.parsePadUrl('/pad/#/1/edit//CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G//present');
return cb(secret.hashData.version === 1 return cb(secret.hashData.version === 1
&& secret.hashData.mode === "edit" && secret.hashData.mode === "edit"
&& secret.hashData.channel === "CmN5+YJkrHFS3NSBg-P7Sg" && secret.hashData.channel === "CmN5+YJkrHFS3NSBg-P7Sg"
@ -196,7 +196,7 @@ define([
// test support for present & embed mode in hashes // test support for present & embed mode in hashes
assert(function (cb) { assert(function (cb) {
var secret = Cryptpad.parsePadUrl('/pad/#/1/edit//CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G/embed/present/'); var secret = Hash.parsePadUrl('/pad/#/1/edit//CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G/embed/present/');
return cb(secret.hashData.version === 1 return cb(secret.hashData.version === 1
&& secret.hashData.mode === "edit" && secret.hashData.mode === "edit"
&& secret.hashData.channel === "CmN5+YJkrHFS3NSBg-P7Sg" && secret.hashData.channel === "CmN5+YJkrHFS3NSBg-P7Sg"
@ -207,7 +207,7 @@ define([
// test support for present & embed mode in hashes // test support for present & embed mode in hashes
assert(function (cb) { assert(function (cb) {
var secret = Cryptpad.parsePadUrl('/pad/#/1/edit//CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G/present/embed'); var secret = Hash.parsePadUrl('/pad/#/1/edit//CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G/present/embed');
return cb(secret.hashData.version === 1 return cb(secret.hashData.version === 1
&& secret.hashData.mode === "edit" && secret.hashData.mode === "edit"
&& secret.hashData.channel === "CmN5+YJkrHFS3NSBg-P7Sg" && secret.hashData.channel === "CmN5+YJkrHFS3NSBg-P7Sg"
@ -218,7 +218,7 @@ define([
// test support for embed mode in hashes // test support for embed mode in hashes
assert(function (cb) { assert(function (cb) {
var secret = Cryptpad.parsePadUrl('/pad/#/1/edit//CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G///embed//'); var secret = Hash.parsePadUrl('/pad/#/1/edit//CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G///embed//');
return cb(secret.hashData.version === 1 return cb(secret.hashData.version === 1
&& secret.hashData.mode === "edit" && secret.hashData.mode === "edit"
&& secret.hashData.channel === "CmN5+YJkrHFS3NSBg-P7Sg" && secret.hashData.channel === "CmN5+YJkrHFS3NSBg-P7Sg"
@ -229,7 +229,7 @@ define([
// test support for trailing slash // test support for trailing slash
assert(function (cb) { assert(function (cb) {
var secret = Cryptpad.parsePadUrl('/pad/#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI/'); var secret = Hash.parsePadUrl('/pad/#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI/');
return cb(secret.hashData.version === 1 && return cb(secret.hashData.version === 1 &&
secret.hashData.mode === "edit" && secret.hashData.mode === "edit" &&
secret.hashData.channel === "3Ujt4F2Sjnjbis6CoYWpoQ" && secret.hashData.channel === "3Ujt4F2Sjnjbis6CoYWpoQ" &&
@ -238,7 +238,7 @@ define([
}, "test support for trailing slashes in version 1 hash failed to parse"); }, "test support for trailing slashes in version 1 hash failed to parse");
assert(function (cb) { assert(function (cb) {
var secret = Cryptpad.parsePadUrl('/invite/#/1/ilrOtygzDVoUSRpOOJrUuQ/e8jvf36S3chzkkcaMrLSW7PPrz7VDp85lIFNI26dTmr=/'); var secret = Hash.parsePadUrl('/invite/#/1/ilrOtygzDVoUSRpOOJrUuQ/e8jvf36S3chzkkcaMrLSW7PPrz7VDp85lIFNI26dTmr=/');
var hd = secret.hashData; var hd = secret.hashData;
cb(hd.channel === "ilrOtygzDVoUSRpOOJrUuQ" && cb(hd.channel === "ilrOtygzDVoUSRpOOJrUuQ" &&
hd.pubkey === "e8jvf36S3chzkkcaMrLSW7PPrz7VDp85lIFNI26dTmr=" && hd.pubkey === "e8jvf36S3chzkkcaMrLSW7PPrz7VDp85lIFNI26dTmr=" &&

@ -1,13 +1,14 @@
define([ define([
'jquery', 'jquery',
'/bower_components/textpatcher/TextPatcher.js', '/bower_components/textpatcher/TextPatcher.js',
'/common/cryptpad-common.js',
'/common/diffMarked.js', '/common/diffMarked.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/common/sframe-app-framework.js', '/common/sframe-app-framework.js',
'/common/common-util.js', '/common/common-util.js',
'/common/common-hash.js',
'/common/modes.js', '/common/modes.js',
'/customize/messages.js',
'cm/lib/codemirror', 'cm/lib/codemirror',
'css!cm/lib/codemirror.css', 'css!cm/lib/codemirror.css',
@ -39,17 +40,17 @@ define([
], function ( ], function (
$, $,
TextPatcher, TextPatcher,
Cryptpad,
DiffMd, DiffMd,
nThen, nThen,
SFCommon, SFCommon,
Framework, Framework,
Util, Util,
Hash,
Modes, Modes,
Messages,
CMeditor) CMeditor)
{ {
window.CodeMirror = CMeditor; window.CodeMirror = CMeditor;
var Messages = Cryptpad.Messages;
var MEDIA_TAG_MODES = Object.freeze([ var MEDIA_TAG_MODES = Object.freeze([
'markdown', 'markdown',
@ -82,7 +83,11 @@ define([
}, 150); }, 150);
$previewButton.removeClass('fa-question').addClass('fa-eye'); $previewButton.removeClass('fa-question').addClass('fa-eye');
window.setTimeout(function () {
// setTimeout needed for tippy (tooltip), otherwise we have the browser's default
// tooltips
$previewButton.attr('title', Messages.previewButtonTitle); $previewButton.attr('title', Messages.previewButtonTitle);
});
var previewTo; var previewTo;
$previewButton.click(function () { $previewButton.click(function () {
clearTimeout(previewTo); clearTimeout(previewTo);
@ -293,8 +298,8 @@ define([
//var cursor = editor.getCursor(); //var cursor = editor.getCursor();
//var cleanName = data.name.replace(/[\[\]]/g, ''); //var cleanName = data.name.replace(/[\[\]]/g, '');
//var text = '!['+cleanName+']('+data.url+')'; //var text = '!['+cleanName+']('+data.url+')';
var parsed = Cryptpad.parsePadUrl(data.url); var parsed = Hash.parsePadUrl(data.url);
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel); var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>'; var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>';
editor.replaceSelection(mt); editor.replaceSelection(mt);

@ -1,308 +0,0 @@
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 (ifrw, Cryptpad, defaultMode, CMeditor) {
var exp = {};
var Messages = Cryptpad.Messages;
var CodeMirror = exp.CodeMirror = CMeditor;
CodeMirror.modeURL = "cm/mode/%N/%N";
var $pad = $('#pad-iframe');
var $textarea = exp.$textarea = $('#editor1');
if (!$textarea.length) { $textarea = exp.$textarea = $pad.contents().find('#editor1'); }
var Title;
var onLocal = function () {};
var $rightside;
var $drawer;
exp.init = function (local, title, toolbar) {
if (typeof local === "function") {
onLocal = local;
}
Title = title;
$rightside = toolbar.$rightside;
$drawer = toolbar.$drawer;
};
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: defaultMode || "javascript",
readOnly: true
});
editor.setValue(Messages.codeInitialState);
var setMode = exp.setMode = function (mode, cb) {
exp.highlightMode = mode;
if (mode !== "text") {
CMeditor.autoLoadMode(editor, mode);
}
editor.setOption('mode', mode);
if (exp.$language) {
var name = exp.$language.find('a[data-value="' + mode + '"]').text() || undefined;
name = name ? Messages.languageButton + ' ('+name+')' : Messages.languageButton;
exp.$language.setValue(mode, name);
}
if(cb) { cb(mode); }
};
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) {
var name = theme || undefined;
name = name ? Messages.themeButton + ' ('+theme+')' : Messages.themeButton;
$select.setValue(theme, name);
}
};
}());
exp.getHeadingText = function () {
var lines = editor.getValue().split(/\n/);
var text = '';
lines.some(function (line) {
// 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;
}
// 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;
}
// TODO make one more pass for multiline comments
});
return text.trim();
};
exp.configureLanguage = function (cb, onModeChanged) {
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,
feedback: 'CODE_LANGUAGE',
};
var $block = exp.$language = Cryptpad.createDropdown(dropdownConfig);
$block.find('button').attr('title', Messages.languageButtonTitle);
$block.find('a').click(function () {
setMode($(this).attr('data-value'), onModeChanged);
onLocal();
});
if ($drawer) { $drawer.append($block); }
if (cb) { cb(); }
};
exp.configureTheme = function (cb) {
/* 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,
feedback: 'CODE_THEME',
};
var $block = exp.$theme = Cryptpad.createDropdown(dropdownConfig);
$block.find('button').attr('title', Messages.themeButtonTitle);
setTheme(lastTheme, $block);
$block.find('a').click(function () {
var theme = $(this).attr('data-value');
setTheme(theme, $block);
localStorage.setItem(themeKey, theme);
});
if ($drawer) { $drawer.append($block); }
if (cb) { cb(); }
};
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]);
mode = mode && mode.mode || null;
}
} 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;
});

@ -1,366 +0,0 @@
define([
'jquery',
'/file/file-crypto.js',
'/common/common-thumbnail.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
], function ($, FileCrypto, Thumb) {
var Nacl = window.nacl;
var module = {};
var blobToArrayBuffer = module.blobToArrayBuffer = function (blob, cb) {
var reader = new FileReader();
reader.onloadend = function () {
cb(void 0, this.result);
};
reader.readAsArrayBuffer(blob);
};
var arrayBufferToString = function (AB) {
try {
return Nacl.util.encodeBase64(new Uint8Array(AB));
} catch (e) {
console.error(e);
return null;
}
};
module.upload = function (file, noStore, common, updateProgress, onComplete, onError, onPending) {
var u8 = file.blob; // This is not a blob but a uint8array
var metadata = file.metadata;
// if it exists, path contains the new pad location in the drive
var path = file.path;
var key = Nacl.randomBytes(32);
var next = FileCrypto.encrypt(u8, metadata, key);
var estimate = FileCrypto.computeEncryptedSize(u8.length, metadata);
var sendChunk = function (box, cb) {
var enc = Nacl.util.encodeBase64(box);
common.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) {
cb(e, msg);
});
};
var actual = 0;
var again = function (err, box) {
if (err) { throw new Error(err); }
if (box) {
actual += box.length;
var progressValue = (actual / estimate * 100);
updateProgress(progressValue);
return void sendChunk(box, function (e) {
if (e) { return console.error(e); }
next(again);
});
}
if (actual !== estimate) {
console.error('Estimated size does not match actual size');
}
// if not box then done
common.uploadComplete(function (e, id) {
if (e) { return void console.error(e); }
var uri = ['', 'blob', id.slice(0,2), id].join('/');
console.log("encrypted blob is now available as %s", uri);
var b64Key = Nacl.util.encodeBase64(key);
var hash = common.getFileHashFromKeys(id, b64Key);
var href = '/file/#' + hash;
var title = metadata.name;
if (noStore) { return void onComplete(href); }
common.initialPath = path;
common.renamePad(title || "", href, function (err) {
if (err) { return void console.error(err); }
onComplete(href);
common.setPadAttribute('fileType', metadata.type, null, href);
});
});
};
common.uploadStatus(estimate, function (e, pending) {
if (e) {
console.error(e);
onError(e);
return;
}
if (pending) {
return void onPending(function () {
// if the user wants to cancel the pending upload to execute that one
common.uploadCancel(function (e, res) {
if (e) {
return void console.error(e);
}
console.log(res);
next(again);
});
});
}
next(again);
});
};
module.create = function (common, config) {
var File = {};
var Messages = common.Messages;
var queue = File.queue = {
queue: [],
inProgress: false
};
var uid = function () {
return 'file-' + String(Math.random()).substring(2);
};
var $table = File.$table = $('<table>', { id: 'uploadStatus' });
var $thead = $('<tr>').appendTo($table);
$('<td>').text(Messages.upload_name).appendTo($thead);
$('<td>').text(Messages.upload_size).appendTo($thead);
$('<td>').text(Messages.upload_progress).appendTo($thead);
$('<td>').text(Messages.cancel).appendTo($thead);
var createTableContainer = function ($body) {
File.$container = $('<div>', { id: 'uploadStatusContainer' }).append($table).appendTo($body);
return File.$container;
};
var getData = function (file, href) {
var data = {};
data.name = file.metadata.name;
data.url = href;
if (file.metadata.type.slice(0,6) === 'image/') {
data.mediatag = true;
}
return data;
};
var upload = function (file) {
var blob = file.blob; // This is not a blob but an array buffer
var u8 = new Uint8Array(blob);
var metadata = file.metadata;
var id = file.id;
if (queue.inProgress) { return; }
queue.inProgress = true;
var $row = $table.find('tr[id="'+id+'"]');
$row.find('.upCancel').html('-');
var $pv = $row.find('.progressValue');
var $pb = $row.find('.progressContainer');
var $pc = $row.find('.upProgress');
var $link = $row.find('.upLink');
var updateProgress = function (progressValue) {
$pv.text(Math.round(progressValue*100)/100 + '%');
$pb.css({
width: (progressValue/100)*$pc.width()+'px'
});
};
var onComplete = function (href) {
$link.attr('href', href)
.click(function (e) {
e.preventDefault();
window.open($link.attr('href'), '_blank');
});
var title = metadata.name;
common.log(Messages._getKey('upload_success', [title]));
common.prepareFeedback('upload')();
if (config.onUploaded) {
var data = getData(file, href);
config.onUploaded(file.dropEvent, data);
}
queue.inProgress = false;
queue.next();
};
var onError = function (e) {
queue.inProgress = false;
queue.next();
if (e === 'TOO_LARGE') {
// TODO update table to say too big?
return void common.alert(Messages.upload_tooLarge);
}
if (e === 'NOT_ENOUGH_SPACE') {
// TODO update table to say not enough space?
return void common.alert(Messages.upload_notEnoughSpace);
}
console.error(e);
return void common.alert(Messages.upload_serverError);
};
var onPending = function (cb) {
common.confirm(Messages.upload_uploadPending, function (yes) {
if (!yes) { return; }
cb();
});
};
file.blob = u8;
module.upload(file, config.noStore, common, updateProgress, onComplete, onError, onPending);
};
var prettySize = function (bytes) {
var kB = common.bytesToKilobytes(bytes);
if (kB < 1024) { return kB + Messages.KB; }
var mB = common.bytesToMegabytes(bytes);
return mB + Messages.MB;
};
queue.next = function () {
if (queue.queue.length === 0) {
queue.to = window.setTimeout(function () {
if (config.keepTable) { return; }
File.$container.fadeOut();
}, 3000);
return;
}
if (queue.inProgress) { return; }
File.$container.show();
var file = queue.queue.shift();
upload(file);
};
queue.push = function (obj) {
var id = uid();
obj.id = id;
queue.queue.push(obj);
$table.show();
var estimate = FileCrypto.computeEncryptedSize(obj.blob.byteLength, obj.metadata);
var $progressBar = $('<div>', {'class':'progressContainer'});
var $progressValue = $('<span>', {'class':'progressValue'}).text(Messages.upload_pending);
var $tr = $('<tr>', {id: id}).appendTo($table);
var $cancel = $('<span>', {'class': 'cancel fa fa-times'}).click(function () {
queue.queue = queue.queue.filter(function (el) { return el.id !== id; });
$cancel.remove();
$tr.find('.upCancel').text('-');
$tr.find('.progressValue').text(Messages.upload_cancelled);
});
var $link = $('<a>', {
'class': 'upLink',
'rel': 'noopener noreferrer'
}).text(obj.metadata.name);
$('<td>').append($link).appendTo($tr);
$('<td>').text(prettySize(estimate)).appendTo($tr);
$('<td>', {'class': 'upProgress'}).append($progressBar).append($progressValue).appendTo($tr);
$('<td>', {'class': 'upCancel'}).append($cancel).appendTo($tr);
queue.next();
};
var handleFile = File.handleFile = function (file, e, thumbnail) {
var thumb;
var file_arraybuffer;
var finish = function () {
var metadata = {
name: file.name,
type: file.type,
};
if (thumb) { metadata.thumbnail = thumb; }
queue.push({
blob: file_arraybuffer,
metadata: metadata,
dropEvent: e
});
};
blobToArrayBuffer(file, function (e, buffer) {
if (e) { console.error(e); }
file_arraybuffer = buffer;
if (thumbnail) { // there is already a thumbnail
return blobToArrayBuffer(thumbnail, function (e, buffer) {
if (e) { console.error(e); }
thumb = arrayBufferToString(buffer);
finish();
});
}
if (!Thumb.isSupportedType(file.type)) { return finish(); }
// make a resized thumbnail from the image..
Thumb.fromBlob(file, function (e, thumb64) {
if (e) { console.error(e); }
if (!thumb64) { return finish(); }
thumb = thumb64;
finish();
});
});
};
var onFileDrop = File.onFileDrop = function (file, e) {
if (!common.isLoggedIn()) {
return common.alert(common.Messages.upload_mustLogin);
}
Array.prototype.slice.call(file).forEach(function (d) {
handleFile(d, e);
});
};
var createAreaHandlers = File.createDropArea = function ($area, $hoverArea) {
var counter = 0;
if (!$hoverArea) { $hoverArea = $area; }
if (!$area) { return; }
$hoverArea
.on('dragenter', function (e) {
e.preventDefault();
e.stopPropagation();
counter++;
$hoverArea.addClass('hovering');
})
.on('dragleave', function (e) {
e.preventDefault();
e.stopPropagation();
counter--;
if (counter <= 0) {
$hoverArea.removeClass('hovering');
}
});
$area
.on('drag dragstart dragend dragover drop dragenter dragleave', function (e) {
e.preventDefault();
e.stopPropagation();
})
.on('drop', function (e) {
e.stopPropagation();
var dropped = e.originalEvent.dataTransfer.files;
counter = 0;
$hoverArea.removeClass('hovering');
onFileDrop(dropped, e);
});
};
var createUploader = function ($area, $hover, $body) {
if (!config.noHandlers) {
createAreaHandlers($area, null);
}
createTableContainer($body);
};
createUploader(config.dropArea, config.hoverArea, config.body);
return File;
};
return module;
});

@ -1,9 +1,8 @@
define([ define([
'/common/common-util.js', '/common/common-util.js',
'/common/common-interface.js',
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/bower_components/tweetnacl/nacl-fast.min.js' '/bower_components/tweetnacl/nacl-fast.min.js'
], function (Util, UI, Crypto) { ], function (Util, Crypto) {
var Nacl = window.nacl; var Nacl = window.nacl;
var Hash = {}; var Hash = {};
@ -35,8 +34,8 @@ define([
var getFileHashFromKeys = Hash.getFileHashFromKeys = function (fileKey, cryptKey) { var getFileHashFromKeys = Hash.getFileHashFromKeys = function (fileKey, cryptKey) {
return '/1/' + hexToBase64(fileKey) + '/' + Crypto.b64RemoveSlashes(cryptKey) + '/'; return '/1/' + hexToBase64(fileKey) + '/' + Crypto.b64RemoveSlashes(cryptKey) + '/';
}; };
Hash.getUserHrefFromKeys = function (username, pubkey) { Hash.getUserHrefFromKeys = function (origin, username, pubkey) {
return window.location.origin + '/user/#/1/' + username + '/' + pubkey.replace(/\//g, '-'); return origin + '/user/#/1/' + username + '/' + pubkey.replace(/\//g, '-');
}; };
var fixDuplicateSlashes = function (s) { var fixDuplicateSlashes = function (s) {
@ -212,14 +211,12 @@ Version 1
secret.keys = Crypto.createEditCryptor(parsed.key); secret.keys = Crypto.createEditCryptor(parsed.key);
secret.key = secret.keys.editKeyStr; secret.key = secret.keys.editKeyStr;
if (secret.channel.length !== 32 || secret.key.length !== 24) { if (secret.channel.length !== 32 || secret.key.length !== 24) {
UI.alert("The channel key and/or the encryption key is invalid");
throw new Error("The channel key and/or the encryption key is invalid"); throw new Error("The channel key and/or the encryption key is invalid");
} }
} }
else if (parsed.mode === 'view') { else if (parsed.mode === 'view') {
secret.keys = Crypto.createViewCryptor(parsed.key); secret.keys = Crypto.createViewCryptor(parsed.key);
if (secret.channel.length !== 32) { if (secret.channel.length !== 32) {
UI.alert("The channel key is invalid");
throw new Error("The channel key is invalid"); throw new Error("The channel key is invalid");
} }
} }

@ -1,268 +0,0 @@
define([
'jquery',
'/bower_components/chainpad-json-validator/json-ot.js',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/chainpad/chainpad.dist.js',
], function ($, JsonOT, Crypto) {
var ChainPad = window.ChainPad;
var History = {};
var getStates = function (rt) {
var states = [];
var b = rt.getAuthBlock();
if (b) { states.unshift(b); }
while (b.getParent()) {
b = b.getParent();
states.unshift(b);
}
return states;
};
var loadHistory = function (config, common, cb) {
var network = common.getNetwork();
var hkn = network.historyKeeper;
var wcId = common.hrefToHexChannelId(config.href || window.location.href);
var createRealtime = function () {
return ChainPad.create({
userName: 'history',
initialState: '',
transformFunction: JsonOT.validate,
logLevel: 0,
noPrune: true
});
};
var realtime = createRealtime();
var parsed = config.href ? common.parsePadUrl(config.href) : {};
var secret = common.getSecrets(parsed.type, parsed.hash);
History.readOnly = 0;
if (!secret.keys) {
secret.keys = secret.key;
History.readOnly = 0;
}
else if (!secret.keys.validateKey) {
History.readOnly = 1;
}
var crypto = Crypto.createEncryptor(secret.keys);
var to = window.setTimeout(function () {
cb('[GET_FULL_HISTORY_TIMEOUT]');
}, 30000);
var parse = function (msg) {
try {
return JSON.parse(msg);
} catch (e) {
return null;
}
};
var onMsg = function (msg) {
var parsed = parse(msg);
if (parsed[0] === 'FULL_HISTORY_END') {
console.log('END');
window.clearTimeout(to);
cb(null, realtime);
return;
}
if (parsed[0] !== 'FULL_HISTORY') { return; }
if (parsed[1] && parsed[1].validateKey) { // First message
secret.keys.validateKey = parsed[1].validateKey;
return;
}
msg = parsed[1][4];
if (msg) {
msg = msg.replace(/^cp\|/, '');
var decryptedMsg = crypto.decrypt(msg, secret.keys.validateKey);
realtime.message(decryptedMsg);
}
};
network.on('message', function (msg) {
onMsg(msg);
});
network.sendto(hkn, JSON.stringify(['GET_FULL_HISTORY', wcId, secret.keys.validateKey]));
};
History.create = function (common, config) {
if (!config.$toolbar) { return void console.error("config.$toolbar is undefined");}
if (History.loading) { return void console.error("History is already being loaded..."); }
History.loading = true;
var $toolbar = config.$toolbar;
if (!config.applyVal || !config.setHistory || !config.onLocal || !config.onRemote) {
throw new Error("Missing config element: applyVal, onLocal, onRemote, setHistory");
}
// config.setHistory(bool, bool)
// - bool1: history value
// - bool2: reset old content?
var render = function (val) {
if (typeof val === "undefined") { return; }
try {
config.applyVal(val);
} catch (e) {
// Probably a parse error
console.error(e);
}
};
var onClose = function () { config.setHistory(false, true); };
var onRevert = function () {
config.setHistory(false, false);
config.onLocal();
config.onRemote();
};
var onReady = function () {
config.setHistory(true);
};
var Messages = common.Messages;
var realtime;
var states = [];
var c = states.length - 1;
var $hist = $toolbar.find('.cryptpad-toolbar-history');
var $left = $toolbar.find('.cryptpad-toolbar-leftside');
var $right = $toolbar.find('.cryptpad-toolbar-rightside');
var $cke = $toolbar.find('.cke_toolbox_main');
$hist.html('').show();
$left.hide();
$right.hide();
$cke.hide();
common.spinner($hist).get().show();
var onUpdate;
var update = function () {
if (!realtime) { return []; }
states = getStates(realtime);
if (typeof onUpdate === "function") { onUpdate(); }
return states;
};
// Get the content of the selected version, and change the version number
var get = function (i) {
i = parseInt(i);
if (isNaN(i)) { return; }
if (i < 0) { i = 0; }
if (i > states.length - 1) { i = states.length - 1; }
var val = states[i].getContent().doc;
c = i;
if (typeof onUpdate === "function") { onUpdate(); }
$hist.find('.next, .previous').css('visibility', '');
if (c === states.length - 1) { $hist.find('.next').css('visibility', 'hidden'); }
if (c === 0) { $hist.find('.previous').css('visibility', 'hidden'); }
return val || '';
};
var getNext = function (step) {
return typeof step === "number" ? get(c + step) : get(c + 1);
};
var getPrevious = function (step) {
return typeof step === "number" ? get(c - step) : get(c - 1);
};
// Create the history toolbar
var display = function () {
$hist.html('');
var $prev =$('<button>', {
'class': 'previous fa fa-step-backward buttonPrimary',
title: Messages.history_prev
}).appendTo($hist);
var $nav = $('<div>', {'class': 'goto'}).appendTo($hist);
var $next = $('<button>', {
'class': 'next fa fa-step-forward buttonPrimary',
title: Messages.history_next
}).appendTo($hist);
$('<label>').text(Messages.history_version).appendTo($nav);
var $cur = $('<input>', {
'class' : 'gotoInput',
'type' : 'number',
'min' : '1',
'max' : states.length
}).val(c + 1).appendTo($nav).mousedown(function (e) {
// stopPropagation because the event would be cancelled by the dropdown menus
e.stopPropagation();
});
var $label2 = $('<label>').text(' / '+ states.length).appendTo($nav);
$('<br>').appendTo($nav);
var $close = $('<button>', {
'class':'closeHistory',
title: Messages.history_closeTitle
}).text(Messages.history_closeTitle).appendTo($nav);
var $rev = $('<button>', {
'class':'revertHistory buttonSuccess',
title: Messages.history_restoreTitle
}).text(Messages.history_restore).appendTo($nav);
if (History.readOnly) { $rev.hide(); }
onUpdate = function () {
$cur.attr('max', states.length);
$cur.val(c+1);
$label2.text(' / ' + states.length);
};
var close = function () {
$hist.hide();
$left.show();
$right.show();
$cke.show();
};
// Buttons actions
$prev.click(function () { render(getPrevious()); });
$next.click(function () { render(getNext()); });
$cur.keydown(function (e) {
var p = function () { e.preventDefault(); };
if (e.which === 13) { p(); return render( get($cur.val() - 1) ); } // Enter
if ([37, 40].indexOf(e.which) >= 0) { p(); return render(getPrevious()); } // Left
if ([38, 39].indexOf(e.which) >= 0) { p(); return render(getNext()); } // Right
if (e.which === 33) { p(); return render(getNext(10)); } // PageUp
if (e.which === 34) { p(); return render(getPrevious(10)); } // PageUp
if (e.which === 27) { p(); $close.click(); }
}).keyup(function (e) { e.stopPropagation(); }).focus();
$cur.on('change', function () {
render( get($cur.val() - 1) );
});
$close.click(function () {
states = [];
close();
onClose();
});
$rev.click(function () {
common.confirm(Messages.history_restorePrompt, function (yes) {
if (!yes) { return; }
close();
onRevert();
common.log(Messages.history_restoreDone);
});
});
// Display the latest content
render(get(c));
};
// Load all the history messages into a new chainpad object
loadHistory(config, common, function (err, newRt) {
History.loading = false;
if (err) { throw new Error(err); }
realtime = newRt;
update();
c = states.length - 1;
display();
onReady();
});
};
return History;
});

@ -2,16 +2,17 @@ define([
'jquery', 'jquery',
'/customize/messages.js', '/customize/messages.js',
'/common/common-util.js', '/common/common-util.js',
'/common/common-hash.js',
'/common/common-notifier.js',
'/customize/application_config.js', '/customize/application_config.js',
'/bower_components/alertifyjs/dist/js/alertify.js', '/bower_components/alertifyjs/dist/js/alertify.js',
'/common/notify.js',
'/common/visible.js',
'/common/tippy.min.js', '/common/tippy.min.js',
'/customize/pages.js', '/customize/pages.js',
'/common/hyperscript.js', '/common/hyperscript.js',
'/bower_components/bootstrap-tokenfield/dist/bootstrap-tokenfield.js', '/bower_components/bootstrap-tokenfield/dist/bootstrap-tokenfield.js',
'css!/common/tippy.css', 'css!/common/tippy.css',
], function ($, Messages, Util, AppConfig, Alertify, Notify, Visible, Tippy, Pages, h) { ], function ($, Messages, Util, Hash, Notifier, AppConfig,
Alertify, Tippy, Pages, h) {
var UI = {}; var UI = {};
/* /*
@ -270,7 +271,7 @@ define([
document.body.appendChild(frame); document.body.appendChild(frame);
setTimeout(function () { setTimeout(function () {
$ok.focus(); $ok.focus();
UI.notify(); Notifier.notify();
}); });
}; };
@ -318,7 +319,7 @@ define([
document.body.appendChild(frame); document.body.appendChild(frame);
setTimeout(function () { setTimeout(function () {
$(input).select().focus(); $(input).select().focus();
UI.notify(); Notifier.notify();
}); });
}; };
@ -365,7 +366,7 @@ define([
document.body.appendChild(frame); document.body.appendChild(frame);
setTimeout(function () { setTimeout(function () {
UI.notify(); Notifier.notify();
$(frame).find('.ok').focus(); $(frame).find('.ok').focus();
if (typeof(opt.done) === 'function') { if (typeof(opt.done) === 'function') {
opt.done($ok.closest('.dialog')); opt.done($ok.closest('.dialog'));
@ -468,44 +469,6 @@ 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, cfg) {
return function () {
var $files = $('<input>', {type:"file"});
if (cfg && cfg.accept) {
$files.attr('accept', cfg.accept);
}
$files.click();
$files.on('change', function (e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function (e) { f(e.target.result, file); };
reader.readAsText(file, type);
});
};
};
var $defaultIcon = $('<span>', {"class": "fa fa-file-text-o"}); var $defaultIcon = $('<span>', {"class": "fa fa-file-text-o"});
UI.getIcon = function (type) { UI.getIcon = function (type) {
var $icon = $defaultIcon.clone(); var $icon = $defaultIcon.clone();
@ -517,6 +480,17 @@ define([
return $icon; return $icon;
}; };
UI.getFileIcon = function (data) {
var $icon = UI.getIcon();
if (!data) { return $icon; }
var href = data.href;
if (!href) { return $icon; }
var type = Hash.parsePadUrl(href).type;
$icon = UI.getIcon(type);
return $icon;
};
// Tooltips // Tooltips
@ -524,11 +498,8 @@ define([
// If an element is removed from the UI while a tooltip is applied on that element, the tooltip will get hung // If an element is removed from the UI while a tooltip is applied on that element, the tooltip will get hung
// forever, this is a solution which just searches for tooltips which have no corrisponding element and removes // forever, this is a solution which just searches for tooltips which have no corrisponding element and removes
// them. // them.
var win;
$('.tippy-popper').each(function (i, el) { $('.tippy-popper').each(function (i, el) {
win = win || $('#pad-iframe').length? $('#pad-iframe')[0].contentWindow: undefined; if ($('[aria-describedby=' + el.getAttribute('id') + ']').length === 0) {
if (!win) { return; }
if (win.$('[aria-describedby=' + el.getAttribute('id') + ']').length === 0) {
el.remove(); el.remove();
} }
}); });
@ -536,52 +507,54 @@ define([
UI.addTooltips = function () { UI.addTooltips = function () {
var MutationObserver = window.MutationObserver; var MutationObserver = window.MutationObserver;
var addTippy = function (el) {
if (el.nodeName === 'IFRAME') { return; }
var delay = typeof(AppConfig.tooltipDelay) === "number" ? AppConfig.tooltipDelay : 500; var delay = typeof(AppConfig.tooltipDelay) === "number" ? AppConfig.tooltipDelay : 500;
var addTippy = function (i, el) {
if (el.nodeName === 'IFRAME') { return; }
Tippy(el, { Tippy(el, {
position: 'bottom', position: 'bottom',
distance: 0, distance: 0,
performance: true, performance: true,
dynamicTitle: true, dynamicTitle: true,
delay: [delay, 0] delay: [delay, 0],
sticky: true
}); });
}; };
var $body = $('body'); // This is the robust solution to remove dangling tooltips
var $padIframe = $('#pad-iframe').contents().find('body'); // The mutation observer does not always find removed nodes.
$('[title]').each(function (i, el) { setInterval(UI.clearTooltips, delay);
addTippy(el); var checkRemoved = function (x) {
}); var out = false;
$('#pad-iframe').contents().find('[title]').each(function (i, el) { $(x).find('[aria-describedby]').each(function (i, el) {
addTippy(el); var id = el.getAttribute('aria-describedby');
if (id.indexOf('tippy-tooltip-') !== 0) { return; }
out = true;
}); });
return out;
};
$('[title]').each(addTippy);
var observer = new MutationObserver(function(mutations) { var observer = new MutationObserver(function(mutations) {
var removed = false;
mutations.forEach(function(mutation) { mutations.forEach(function(mutation) {
if (mutation.type === 'childList' && mutation.addedNodes.length) { if (mutation.type === "childList") {
$body.find('[title]').each(function (i, el) { for (var i = 0; i < mutation.addedNodes.length; i++) {
addTippy(el); $(mutation.addedNodes[i]).find('[title]').each(addTippy);
}); }
if (!$padIframe.length) { return; } for (var j = 0; j < mutation.removedNodes.length; j++) {
$padIframe.find('[title]').each(function (i, el) { removed |= checkRemoved(mutation.removedNodes[j]);
addTippy(el); }
}); }
if (mutation.type === "attributes" && mutation.attributeName === "title") {
addTippy(0, mutation.target);
} }
}); });
if (removed) { UI.clearTooltips(); }
}); });
observer.observe($('body')[0], { observer.observe($('body')[0], {
attributes: false, attributes: true,
childList: true,
characterData: false,
subtree: true
});
if ($('#pad-iframe').length) {
observer.observe($('#pad-iframe').contents().find('body')[0], {
attributes: false,
childList: true, childList: true,
characterData: false, characterData: false,
subtree: true subtree: true
}); });
}
}; };
return UI; return UI;

@ -3,10 +3,12 @@ define([
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/common/curve.js', '/common/curve.js',
'/common/common-hash.js', '/common/common-hash.js',
'/common/common-util.js',
'/customize/messages.js',
'/bower_components/marked/marked.min.js', '/bower_components/marked/marked.min.js',
'/common/common-realtime.js', '/common/common-realtime.js',
], function ($, Crypto, Curve, Hash, Marked, Realtime) { ], function ($, Crypto, Curve, Hash, Util, Messages, Marked, Realtime) {
var Msg = { var Msg = {
inputs: [], inputs: [],
}; };
@ -80,7 +82,7 @@ define([
friends[pubKey] = data; friends[pubKey] = data;
Realtime.whenRealtimeSyncs(common, common.getRealtime(), function () { Realtime.whenRealtimeSyncs(common.getRealtime(), function () {
cb(); cb();
common.pinPads([data.channel], function (e) { common.pinPads([data.channel], function (e) {
if (e) { console.error(e); } if (e) { console.error(e); }
@ -98,7 +100,7 @@ define([
var msg; var msg;
if (sender === network.historyKeeper) { return; } if (sender === network.historyKeeper) { return; }
try { try {
var parsed = common.parsePadUrl(window.location.href); var parsed = Hash.parsePadUrl(window.location.href);
if (!parsed.hashData) { return; } if (!parsed.hashData) { return; }
var chan = parsed.hashData.channel; var chan = parsed.hashData.channel;
// Decrypt // Decrypt
@ -132,11 +134,11 @@ define([
todo(true); todo(true);
return; return;
} }
var confirmMsg = common.Messages._getKey('contacts_request', [ var confirmMsg = Messages._getKey('contacts_request', [
common.fixHTML(msgData.displayName) Util.fixHTML(msgData.displayName)
]); ]);
common.onFriendRequest(confirmMsg, todo); common.onFriendRequest(confirmMsg, todo);
//common.confirm(confirmMsg, todo, null, true); //UI.confirm(confirmMsg, todo, null, true);
return; return;
} }
if (msg[0] === "FRIEND_REQ_OK") { if (msg[0] === "FRIEND_REQ_OK") {
@ -147,12 +149,12 @@ define([
addToFriendList(common, msgData, function (err) { addToFriendList(common, msgData, function (err) {
if (err) { if (err) {
return void common.onFriendComplete({ return void common.onFriendComplete({
logText: common.Messages.contacts_addError, logText: Messages.contacts_addError,
netfluxId: sender netfluxId: sender
}); });
} }
common.onFriendComplete({ common.onFriendComplete({
logText: common.Messages.contacts_added, logText: Messages.contacts_added,
netfluxId: sender netfluxId: sender
}); });
var msg = ["FRIEND_REQ_ACK", chan]; var msg = ["FRIEND_REQ_ACK", chan];
@ -165,7 +167,7 @@ define([
var i = pendingRequests.indexOf(sender); var i = pendingRequests.indexOf(sender);
if (i !== -1) { pendingRequests.splice(i, 1); } if (i !== -1) { pendingRequests.splice(i, 1); }
common.onFriendComplete({ common.onFriendComplete({
logText: common.Messages.contacts_rejected, logText: Messages.contacts_rejected,
netfluxId: sender netfluxId: sender
}); });
common.changeDisplayName(proxy[common.displayNameKey]); common.changeDisplayName(proxy[common.displayNameKey]);
@ -177,12 +179,12 @@ define([
addToFriendList(common, data, function (err) { addToFriendList(common, data, function (err) {
if (err) { if (err) {
return void common.onFriendComplete({ return void common.onFriendComplete({
logText: common.Messages.contacts_addError, logText: Messages.contacts_addError,
netfluxId: sender netfluxId: sender
}); });
} }
common.onFriendComplete({ common.onFriendComplete({
logText: common.Messages.contacts_added, logText: Messages.contacts_added,
netfluxId: sender netfluxId: sender
}); });
}); });
@ -201,7 +203,7 @@ define([
Msg.inviteFromUserlist = function (common, netfluxId) { Msg.inviteFromUserlist = function (common, netfluxId) {
var network = common.getNetwork(); var network = common.getNetwork();
var parsed = common.parsePadUrl(window.location.href); var parsed = Hash.parsePadUrl(window.location.href);
if (!parsed.hashData) { return; } if (!parsed.hashData) { return; }
// Message // Message
var chan = parsed.hashData.channel; var chan = parsed.hashData.channel;

@ -3,7 +3,9 @@ define([
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/common/curve.js', '/common/curve.js',
'/common/common-hash.js', '/common/common-hash.js',
], function ($, Crypto, Curve, Hash) { '/common/common-util.js',
'/common/common-realtime.js',
], function ($, Crypto, Curve, Hash, Util, Realtime) {
'use strict'; 'use strict';
var Msg = { var Msg = {
inputs: [], inputs: [],
@ -48,21 +50,6 @@ define([
return proxy.friends; return proxy.friends;
}; };
var eachFriend = function (friends, cb) {
Object.keys(friends).forEach(function (id) {
if (id === 'me') { return; }
cb(friends[id], id, friends);
});
};
Msg.getFriendChannelsList = function (proxy) {
var list = [];
eachFriend(proxy, function (friend) {
list.push(friend.channel);
});
return list;
};
var msgAlreadyKnown = function (channel, sig) { var msgAlreadyKnown = function (channel, sig) {
return channel.messages.some(function (message) { return channel.messages.some(function (message) {
return message[0] === sig; return message[0] === sig;
@ -149,7 +136,7 @@ define([
return; return;
} }
var txid = common.uid(); var txid = Util.uid();
initRangeRequest(txid, curvePublic, hash, cb); initRangeRequest(txid, curvePublic, hash, cb);
var msg = [ 'GET_HISTORY_RANGE', chan.id, { var msg = [ 'GET_HISTORY_RANGE', chan.id, {
from: hash, from: hash,
@ -245,7 +232,7 @@ define([
if (!proxy.friends) { return; } if (!proxy.friends) { return; }
var friends = proxy.friends; var friends = proxy.friends;
delete friends[curvePublic]; delete friends[curvePublic];
common.whenRealtimeSyncs(realtime, cb); Realtime.whenRealtimeSyncs(realtime, cb);
}; };
var pushMsg = function (channel, cryptMsg) { var pushMsg = function (channel, cryptMsg) {
@ -352,7 +339,7 @@ define([
return cb(); return cb();
}; };
var onDirectMessage = function (common, msg, sender) { var onDirectMessage = function (msg, sender) {
if (sender !== Msg.hk) { return void onIdMessage(msg, sender); } if (sender !== Msg.hk) { return void onIdMessage(msg, sender); }
var parsed = JSON.parse(msg); var parsed = JSON.parse(msg);
@ -443,7 +430,7 @@ define([
// listen for messages... // listen for messages...
network.on('message', function(msg, sender) { network.on('message', function(msg, sender) {
onDirectMessage(common, msg, sender); onDirectMessage(msg, sender);
}); });
messenger.removeFriend = function (curvePublic, cb) { messenger.removeFriend = function (curvePublic, cb) {
@ -476,7 +463,7 @@ define([
channel.wc.bcast(cryptMsg).then(function () { channel.wc.bcast(cryptMsg).then(function () {
delete friends[curvePublic]; delete friends[curvePublic];
delete channels[curvePublic]; delete channels[curvePublic];
common.whenRealtimeSyncs(realtime, function () { Realtime.whenRealtimeSyncs(realtime, function () {
cb(); cb();
}); });
}, function (err) { }, function (err) {

@ -1,59 +0,0 @@
define(function () {
var module = {};
module.create = function (UserList, Title, cfg, Cryptpad) {
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 (Cryptpad) {
if (typeof(metadata.type) === 'undefined') {
// initialize pad type by location.pathname
metadata.type = Cryptpad.getAppType();
}
} else {
console.log("Cryptpad should exist but it does not");
}
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,29 @@
define([
'/common/visible.js',
'/common/notify.js'
], function (Visible, Notify) {
var Notifier = {};
var notify = {};
Notifier.unnotify = function () {
if (notify.tabNotification &&
typeof(notify.tabNotification.cancel) === 'function') {
notify.tabNotification.cancel();
}
};
Notifier.notify = function () {
if (Visible.isSupported() && !Visible.currently()) {
Notifier.unnotify();
notify.tabNotification = Notify.tab(1000, 10);
}
};
if (Visible.isSupported()) {
Visible.onChange(function (yes) {
if (yes) { Notifier.unnotify(); }
});
}
return Notifier;
});

@ -1,7 +1,8 @@
define([ define([
'/customize/application_config.js', '/customize/application_config.js',
'/customize/messages.js', '/customize/messages.js',
], function (AppConfig, Messages) { '/common/common-interface.js',
], function (AppConfig, Messages, UI) {
var common = {}; var common = {};
common.infiniteSpinnerDetected = false; common.infiniteSpinnerDetected = false;
@ -15,7 +16,7 @@ define([
/* /*
TODO make this not blow up when disconnected or lagging... TODO make this not blow up when disconnected or lagging...
*/ */
common.whenRealtimeSyncs = function (Cryptpad, realtime, cb) { common.whenRealtimeSyncs = function (realtime, cb) {
if (typeof(realtime.getAuthDoc) !== 'function') { if (typeof(realtime.getAuthDoc) !== 'function') {
return void console.error('improper use of this function'); return void console.error('improper use of this function');
} }
@ -28,7 +29,7 @@ define([
}, 0); }, 0);
}; };
common.beginDetectingInfiniteSpinner = function (Cryptpad, realtime) { common.beginDetectingInfiniteSpinner = function (realtime) {
if (intr) { return; } if (intr) { return; }
intr = window.setInterval(function () { intr = window.setInterval(function () {
var l; var l;
@ -44,7 +45,7 @@ define([
infiniteSpinnerHandlers.forEach(function (ish) { ish(); }); infiniteSpinnerHandlers.forEach(function (ish) { ish(); });
// inform the user their session is in a bad state // inform the user their session is in a bad state
Cryptpad.confirm(Messages.realtime_unrecoverableError, function (yes) { UI.confirm(Messages.realtime_unrecoverableError, function (yes) {
if (!yes) { return; } if (!yes) { return; }
window.parent.location.reload(); window.parent.location.reload();
}); });

@ -105,6 +105,7 @@ define([
var ctx = c2.getContext('2d'); var ctx = c2.getContext('2d');
ctx.drawImage(canvas, D.x, D.y, D.w, D.h); ctx.drawImage(canvas, D.x, D.y, D.w, D.h);
cb(void 0, c2.toDataURL()); cb(void 0, c2.toDataURL());
}; };
@ -124,19 +125,15 @@ define([
Thumb.fromVideoBlob = function (blob, cb) { Thumb.fromVideoBlob = function (blob, cb) {
var url = URL.createObjectURL(blob); var url = URL.createObjectURL(blob);
var video = document.createElement("VIDEO"); var video = document.createElement("VIDEO");
video.src = url;
video.addEventListener('loadedmetadata', function() {
video.currentTime = Number(Math.floor(Math.min(video.duration/10, 5)));
video.addEventListener('loadeddata', function() { video.addEventListener('loadeddata', function() {
var D = getResizedDimensions(video, 'video'); var D = getResizedDimensions(video, 'video');
Thumb.fromCanvas(video, D, cb); Thumb.fromCanvas(video, D, cb);
}); }, false);
});
video.addEventListener('error', function (e) { video.addEventListener('error', function (e) {
console.error(e); console.error(e);
cb('ERROR'); cb('ERROR');
}); });
video.src = url;
}; };
Thumb.fromPdfBlob = function (blob, cb) { Thumb.fromPdfBlob = function (blob, cb) {
require.config({paths: {'pdfjs-dist': '/common/pdfjs'}}); require.config({paths: {'pdfjs-dist': '/common/pdfjs'}});

@ -1,87 +0,0 @@
define(['jquery'], 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;
onLocal();
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);
onLocal();
};
// update title: href is optional; if not specified, we use window.location.href
exp.updateTitle = function (newTitle, href, cb) {
cb = cb || $.noop;
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, href, function (err, data) {
if (err) {
console.log("Couldn't set pad title");
console.error(err);
updateLocalTitle(oldTitle);
return void cb(err);
}
updateLocalTitle(data);
cb(null, 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;
});

@ -3,42 +3,26 @@ define([
'/api/config', '/api/config',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/common-util.js', '/common/common-util.js',
'/common/common-hash.js',
'/common/common-language.js', '/common/common-language.js',
'/common/common-interface.js',
'/common/media-tag.js', '/common/media-tag.js',
'/common/tippy.min.js',
'/customize/application_config.js',
'css!/common/tippy.css', 'css!/common/tippy.css',
], function ($, Config, Cryptpad, Util, Language, MediaTag, Tippy, AppConfig) { ], function ($, Config, Cryptpad, Util, Hash, Language, UI, MediaTag) {
var UI = {}; var UIElements = {};
var Messages = Cryptpad.Messages; var Messages = Cryptpad.Messages;
/** UIElements.updateTags = function (common, href) {
* Requirements from cryptpad-common.js
* getFileSize
* - hrefToHexChannelId
* displayAvatar
* - getFirstEmojiOrCharacter
* - parsePadUrl
* - getSecrets
* - base64ToHex
* - getBlobPathFromHex
* - bytesToMegabytes
* createUserAdminMenu
* - fixHTML
* - createDropdown
*/
UI.updateTags = function (common, href) {
var sframeChan = common.getSframeChannel(); var sframeChan = common.getSframeChannel();
sframeChan.query('Q_TAGS_GET', href || null, function (err, res) { sframeChan.query('Q_TAGS_GET', href || null, function (err, res) {
if (err || res.error) { if (err || res.error) {
if (res.error === 'NO_ENTRY') { if (res.error === 'NO_ENTRY') {
Cryptpad.alert(Messages.tags_noentry); UI.alert(Messages.tags_noentry);
} }
return void console.error(err || res.error); return void console.error(err || res.error);
} }
Cryptpad.dialog.tagPrompt(res.data, function (tags) { UI.dialog.tagPrompt(res.data, function (tags) {
if (!Array.isArray(tags)) { return; } if (!Array.isArray(tags)) { return; }
sframeChan.event('EV_TAGS_SET', { sframeChan.event('EV_TAGS_SET', {
tags: tags, tags: tags,
@ -48,7 +32,23 @@ define([
}); });
}; };
UI.createButton = function (common, type, rightside, data, callback) { var importContent = function (type, f, cfg) {
return function () {
var $files = $('<input>', {type:"file"});
if (cfg && cfg.accept) {
$files.attr('accept', cfg.accept);
}
$files.click();
$files.on('change', function (e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function (e) { f(e.target.result, file); };
reader.readAsText(file, type);
});
};
};
UIElements.createButton = function (common, type, rightside, data, callback) {
var AppConfig = common.getAppConfig(); var AppConfig = common.getAppConfig();
var button; var button;
var size = "17px"; var size = "17px";
@ -73,7 +73,7 @@ define([
if (callback) { if (callback) {
button button
.click(common.prepareFeedback(type)) .click(common.prepareFeedback(type))
.click(Cryptpad.importContent('text/plain', function (content, file) { .click(importContent('text/plain', function (content, file) {
callback(content, file); callback(content, file);
}, {accept: data ? data.accept : undefined})); }, {accept: data ? data.accept : undefined}));
} }
@ -93,7 +93,7 @@ define([
target: data.target target: data.target
}; };
if (data.filter && !data.filter(file)) { if (data.filter && !data.filter(file)) {
Cryptpad.log('Invalid avatar (type or size)'); UI.log('Invalid avatar (type or size)');
return; return;
} }
data.FM.handleFile(file, ev); data.FM.handleFile(file, ev);
@ -142,11 +142,11 @@ define([
title: title, title: title,
toSave: toSave toSave: toSave
}, function () { }, function () {
Cryptpad.alert(Messages.templateSaved); UI.alert(Messages.templateSaved);
common.feedback('TEMPLATE_CREATED'); common.feedback('TEMPLATE_CREATED');
}); });
}; };
Cryptpad.prompt(Messages.saveTemplatePrompt, title, todo); UI.prompt(Messages.saveTemplatePrompt, title, todo);
}); });
} }
break; break;
@ -162,12 +162,12 @@ define([
.click(common.prepareFeedback(type)) .click(common.prepareFeedback(type))
.click(function() { .click(function() {
var msg = common.isLoggedIn() ? Messages.forgetPrompt : Messages.fm_removePermanentlyDialog; var msg = common.isLoggedIn() ? Messages.forgetPrompt : Messages.fm_removePermanentlyDialog;
Cryptpad.confirm(msg, function (yes) { UI.confirm(msg, function (yes) {
if (!yes) { return; } if (!yes) { return; }
sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) { sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) {
if (err) { return void callback(err); } if (err) { return void callback(err); }
var cMsg = common.isLoggedIn() ? Messages.movedToTrash : Messages.deleted; var cMsg = common.isLoggedIn() ? Messages.movedToTrash : Messages.deleted;
Cryptpad.alert(cMsg, undefined, true); UI.alert(cMsg, undefined, true);
callback(); callback();
return; return;
}); });
@ -220,7 +220,7 @@ define([
title: Messages.tags_title, title: Messages.tags_title,
}) })
.click(common.prepareFeedback(type)) .click(common.prepareFeedback(type))
.click(function () { UI.updateTags(common, null); }); .click(function () { UIElements.updateTags(common, null); });
break; break;
default: default:
button = $('<button>', { button = $('<button>', {
@ -236,7 +236,7 @@ define([
}; };
// Avatars // Avatars
UI.displayMediatagImage = function (Common, $tag, cb) { UIElements.displayMediatagImage = function (Common, $tag, cb) {
if (!$tag.length || !$tag.is('media-tag')) { return void cb('NOT_MEDIATAG'); } if (!$tag.length || !$tag.is('media-tag')) { return void cb('NOT_MEDIATAG'); }
var observer = new MutationObserver(function(mutations) { var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) { mutations.forEach(function(mutation) {
@ -267,31 +267,52 @@ define([
}); });
MediaTag($tag[0]); MediaTag($tag[0]);
}; };
UI.displayAvatar = function (Common, $container, href, name, cb) {
var emoji_patt = /([\uD800-\uDBFF][\uDC00-\uDFFF])/;
var isEmoji = function (str) {
return emoji_patt.test(str);
};
var emojiStringToArray = function (str) {
var split = str.split(emoji_patt);
var arr = [];
for (var i=0; i<split.length; i++) {
var char = split[i];
if (char !== "") {
arr.push(char);
}
}
return arr;
};
var getFirstEmojiOrCharacter = function (str) {
if (!str || !str.trim()) { return '?'; }
var emojis = emojiStringToArray(str);
return isEmoji(emojis[0])? emojis[0]: str[0];
};
UIElements.displayAvatar = function (Common, $container, href, name, cb) {
var displayDefault = function () { var displayDefault = function () {
var text = Cryptpad.getFirstEmojiOrCharacter(name); var text = getFirstEmojiOrCharacter(name);
var $avatar = $('<span>', {'class': 'cp-avatar-default'}).text(text); var $avatar = $('<span>', {'class': 'cp-avatar-default'}).text(text);
$container.append($avatar); $container.append($avatar);
if (cb) { cb(); } if (cb) { cb(); }
}; };
if (!href) { return void displayDefault(); } if (!href) { return void displayDefault(); }
var parsed = Cryptpad.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
var secret = Cryptpad.getSecrets('file', parsed.hash); var secret = Hash.getSecrets('file', parsed.hash);
if (secret.keys && secret.channel) { if (secret.keys && secret.channel) {
var cryptKey = secret.keys && secret.keys.fileKeyStr; var cryptKey = secret.keys && secret.keys.fileKeyStr;
var hexFileName = Cryptpad.base64ToHex(secret.channel); var hexFileName = Util.base64ToHex(secret.channel);
var src = Cryptpad.getBlobPathFromHex(hexFileName); var src = Hash.getBlobPathFromHex(hexFileName);
Common.getFileSize(href, function (e, data) { Common.getFileSize(href, function (e, data) {
if (e) { if (e) {
displayDefault(); displayDefault();
return void console.error(e); return void console.error(e);
} }
if (typeof data !== "number") { return void displayDefault(); } if (typeof data !== "number") { return void displayDefault(); }
if (Cryptpad.bytesToMegabytes(data) > 0.5) { return void displayDefault(); } if (Util.bytesToMegabytes(data) > 0.5) { return void displayDefault(); }
var $img = $('<media-tag>').appendTo($container); var $img = $('<media-tag>').appendTo($container);
$img.attr('src', src); $img.attr('src', src);
$img.attr('data-crypto-key', 'cryptpad:' + cryptKey); $img.attr('data-crypto-key', 'cryptpad:' + cryptKey);
UI.displayMediatagImage(Common, $img, function (err, $image, img) { UIElements.displayMediatagImage(Common, $img, function (err, $image, img) {
if (err) { return void console.error(err); } if (err) { return void console.error(err); }
var w = img.width; var w = img.width;
var h = img.height; var h = img.height;
@ -317,13 +338,13 @@ define([
update. update.
*/ */
var LIMIT_REFRESH_RATE = 30000; // milliseconds var LIMIT_REFRESH_RATE = 30000; // milliseconds
UI.createUsageBar = function (common, cb) { UIElements.createUsageBar = function (common, cb) {
if (!common.isLoggedIn()) { return cb("NOT_LOGGED_IN"); } if (!common.isLoggedIn()) { return cb("NOT_LOGGED_IN"); }
// getPinnedUsage updates common.account.usage, and other values // getPinnedUsage updates common.account.usage, and other values
// so we can just use those and only check for errors // so we can just use those and only check for errors
var $container = $('<span>', {'class':'cp-limit-container'}); var $container = $('<span>', {'class':'cp-limit-container'});
var todo; var todo;
var updateUsage = Cryptpad.notAgainForAnother(function () { var updateUsage = Util.notAgainForAnother(function () {
common.getPinUsage(todo); common.getPinUsage(todo);
}, LIMIT_REFRESH_RATE); }, LIMIT_REFRESH_RATE);
@ -404,23 +425,174 @@ define([
cb(null, $container); cb(null, $container);
}; };
UI.createUserAdminMenu = function (Common, config) { // Create a button with a dropdown menu
// input is a config object with parameters:
// - container (optional): the dropdown container (span)
// - text (optional): the button text value
// - options: array of {tag: "", attributes: {}, content: "string"}
//
// allowed options tags: ['a', 'hr', 'p']
UIElements.createDropdown = function (config) {
if (typeof config !== "object" || !Array.isArray(config.options)) { return; }
var allowedTags = ['a', 'p', 'hr'];
var isValidOption = function (o) {
if (typeof o !== "object") { return false; }
if (!o.tag || allowedTags.indexOf(o.tag) === -1) { return false; }
return true;
};
// Container
var $container = $(config.container);
var containerConfig = {
'class': 'cp-dropdown-container'
};
if (config.buttonTitle) {
containerConfig.title = config.buttonTitle;
}
if (!config.container) {
$container = $('<span>', containerConfig);
}
// Button
var $button = $('<button>', {
'class': ''
}).append($('<span>', {'class': 'cp-dropdown-button-title'}).html(config.text || ""));
/*$('<span>', {
'class': 'fa fa-caret-down',
}).appendTo($button);*/
// Menu
var $innerblock = $('<div>', {'class': 'cp-dropdown-content'});
if (config.left) { $innerblock.addClass('cp-dropdown-left'); }
config.options.forEach(function (o) {
if (!isValidOption(o)) { return; }
$('<' + o.tag + '>', o.attributes || {}).html(o.content || '').appendTo($innerblock);
});
$container.append($button).append($innerblock);
var value = config.initialValue || '';
var setActive = function ($el) {
if ($el.length !== 1) { return; }
$innerblock.find('.cp-dropdown-element-active').removeClass('cp-dropdown-element-active');
$el.addClass('cp-dropdown-element-active');
var scroll = $el.position().top + $innerblock.scrollTop();
if (scroll < $innerblock.scrollTop()) {
$innerblock.scrollTop(scroll);
} else if (scroll > ($innerblock.scrollTop() + 280)) {
$innerblock.scrollTop(scroll-270);
}
};
var hide = function () {
window.setTimeout(function () { $innerblock.hide(); }, 0);
};
var show = function () {
$innerblock.show();
$innerblock.find('.cp-dropdown-element-active').removeClass('cp-dropdown-element-active');
if (config.isSelect && value) {
var $val = $innerblock.find('[data-value="'+value+'"]');
setActive($val);
$innerblock.scrollTop($val.position().top + $innerblock.scrollTop());
}
if (config.feedback) { Cryptpad.feedback(config.feedback); }
};
$container.click(function (e) {
e.stopPropagation();
var state = $innerblock.is(':visible');
$('.cp-dropdown-content').hide();
try {
$('iframe').each(function (idx, ifrw) {
$(ifrw).contents().find('.cp-dropdown-content').hide();
});
} catch (er) {
// empty try catch in case this iframe is problematic (cross-origin)
}
if (state) {
hide();
return;
}
show();
});
if (config.isSelect) {
var pressed = '';
var to;
$container.keydown(function (e) {
var $value = $innerblock.find('[data-value].cp-dropdown-element-active');
if (e.which === 38) { // Up
if ($value.length) {
var $prev = $value.prev();
setActive($prev);
}
}
if (e.which === 40) { // Down
if ($value.length) {
var $next = $value.next();
setActive($next);
}
}
if (e.which === 13) { //Enter
if ($value.length) {
$value.click();
hide();
}
}
if (e.which === 27) { // Esc
hide();
}
});
$container.keypress(function (e) {
window.clearTimeout(to);
var c = String.fromCharCode(e.which);
pressed += c;
var $value = $innerblock.find('[data-value^="'+pressed+'"]:first');
if ($value.length) {
setActive($value);
$innerblock.scrollTop($value.position().top + $innerblock.scrollTop());
}
to = window.setTimeout(function () {
pressed = '';
}, 1000);
});
$container.setValue = function (val, name) {
value = val;
var $val = $innerblock.find('[data-value="'+val+'"]');
var textValue = name || $val.html() || val;
$button.find('.cp-dropdown-button-title').html(textValue);
};
$container.getValue = function () {
return value || '';
};
}
return $container;
};
UIElements.createUserAdminMenu = function (Common, config) {
var metadataMgr = Common.getMetadataMgr(); var metadataMgr = Common.getMetadataMgr();
var displayNameCls = config.displayNameCls || 'displayName'; var displayNameCls = config.displayNameCls || 'cp-toolbar-user-name';
var $displayedName = $('<span>', {'class': displayNameCls}); var $displayedName = $('<span>', {'class': displayNameCls});
var accountName = metadataMgr.getPrivateData().accountName; var accountName = metadataMgr.getPrivateData().accountName;
var origin = metadataMgr.getPrivateData().origin; var origin = metadataMgr.getPrivateData().origin;
var padType = metadataMgr.getMetadata().type; var padType = metadataMgr.getMetadata().type;
var $userName = $('<span>', {'class': 'userDisplayName'}); var $userName = $('<span>');
var options = []; var options = [];
if (config.displayNameCls) { if (config.displayNameCls) {
var $userAdminContent = $('<p>'); var $userAdminContent = $('<p>');
if (accountName) { if (accountName) {
var $userAccount = $('<span>', {'class': 'userAccount'}).append(Messages.user_accountName + ': ' + Cryptpad.fixHTML(accountName)); var $userAccount = $('<span>').append(Messages.user_accountName + ': ');
$userAdminContent.append($userAccount); $userAdminContent.append($userAccount).append(Util.fixHTML(accountName));
$userAdminContent.append($('<br>')); $userAdminContent.append($('<br>'));
} }
if (config.displayName) { if (config.displayName) {
@ -456,14 +628,14 @@ define([
if (accountName) { if (accountName) {
options.push({ options.push({
tag: 'a', tag: 'a',
attributes: {'class': 'profile'}, attributes: {'class': 'cp-toolbar-menu-profile'},
content: Messages.profileButton content: Messages.profileButton
}); });
} }
if (padType !== 'settings') { if (padType !== 'settings') {
options.push({ options.push({
tag: 'a', tag: 'a',
attributes: {'class': 'settings'}, attributes: {'class': 'cp-toolbar-menu-settings'},
content: Messages.settingsButton content: Messages.settingsButton
}); });
} }
@ -471,18 +643,18 @@ define([
if (accountName) { if (accountName) {
options.push({ options.push({
tag: 'a', tag: 'a',
attributes: {'class': 'logout'}, attributes: {'class': 'cp-toolbar-menu-logout'},
content: Messages.logoutButton content: Messages.logoutButton
}); });
} else { } else {
options.push({ options.push({
tag: 'a', tag: 'a',
attributes: {'class': 'login'}, attributes: {'class': 'cp-toolbar-menu-login'},
content: Messages.login_login content: Messages.login_login
}); });
options.push({ options.push({
tag: 'a', tag: 'a',
attributes: {'class': 'register'}, attributes: {'class': 'cp-toolbar-menu-register'},
content: Messages.login_register content: Messages.login_register
}); });
} }
@ -505,11 +677,18 @@ define([
container: config.$initBlock, // optional container: config.$initBlock, // optional
feedback: "USER_ADMIN", feedback: "USER_ADMIN",
}; };
var $userAdmin = Cryptpad.createDropdown(dropdownConfigUser); var $userAdmin = UIElements.createDropdown(dropdownConfigUser);
/*
// Uncomment these lines to have a language selector in the admin menu
// FIXME clicking on the inner menu hides the outer one
var $lang = UIElements.createLanguageSelector(Common);
$userAdmin.find('.cp-dropdown-content').append($lang);
*/
var $displayName = $userAdmin.find('.'+displayNameCls); var $displayName = $userAdmin.find('.'+displayNameCls);
var $avatar = $userAdmin.find('.cp-dropdown-button-title'); var $avatar = $userAdmin.find('> button .cp-dropdown-button-title');
var loadingAvatar; var loadingAvatar;
var to; var to;
var oldUrl = ''; var oldUrl = '';
@ -528,10 +707,11 @@ define([
$displayName.text(newName || Messages.anonymous); $displayName.text(newName || Messages.anonymous);
if (accountName && oldUrl !== url) { if (accountName && oldUrl !== url) {
$avatar.html(''); $avatar.html('');
UI.displayAvatar(Common, $avatar, url, newName || Messages.anonymous, function ($img) { UIElements.displayAvatar(Common, $avatar, url,
newName || Messages.anonymous, function ($img) {
oldUrl = url; oldUrl = url;
if ($img) { if ($img) {
$userAdmin.find('button').addClass('cp-avatar'); $userAdmin.find('> button').addClass('cp-avatar');
} }
loadingAvatar = false; loadingAvatar = false;
}); });
@ -542,31 +722,31 @@ define([
metadataMgr.onChange(updateButton); metadataMgr.onChange(updateButton);
updateButton(); updateButton();
$userAdmin.find('a.logout').click(function () { $userAdmin.find('a.cp-toolbar-menu-logout').click(function () {
Common.logout(function () { Common.logout(function () {
window.parent.location = origin+'/'; window.parent.location = origin+'/';
}); });
}); });
$userAdmin.find('a.settings').click(function () { $userAdmin.find('a.cp-toolbar-menu-settings').click(function () {
if (padType) { if (padType) {
window.open(origin+'/settings/'); window.open(origin+'/settings/');
} else { } else {
window.parent.location = origin+'/settings/'; window.parent.location = origin+'/settings/';
} }
}); });
$userAdmin.find('a.profile').click(function () { $userAdmin.find('a.cp-toolbar-menu-profile').click(function () {
if (padType) { if (padType) {
window.open(origin+'/profile/'); window.open(origin+'/profile/');
} else { } else {
window.parent.location = origin+'/profile/'; window.parent.location = origin+'/profile/';
} }
}); });
$userAdmin.find('a.login').click(function () { $userAdmin.find('a.cp-toolbar-menu-login').click(function () {
Common.setLoginRedirect(function () { Common.setLoginRedirect(function () {
window.parent.location = origin+'/login/'; window.parent.location = origin+'/login/';
}); });
}); });
$userAdmin.find('a.register').click(function () { $userAdmin.find('a.cp-toolbar-menu-register').click(function () {
Common.setLoginRedirect(function () { Common.setLoginRedirect(function () {
window.parent.location = origin+'/register/'; window.parent.location = origin+'/register/';
}); });
@ -577,7 +757,7 @@ define([
// Provide $container if you want to put the generated block in another element // Provide $container if you want to put the generated block in another element
// Provide $initBlock if you already have the menu block and you want the content inserted in it // Provide $initBlock if you already have the menu block and you want the content inserted in it
UI.createLanguageSelector = function (common, $container, $initBlock) { UIElements.createLanguageSelector = function (common, $container, $initBlock) {
var options = []; var options = [];
var languages = Messages._languages; var languages = Messages._languages;
var keys = Object.keys(languages).sort(); var keys = Object.keys(languages).sort();
@ -599,7 +779,7 @@ define([
container: $initBlock, // optional container: $initBlock, // optional
isSelect: true isSelect: true
}; };
var $block = Cryptpad.createDropdown(dropdownConfig); var $block = UIElements.createDropdown(dropdownConfig);
$block.attr('id', 'cp-language-selector'); $block.attr('id', 'cp-language-selector');
if ($container) { if ($container) {
@ -611,20 +791,51 @@ define([
return $block; return $block;
}; };
UIElements.createModal = function (cfg) {
var $body = cfg.$body || $('body');
var $blockContainer = $body.find('#'+cfg.id);
if (!$blockContainer.length) {
$blockContainer = $('<div>', {
'class': 'cp-modal-container',
'id': cfg.id
});
}
var hide = function () {
if (cfg.onClose) { return void cfg.onClose(); }
$blockContainer.hide();
};
$blockContainer.html('').appendTo($body);
var $block = $('<div>', {'class': 'cp-modal'}).appendTo($blockContainer);
$('<span>', {
'class': 'cp-modal-close fa fa-times',
'title': Messages.filePicker_close
}).click(hide).appendTo($block);
$body.click(hide);
$block.click(function (e) {
e.stopPropagation();
});
$body.keydown(function (e) {
if (e.which === 27) {
hide();
}
});
return $blockContainer;
};
UI.initFilePicker = function (common, cfg) { UIElements.initFilePicker = function (common, cfg) {
var onSelect = cfg.onSelect || $.noop; var onSelect = cfg.onSelect || $.noop;
var sframeChan = common.getSframeChannel(); var sframeChan = common.getSframeChannel();
sframeChan.on("EV_FILE_PICKED", function (data) { sframeChan.on("EV_FILE_PICKED", function (data) {
onSelect(data); onSelect(data);
}); });
}; };
UI.openFilePicker = function (common, types) { UIElements.openFilePicker = function (common, types) {
var sframeChan = common.getSframeChannel(); var sframeChan = common.getSframeChannel();
sframeChan.event("EV_FILE_PICKER_OPEN", types); sframeChan.event("EV_FILE_PICKER_OPEN", types);
}; };
UI.openTemplatePicker = function (common) { UIElements.openTemplatePicker = function (common) {
var metadataMgr = common.getMetadataMgr(); var metadataMgr = common.getMetadataMgr();
var type = metadataMgr.getMetadataLazy().type; var type = metadataMgr.getMetadataLazy().type;
var sframeChan = common.getSframeChannel(); var sframeChan = common.getSframeChannel();
@ -646,10 +857,10 @@ define([
var fileDialogCfg = { var fileDialogCfg = {
onSelect: function (data) { onSelect: function (data) {
if (data.type === type && first) { if (data.type === type && first) {
Cryptpad.addLoadingScreen({hideTips: true}); UI.addLoadingScreen({hideTips: true});
sframeChan.query('Q_TEMPLATE_USE', data.href, function () { sframeChan.query('Q_TEMPLATE_USE', data.href, function () {
first = false; first = false;
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
common.feedback('TEMPLATE_USED'); common.feedback('TEMPLATE_USED');
}); });
if (focus) { focus.focus(); } if (focus) { focus.focus(); }
@ -664,7 +875,7 @@ define([
if (data) { if (data) {
common.openFilePicker(pickerCfg); common.openFilePicker(pickerCfg);
focus = document.activeElement; focus = document.activeElement;
Cryptpad.confirm(Messages.useTemplate, onConfirm, { UI.confirm(Messages.useTemplate, onConfirm, {
ok: Messages.useTemplateOK, ok: Messages.useTemplateOK,
cancel: Messages.useTemplateCancel, cancel: Messages.useTemplateCancel,
}); });
@ -672,58 +883,5 @@ define([
}); });
}; };
UI.addTooltips = function () { return UIElements;
var MutationObserver = window.MutationObserver;
var delay = typeof(AppConfig.tooltipDelay) === "number" ? AppConfig.tooltipDelay : 500;
var addTippy = function (i, el) {
if (el.nodeName === 'IFRAME') { return; }
Tippy(el, {
position: 'bottom',
distance: 0,
performance: true,
dynamicTitle: true,
delay: [delay, 0]
});
};
var clearTooltips = function () {
$('.tippy-popper').each(function (i, el) {
if ($('[aria-describedby=' + el.getAttribute('id') + ']').length === 0) {
el.remove();
}
});
};
// This is the robust solution to remove dangling tooltips
// The mutation observer does not always find removed nodes.
setInterval(clearTooltips, delay);
var checkRemoved = function (x) {
var out = false;
$(x).find('[aria-describedby]').each(function (i, el) {
var id = el.getAttribute('aria-describedby');
if (id.indexOf('tippy-tooltip-') !== 0) { return; }
out = true;
});
return out;
};
$('[title]').each(addTippy);
var observer = new MutationObserver(function(mutations) {
var removed = false;
mutations.forEach(function(mutation) {
for (var i = 0; i < mutation.addedNodes.length; i++) {
$(mutation.addedNodes[i]).find('[title]').each(addTippy);
}
for (var j = 0; j < mutation.removedNodes.length; j++) {
removed |= checkRemoved(mutation.removedNodes[j]);
}
});
if (removed) { clearTooltips(); }
});
observer.observe($('body')[0], {
attributes: false,
childList: true,
characterData: false,
subtree: true
});
};
return UI;
}); });

@ -1,117 +0,0 @@
define(['json.sortify'], function (Sortify) {
var module = {};
module.create = function (info, onLocal, Cryptget, Cryptpad) {
var exp = {};
var userData = exp.userData = {};
var userList = exp.userList = info.userList;
var myData = exp.myData = {};
exp.myUserName = info.myID;
exp.myNetfluxId = info.myID;
var network = Cryptpad.getNetwork();
var parsed = Cryptpad.parsePadUrl(window.location.href);
var appType = parsed ? parsed.type : undefined;
var oldUserData = {};
var addToUserData = exp.addToUserData = function(data) {
var users = userList.users;
for (var attrname in data) { userData[attrname] = data[attrname]; }
if (users && users.length) {
for (var userKey in userData) {
if (users.indexOf(userKey) === -1) {
delete userData[userKey];
}
}
}
if(userList && typeof userList.onChange === "function") {
// Make sure we don't update the userlist everytime someone makes a change to the pad
if (Sortify(oldUserData) === Sortify(userData)) { return; }
oldUserData = JSON.parse(JSON.stringify(userData));
userList.onChange(userData);
}
};
exp.getToolbarConfig = function () {
return {
data: userData,
list: userList,
userNetfluxId: exp.myNetfluxId
};
};
var setName = exp.setName = function (newName, cb) {
if (typeof(newName) !== 'string') { return; }
var myUserNameTemp = newName.trim();
if(myUserNameTemp.length > 32) {
myUserNameTemp = myUserNameTemp.substr(0, 32);
}
exp.myUserName = myUserNameTemp;
myData = {};
myData[exp.myNetfluxId] = {
name: exp.myUserName,
uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl(),
curvePublic: Cryptpad.getProxy().curvePublic
};
addToUserData(myData);
/*Cryptpad.setAttribute('username', exp.myUserName, function (err) {
if (err) {
console.log("Couldn't set username");
console.error(err);
return;
}
if (typeof cb === "function") { cb(); }
});*/
if (typeof cb === "function") { cb(); }
};
exp.getLastName = function ($changeNameButton, isNew) {
Cryptpad.getLastName(function (err, lastName) {
if (err) {
console.log("Could not get previous name");
console.error(err);
return;
}
// Update the toolbar list:
// Add the current user in the metadata
if (typeof(lastName) === 'string') {
setName(lastName, onLocal);
} else {
myData[exp.myNetfluxId] = {
name: "",
uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl(),
curvePublic: Cryptpad.getProxy().curvePublic
};
addToUserData(myData);
onLocal();
$changeNameButton.click();
}
if (isNew && appType) {
Cryptpad.selectTemplate(appType, info.realtime, Cryptget);
}
});
};
Cryptpad.onDisplayNameChanged(function (newName) {
setName(newName, onLocal);
});
network.on('reconnect', function (uid) {
exp.myNetfluxId = uid;
exp.setName(exp.myUserName);
});
return exp;
};
return module;
});

@ -1,112 +0,0 @@
define(function () {
var module = {};
module.create = function (info, onLocal, Cryptget, Cryptpad) {
var exp = {};
var userData = exp.userData = {};
var userList = exp.userList = info.userList;
var myData = exp.myData = {};
exp.myUserName = info.myID;
exp.myNetfluxId = info.myID;
var network = Cryptpad.getNetwork();
var parsed = Cryptpad.parsePadUrl(window.location.href);
var appType = parsed ? parsed.type : undefined;
var addToUserData = exp.addToUserData = function(data) {
var users = userList.users;
for (var attrname in data) { userData[attrname] = data[attrname]; }
if (users && users.length) {
for (var userKey in userData) {
if (users.indexOf(userKey) === -1) {
delete userData[userKey];
}
}
}
if(userList && typeof userList.onChange === "function") {
userList.onChange(userData);
}
};
exp.getToolbarConfig = function () {
return {
data: userData,
list: userList,
userNetfluxId: exp.myNetfluxId
};
};
var setName = exp.setName = function (newName, cb) {
if (typeof(newName) !== 'string') { return; }
var myUserNameTemp = newName.trim();
if(myUserNameTemp.length > 32) {
myUserNameTemp = myUserNameTemp.substr(0, 32);
}
exp.myUserName = myUserNameTemp;
myData = {};
myData[exp.myNetfluxId] = {
name: exp.myUserName,
uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl(),
curvePublic: Cryptpad.getProxy().curvePublic
};
addToUserData(myData);
/*Cryptpad.setAttribute('username', exp.myUserName, function (err) {
if (err) {
console.log("Couldn't set username");
console.error(err);
return;
}
if (typeof cb === "function") { cb(); }
});*/
if (typeof cb === "function") { cb(); }
};
exp.getLastName = function ($changeNameButton, isNew) {
Cryptpad.getLastName(function (err, lastName) {
if (err) {
console.log("Could not get previous name");
console.error(err);
return;
}
// Update the toolbar list:
// Add the current user in the metadata
if (typeof(lastName) === 'string') {
setName(lastName, onLocal);
} else {
myData[exp.myNetfluxId] = {
name: "",
uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl(),
curvePublic: Cryptpad.getProxy().curvePublic
};
addToUserData(myData);
onLocal();
$changeNameButton.click();
}
if (isNew && appType) {
Cryptpad.selectTemplate(appType, info.realtime, Cryptget);
}
});
};
Cryptpad.onDisplayNameChanged(function (newName) {
setName(newName, onLocal);
});
network.on('reconnect', function (uid) {
exp.myNetfluxId = uid;
exp.setName(exp.myUserName);
});
return exp;
};
return module;
});

@ -89,22 +89,6 @@ define([], function () {
return a; return a;
}; };
Util.getHash = function () {
return window.location.hash.slice(1);
};
Util.replaceHash = function (hash) {
if (window.history && window.history.replaceState) {
if (!/^#/.test(hash)) { hash = '#' + hash; }
void window.history.replaceState({}, window.document.title, hash);
if (typeof(window.onhashchange) === 'function') {
window.onhashchange();
}
return;
}
window.location.hash = hash;
};
/* /*
* Saving files * Saving files
*/ */
@ -186,13 +170,6 @@ define([], function () {
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
}; };
Util.getAppType = function () {
var parts = window.location.pathname.split('/')
.filter(function (x) { return x; });
if (!parts[0]) { return ''; }
return parts[0];
};
/* for wrapping async functions such that they can only be called once */ /* for wrapping async functions such that they can only be called once */
Util.once = function (f) { Util.once = function (f) {
var called; var called;

@ -3,8 +3,11 @@ define([
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/bower_components/chainpad-netflux/chainpad-netflux.js', '/bower_components/chainpad-netflux/chainpad-netflux.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/common-util.js',
'/common/common-hash.js',
'/common/common-realtime.js',
'/bower_components/textpatcher/TextPatcher.js' '/bower_components/textpatcher/TextPatcher.js'
], function ($, Crypto, Realtime, Cryptpad, TextPatcher) { ], function ($, Crypto, CPNetflux, Cryptpad, Util, Hash, Realtime, TextPatcher) {
//var Messages = Cryptpad.Messages; //var Messages = Cryptpad.Messages;
//var noop = function () {}; //var noop = function () {};
var finish = function (S, err, doc) { var finish = function (S, err, doc) {
@ -12,9 +15,9 @@ define([
S.cb(err, doc); S.cb(err, doc);
S.done = true; S.done = true;
var disconnect = Cryptpad.find(S, ['network', 'disconnect']); var disconnect = Util.find(S, ['network', 'disconnect']);
if (typeof(disconnect) === 'function') { disconnect(); } if (typeof(disconnect) === 'function') { disconnect(); }
var abort = Cryptpad.find(S, ['realtime', 'realtime', 'abort']); var abort = Util.find(S, ['realtime', 'realtime', 'abort']);
if (typeof(abort) === 'function') { if (typeof(abort) === 'function') {
S.realtime.realtime.sync(); S.realtime.realtime.sync();
abort(); abort();
@ -23,7 +26,7 @@ define([
var makeConfig = function (hash) { var makeConfig = function (hash) {
// We can't use cryptget with a file or a user so we can use 'pad' as hash type // We can't use cryptget with a file or a user so we can use 'pad' as hash type
var secret = Cryptpad.getSecrets('pad', hash); var secret = Hash.getSecrets('pad', hash);
if (!secret.keys) { secret.keys = secret.key; } // support old hashses if (!secret.keys) { secret.keys = secret.key; } // support old hashses
var config = { var config = {
websocketURL: Cryptpad.getWebsocketURL(), websocketURL: Cryptpad.getWebsocketURL(),
@ -58,7 +61,7 @@ define([
}; };
overwrite(config, opt); overwrite(config, opt);
Session.realtime = Realtime.start(config); Session.realtime = CPNetflux.start(config);
}; };
var put = function (hash, doc, cb, opt) { var put = function (hash, doc, cb, opt) {
@ -80,7 +83,7 @@ define([
cb(new Error("Timeout")); cb(new Error("Timeout"));
}, 5000); }, 5000);
Cryptpad.whenRealtimeSyncs(realtime, function () { Realtime.whenRealtimeSyncs(realtime, function () {
window.clearTimeout(to); window.clearTimeout(to);
realtime.abort(); realtime.abort();
finish(Session, void 0); finish(Session, void 0);
@ -88,7 +91,7 @@ define([
}; };
overwrite(config, opt); overwrite(config, opt);
Session.session = Realtime.start(config); Session.session = CPNetflux.start(config);
}; };
return { return {

File diff suppressed because it is too large Load Diff

@ -1,11 +1,12 @@
define([ define([
'jquery', 'jquery',
'/bower_components/marked/marked.min.js', '/bower_components/marked/marked.min.js',
'/common/cryptpad-common.js', '/common/common-hash.js',
'/common/common-util.js',
'/common/media-tag.js', '/common/media-tag.js',
'/bower_components/diff-dom/diffDOM.js', '/bower_components/diff-dom/diffDOM.js',
'/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/tweetnacl/nacl-fast.min.js',
],function ($, Marked, Cryptpad, MediaTag) { ],function ($, Marked, Hash, Util, MediaTag) {
var DiffMd = {}; var DiffMd = {};
var DiffDOM = window.diffDOM; var DiffDOM = window.diffDOM;
@ -40,8 +41,8 @@ define([
}; };
renderer.image = function (href, title, text) { renderer.image = function (href, title, text) {
if (href.slice(0,6) === '/file/') { if (href.slice(0,6) === '/file/') {
var parsed = Cryptpad.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel); var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '">'; var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '">';
if (mediaMap[src]) { if (mediaMap[src]) {

File diff suppressed because it is too large Load Diff

@ -4,8 +4,10 @@ define([
'/bower_components/chainpad-crypto/crypto.js?v=0.1.5', '/bower_components/chainpad-crypto/crypto.js?v=0.1.5',
'/bower_components/textpatcher/TextPatcher.amd.js', '/bower_components/textpatcher/TextPatcher.amd.js',
'/common/userObject.js', '/common/userObject.js',
'/common/common-interface.js',
'/common/common-hash.js',
'/common/migrate-user-object.js', '/common/migrate-user-object.js',
], function ($, Listmap, Crypto, TextPatcher, FO, Migrate) { ], function ($, Listmap, Crypto, TextPatcher, FO, UI, Hash, Migrate) {
/* /*
This module uses localStorage, which is synchronous, but exposes an This module uses localStorage, which is synchronous, but exposes an
asyncronous API. This is so that we can substitute other storage asyncronous API. This is so that we can substitute other storage
@ -247,7 +249,7 @@ define([
if (typeof(proxy.uid) !== 'string' || proxy.uid.length !== 32) { if (typeof(proxy.uid) !== 'string' || proxy.uid.length !== 32) {
// even anonymous users should have a persistent, unique-ish id // even anonymous users should have a persistent, unique-ish id
console.log('generating a persistent identifier'); console.log('generating a persistent identifier');
proxy.uid = Cryptpad.createChannelId(); proxy.uid = Hash.createChannelId();
} }
// if the user is logged in, but does not have signing keys... // if the user is logged in, but does not have signing keys...
@ -284,11 +286,11 @@ define([
if (!Cryptpad || initialized) { return; } if (!Cryptpad || initialized) { return; }
initialized = true; initialized = true;
var hash = Cryptpad.getUserHash() || localStorage.FS_hash || Cryptpad.createRandomHash(); var hash = Cryptpad.getUserHash() || localStorage.FS_hash || Hash.createRandomHash();
if (!hash) { if (!hash) {
throw new Error('[Store.init] Unable to find or create a drive hash. Aborting...'); throw new Error('[Store.init] Unable to find or create a drive hash. Aborting...');
} }
var secret = Cryptpad.getSecrets('drive', hash); var secret = Hash.getSecrets('drive', hash);
var listmapConfig = { var listmapConfig = {
data: {}, data: {},
websocketURL: Cryptpad.getWebsocketURL(), websocketURL: Cryptpad.getWebsocketURL(),
@ -302,27 +304,6 @@ define([
var exp = {}; var exp = {};
window.addEventListener('storage', function (e) {
if (e.key !== Cryptpad.userHashKey) { return; }
var o = e.oldValue;
var n = e.newValue;
if (!o && n) {
window.location.reload();
} else if (o && !n) {
$(window).on('keyup', function (e) {
if (e.keyCode === 27) {
Cryptpad.removeLoadingScreen();
}
});
Cryptpad.logout();
Cryptpad.addLoadingScreen({hideTips: true});
Cryptpad.errorLoadingScreen(Cryptpad.Messages.onLogout, true);
if (exp.info) {
exp.info.network.disconnect();
}
}
});
var rt = window.rt = Listmap.create(listmapConfig); var rt = window.rt = Listmap.create(listmapConfig);
exp.realtime = rt.realtime; exp.realtime = rt.realtime;
@ -330,7 +311,7 @@ define([
rt.proxy.on('create', function (info) { rt.proxy.on('create', function (info) {
exp.info = info; exp.info = info;
if (!Cryptpad.getUserHash()) { if (!Cryptpad.getUserHash()) {
localStorage.FS_hash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); localStorage.FS_hash = Hash.getEditHashFromKeys(info.channel, secret.keys);
} }
}).on('ready', function () { }).on('ready', function () {
if (store) { return; } // the store is already ready, it is a reconnection if (store) { return; } // the store is already ready, it is a reconnection
@ -346,22 +327,13 @@ define([
// Drive already exist: return the existing drive, don't load data from legacy store // Drive already exist: return the existing drive, don't load data from legacy store
onReady(f, rt.proxy, Cryptpad, exp); onReady(f, rt.proxy, Cryptpad, exp);
}) })
.on('disconnect', function (info) {
// We only manage errors during the loading screen here. Other websocket errors are handled by the apps
if (info.error) {
if (typeof Cryptpad.storeError === "function") {
Cryptpad.storeError();
}
return;
}
})
.on('change', ['drive', 'migrate'], function () { .on('change', ['drive', 'migrate'], function () {
var path = arguments[2]; var path = arguments[2];
var value = arguments[1]; var value = arguments[1];
if (path[0] === 'drive' && path[1] === "migrate" && value === 1) { if (path[0] === 'drive' && path[1] === "migrate" && value === 1) {
rt.network.disconnect(); rt.network.disconnect();
rt.realtime.abort(); rt.realtime.abort();
Cryptpad.alert(Cryptpad.Messages.fs_migration, null, true); UI.alert(Cryptpad.Messages.fs_migration, null, true);
} }
}); });
}; };

@ -3,10 +3,11 @@ define([
'/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/common-util.js',
'/common/credential.js', '/common/credential.js',
'/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/tweetnacl/nacl-fast.min.js',
'/bower_components/scrypt-async/scrypt-async.min.js', // better load speed '/bower_components/scrypt-async/scrypt-async.min.js', // better load speed
], function ($, Listmap, Crypto, Cryptpad, Cred) { ], function ($, Listmap, Crypto, Cryptpad, Util, Cred) {
var Exports = { var Exports = {
Cred: Cred, Cred: Cred,
}; };
@ -43,12 +44,12 @@ define([
keys.editKeyStr = keys.editKeyStr.replace(/\//g, '-'); keys.editKeyStr = keys.editKeyStr.replace(/\//g, '-');
// 32 bytes of hex // 32 bytes of hex
var channelHex = opt.channelHex = Cryptpad.uint8ArrayToHex(channelSeed); var channelHex = opt.channelHex = Util.uint8ArrayToHex(channelSeed);
// should never happen // should never happen
if (channelHex.length !== 32) { throw new Error('invalid channel id'); } if (channelHex.length !== 32) { throw new Error('invalid channel id'); }
opt.channel64 = Cryptpad.hexToBase64(channelHex); opt.channel64 = Util.hexToBase64(channelHex);
opt.userHash = '/1/edit/' + [opt.channel64, opt.keys.editKeyStr].join('/'); opt.userHash = '/1/edit/' + [opt.channel64, opt.keys.editKeyStr].join('/');

@ -2,7 +2,8 @@ define([
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/cryptget.js', '/common/cryptget.js',
'/common/userObject.js', '/common/userObject.js',
], function (Cryptpad, Crypt, FO) { '/common/common-hash.js',
], function (Cryptpad, Crypt, FO, Hash) {
var exp = {}; var exp = {};
var getType = function (el) { var getType = function (el) {
@ -41,7 +42,7 @@ define([
if (typeof(p) === "string") { if (typeof(p) === "string") {
if (getType(root) !== "object") { root = undefined; error(); return; } if (getType(root) !== "object") { root = undefined; error(); return; }
if (i === path.length - 1) { if (i === path.length - 1) {
root[Cryptpad.createChannelId()] = id; root[Hash.createChannelId()] = id;
return; return;
} }
next = getType(path[i+1]); next = getType(path[i+1]);
@ -120,10 +121,10 @@ define([
// Do not migrate a pad if we already have it, it would create a duplicate in the drive // Do not migrate a pad if we already have it, it would create a duplicate in the drive
if (newHrefs.indexOf(href) !== -1) { return; } if (newHrefs.indexOf(href) !== -1) { return; }
// If we have a stronger version, do not add the current href // If we have a stronger version, do not add the current href
if (Cryptpad.findStronger(href, newRecentPads)) { return; } if (Hash.findStronger(href, newRecentPads)) { return; }
// If we have a weaker version, replace the href by the new one // If we have a weaker version, replace the href by the new one
// NOTE: if that weaker version is in the trash, the strong one will be put in unsorted // NOTE: if that weaker version is in the trash, the strong one will be put in unsorted
var weaker = Cryptpad.findWeaker(href, newRecentPads); var weaker = Hash.findWeaker(href, newRecentPads);
if (weaker) { if (weaker) {
// Update RECENTPADS // Update RECENTPADS
newRecentPads.some(function (pad) { newRecentPads.some(function (pad) {

@ -0,0 +1,93 @@
define([
'/file/file-crypto.js',
'/common/common-hash.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
], function (FileCrypto, Hash) {
var Nacl = window.nacl;
var module = {};
module.upload = function (file, noStore, common, updateProgress, onComplete, onError, onPending) {
var u8 = file.blob; // This is not a blob but a uint8array
var metadata = file.metadata;
// if it exists, path contains the new pad location in the drive
var path = file.path;
var key = Nacl.randomBytes(32);
var next = FileCrypto.encrypt(u8, metadata, key);
var estimate = FileCrypto.computeEncryptedSize(u8.length, metadata);
var sendChunk = function (box, cb) {
var enc = Nacl.util.encodeBase64(box);
common.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) {
cb(e, msg);
});
};
var actual = 0;
var again = function (err, box) {
if (err) { throw new Error(err); }
if (box) {
actual += box.length;
var progressValue = (actual / estimate * 100);
updateProgress(progressValue);
return void sendChunk(box, function (e) {
if (e) { return console.error(e); }
next(again);
});
}
if (actual !== estimate) {
console.error('Estimated size does not match actual size');
}
// if not box then done
common.uploadComplete(function (e, id) {
if (e) { return void console.error(e); }
var uri = ['', 'blob', id.slice(0,2), id].join('/');
console.log("encrypted blob is now available as %s", uri);
var b64Key = Nacl.util.encodeBase64(key);
var hash = Hash.getFileHashFromKeys(id, b64Key);
var href = '/file/#' + hash;
var title = metadata.name;
if (noStore) { return void onComplete(href); }
common.initialPath = path;
common.renamePad(title || "", href, function (err) {
if (err) { return void console.error(err); }
onComplete(href);
common.setPadAttribute('fileType', metadata.type, null, href);
});
});
};
common.uploadStatus(estimate, function (e, pending) {
if (e) {
console.error(e);
onError(e);
return;
}
if (pending) {
return void onPending(function () {
// if the user wants to cancel the pending upload to execute that one
common.uploadCancel(function (e, res) {
if (e) {
return void console.error(e);
}
console.log(res);
next(again);
});
});
}
next(again);
});
};
return module;
});

@ -5,11 +5,11 @@ define([
'/bower_components/chainpad-json-validator/json-ot.js', '/bower_components/chainpad-json-validator/json-ot.js',
'json.sortify', 'json.sortify',
'/bower_components/textpatcher/TextPatcher.js', '/bower_components/textpatcher/TextPatcher.js',
'/common/cryptpad-common.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/customize/messages.js', '/customize/messages.js',
'/common/common-util.js', '/common/common-util.js',
'/common/common-interface.js',
'/common/common-thumbnail.js', '/common/common-thumbnail.js',
'/customize/application_config.js', '/customize/application_config.js',
@ -23,11 +23,11 @@ define([
JsonOT, JsonOT,
JSONSortify, JSONSortify,
TextPatcher, TextPatcher,
Cryptpad,
nThen, nThen,
SFCommon, SFCommon,
Messages, Messages,
Util, Util,
UI,
Thumb, Thumb,
AppConfig) AppConfig)
{ {
@ -47,10 +47,6 @@ define([
var badStateTimeout = typeof(AppConfig.badStateTimeout) === 'number' ? var badStateTimeout = typeof(AppConfig.badStateTimeout) === 'number' ?
AppConfig.badStateTimeout : 30000; AppConfig.badStateTimeout : 30000;
var onConnectError = function () {
Cryptpad.errorLoadingScreen(Messages.websocketError);
};
var create = function (options, cb) { var create = function (options, cb) {
var evContentUpdate = Util.mkEvent(); var evContentUpdate = Util.mkEvent();
var evEditableStateChange = Util.mkEvent(); var evEditableStateChange = Util.mkEvent();
@ -151,7 +147,7 @@ define([
evContentUpdate.fire(newContent); evContentUpdate.fire(newContent);
} catch (e) { } catch (e) {
console.log(e.stack); console.log(e.stack);
Cryptpad.errorLoadingScreen(e.message); UI.errorLoadingScreen(e.message);
} }
}; };
@ -256,7 +252,7 @@ define([
newContent = normalize(newContent); newContent = normalize(newContent);
contentUpdate(newContent); contentUpdate(newContent);
} else { } else {
title.updateTitle(Cryptpad.initialName || title.defaultTitle); title.updateTitle(title.defaultTitle);
evOnDefaultContentNeeded.fire(); evOnDefaultContentNeeded.fire();
} }
stateChange(STATE.READY); stateChange(STATE.READY);
@ -264,7 +260,7 @@ define([
if (!readOnly) { onLocal(); } if (!readOnly) { onLocal(); }
evOnReady.fire(newPad); evOnReady.fire(newPad);
Cryptpad.removeLoadingScreen(emitResize); UI.removeLoadingScreen(emitResize);
var privateDat = cpNfInner.metadataMgr.getPrivateData(); var privateDat = cpNfInner.metadataMgr.getPrivateData();
if (options.thumbnail && privateDat.thumbnails) { if (options.thumbnail && privateDat.thumbnails) {
@ -287,9 +283,9 @@ define([
var onConnectionChange = function (info) { var onConnectionChange = function (info) {
stateChange(info.state ? STATE.INITIALIZING : STATE.DISCONNECTED); stateChange(info.state ? STATE.INITIALIZING : STATE.DISCONNECTED);
if (info.state) { if (info.state) {
Cryptpad.findOKButton().click(); UI.findOKButton().click();
} else { } else {
Cryptpad.alert(Messages.common_connectionLost, undefined, true); UI.alert(Messages.common_connectionLost, undefined, true);
} }
}; };
@ -297,8 +293,8 @@ define([
var $export = common.createButton('export', true, {}, function () { var $export = common.createButton('export', true, {}, function () {
var ext = (typeof(extension) === 'function') ? extension() : extension; var ext = (typeof(extension) === 'function') ? extension() : extension;
var suggestion = title.suggestTitle('cryptpad-document'); var suggestion = title.suggestTitle('cryptpad-document');
Cryptpad.prompt(Messages.exportPrompt, UI.prompt(Messages.exportPrompt,
Cryptpad.fixFileName(suggestion) + '.' + ext, function (filename) Util.fixFileName(suggestion) + '.' + ext, function (filename)
{ {
if (!(typeof(filename) === 'string' && filename)) { return; } if (!(typeof(filename) === 'string' && filename)) { return; }
if (async) { if (async) {
@ -373,7 +369,7 @@ define([
}; };
nThen(function (waitFor) { nThen(function (waitFor) {
Cryptpad.addLoadingScreen(); UI.addLoadingScreen();
SFCommon.create(waitFor(function (c) { common = c; })); SFCommon.create(waitFor(function (c) { common = c; }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
cpNfInner = common.startRealtime({ cpNfInner = common.startRealtime({
@ -426,25 +422,19 @@ define([
if (infiniteSpinnerModal) { return; } if (infiniteSpinnerModal) { return; }
infiniteSpinnerModal = true; infiniteSpinnerModal = true;
stateChange(STATE.INFINITE_SPINNER); stateChange(STATE.INFINITE_SPINNER);
Cryptpad.confirm(Messages.realtime_unrecoverableError, function (yes) { UI.confirm(Messages.realtime_unrecoverableError, function (yes) {
if (!yes) { return; } if (!yes) { return; }
common.gotoURL(); common.gotoURL();
}); });
cpNfInner.chainpad.onSettle(function () { cpNfInner.chainpad.onSettle(function () {
infiniteSpinnerModal = false; infiniteSpinnerModal = false;
Cryptpad.findCancelButton().click(); UI.findCancelButton().click();
stateChange(STATE.READY); stateChange(STATE.READY);
onRemote(); onRemote();
}); });
}, 2000); }, 2000);
//Cryptpad.onLogout(function () { ... }); //common.onLogout(function () { ... });
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
onConnectError();
}
});
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
if (readOnly) { $('body').addClass('cp-readonly'); } if (readOnly) { $('body').addClass('cp-readonly'); }
@ -476,7 +466,6 @@ define([
metadataMgr: cpNfInner.metadataMgr, metadataMgr: cpNfInner.metadataMgr,
readOnly: readOnly, readOnly: readOnly,
realtime: cpNfInner.chainpad, realtime: cpNfInner.chainpad,
common: Cryptpad,
sfCommon: common, sfCommon: common,
$container: $(toolbarContainer), $container: $(toolbarContainer),
$contentContainer: $(contentContainer) $contentContainer: $(contentContainer)

@ -2,9 +2,12 @@ define([
'jquery', 'jquery',
'/common/modes.js', '/common/modes.js',
'/common/themes.js', '/common/themes.js',
'/common/cryptpad-common.js', '/customize/messages.js',
'/common/common-ui-elements.js',
'/common/common-hash.js',
'/common/common-util.js',
'/bower_components/textpatcher/TextPatcher.js', '/bower_components/textpatcher/TextPatcher.js',
], function ($, Modes, Themes, Cryptpad, TextPatcher) { ], function ($, Modes, Themes, Messages, UIElements, Hash, Util, TextPatcher) {
var module = {}; var module = {};
var cursorToPos = function(cursor, oldText) { var cursorToPos = function(cursor, oldText) {
@ -101,7 +104,6 @@ define([
module.create = function (Common, defaultMode, CMeditor) { module.create = function (Common, defaultMode, CMeditor) {
var exp = {}; var exp = {};
var Messages = Cryptpad.Messages;
var CodeMirror = exp.CodeMirror = CMeditor; var CodeMirror = exp.CodeMirror = CMeditor;
CodeMirror.modeURL = "cm/mode/%N/%N"; CodeMirror.modeURL = "cm/mode/%N/%N";
@ -211,7 +213,7 @@ define([
isSelect: true, isSelect: true,
feedback: 'CODE_LANGUAGE', feedback: 'CODE_LANGUAGE',
}; };
var $block = exp.$language = Cryptpad.createDropdown(dropdownConfig); var $block = exp.$language = UIElements.createDropdown(dropdownConfig);
$block.find('button').attr('title', Messages.languageButtonTitle); $block.find('button').attr('title', Messages.languageButtonTitle);
$block.find('a').click(function () { $block.find('a').click(function () {
setMode($(this).attr('data-value'), onModeChanged); setMode($(this).attr('data-value'), onModeChanged);
@ -248,7 +250,7 @@ define([
initialValue: lastTheme, initialValue: lastTheme,
feedback: 'CODE_THEME', feedback: 'CODE_THEME',
}; };
var $block = exp.$theme = Cryptpad.createDropdown(dropdownConfig); var $block = exp.$theme = UIElements.createDropdown(dropdownConfig);
$block.find('button').attr('title', Messages.themeButtonTitle); $block.find('button').attr('title', Messages.themeButtonTitle);
setTheme(lastTheme, $block); setTheme(lastTheme, $block);
@ -324,8 +326,8 @@ define([
//var cursor = editor.getCursor(); //var cursor = editor.getCursor();
//var cleanName = data.name.replace(/[\[\]]/g, ''); //var cleanName = data.name.replace(/[\[\]]/g, '');
//var text = '!['+cleanName+']('+data.url+')'; //var text = '!['+cleanName+']('+data.url+')';
var parsed = Cryptpad.parsePadUrl(data.url); var parsed = Hash.parsePadUrl(data.url);
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel); var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' +
parsed.hashData.key + '"></media-tag>'; parsed.hashData.key + '"></media-tag>';

@ -2,8 +2,12 @@ define([
'jquery', 'jquery',
'/file/file-crypto.js', '/file/file-crypto.js',
'/common/common-thumbnail.js', '/common/common-thumbnail.js',
'/common/common-interface.js',
'/common/common-util.js',
'/customize/messages.js',
'/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/tweetnacl/nacl-fast.min.js',
], function ($, FileCrypto, Thumb) { ], function ($, FileCrypto, Thumb, UI, Util, Messages) {
var Nacl = window.nacl; var Nacl = window.nacl;
var module = {}; var module = {};
@ -15,15 +19,6 @@ define([
reader.readAsArrayBuffer(blob); reader.readAsArrayBuffer(blob);
}; };
var arrayBufferToString = function (AB) {
try {
return Nacl.util.encodeBase64(new Uint8Array(AB));
} catch (e) {
console.error(e);
return null;
}
};
module.uploadFile = function (common, data, cb) { module.uploadFile = function (common, data, cb) {
var sframeChan = common.getSframeChannel(); var sframeChan = common.getSframeChannel();
sframeChan.query('Q_UPLOAD_FILE', data, cb); sframeChan.query('Q_UPLOAD_FILE', data, cb);
@ -31,9 +26,6 @@ define([
module.create = function (common, config) { module.create = function (common, config) {
var File = {}; var File = {};
var Cryptpad = common.getCryptpadCommon();
var Messages = Cryptpad.Messages;
var queue = File.queue = { var queue = File.queue = {
queue: [], queue: [],
@ -94,7 +86,7 @@ define([
var id = file.id; var id = file.id;
var dropEvent = file.dropEvent; var dropEvent = file.dropEvent;
delete file.dropEvent; delete file.dropEvent;
if (dropEvent.path) { file.path = dropEvent.path; } if (dropEvent && dropEvent.path) { file.path = dropEvent.path; }
if (queue.inProgress) { return; } if (queue.inProgress) { return; }
queue.inProgress = true; queue.inProgress = true;
@ -123,7 +115,7 @@ define([
window.open(origin + $link.attr('href'), '_blank'); window.open(origin + $link.attr('href'), '_blank');
}); });
var title = metadata.name; var title = metadata.name;
Cryptpad.log(Messages._getKey('upload_success', [title])); UI.log(Messages._getKey('upload_success', [title]));
common.prepareFeedback('upload')(); common.prepareFeedback('upload')();
if (config.onUploaded) { if (config.onUploaded) {
@ -140,18 +132,18 @@ define([
queue.next(); queue.next();
if (e === 'TOO_LARGE') { if (e === 'TOO_LARGE') {
// TODO update table to say too big? // TODO update table to say too big?
return void Cryptpad.alert(Messages.upload_tooLarge); return void UI.alert(Messages.upload_tooLarge);
} }
if (e === 'NOT_ENOUGH_SPACE') { if (e === 'NOT_ENOUGH_SPACE') {
// TODO update table to say not enough space? // TODO update table to say not enough space?
return void Cryptpad.alert(Messages.upload_notEnoughSpace); return void UI.alert(Messages.upload_notEnoughSpace);
} }
console.error(e); console.error(e);
return void Cryptpad.alert(Messages.upload_serverError); return void UI.alert(Messages.upload_serverError);
}; };
onPending = function (cb) { onPending = function (cb) {
Cryptpad.confirm(Messages.upload_uploadPending, cb); UI.confirm(Messages.upload_uploadPending, cb);
}; };
file.noStore = config.noStore; file.noStore = config.noStore;
@ -161,14 +153,14 @@ define([
console.log('Upload started...'); console.log('Upload started...');
}); });
} catch (e) { } catch (e) {
Cryptpad.alert(Messages.upload_serverError); UI.alert(Messages.upload_serverError);
} }
}; };
var prettySize = function (bytes) { var prettySize = function (bytes) {
var kB = Cryptpad.bytesToKilobytes(bytes); var kB = Util.bytesToKilobytes(bytes);
if (kB < 1024) { return kB + Messages.KB; } if (kB < 1024) { return kB + Messages.KB; }
var mB = Cryptpad.bytesToMegabytes(bytes); var mB = Util.bytesToMegabytes(bytes);
return mB + Messages.MB; return mB + Messages.MB;
}; };
@ -220,12 +212,41 @@ define([
queue.next(); queue.next();
}; };
var handleFile = File.handleFile = function (file, e, thumbnail) { var showNamePrompt = true;
var promptName = function (file, cb) {
var extIdx = file.name.lastIndexOf('.');
var name = extIdx !== -1 ? file.name.slice(0,extIdx) : file.name;
var ext = extIdx !== -1 ? file.name.slice(extIdx) : "";
var msg = Messages._getKey('upload_rename', [
Util.fixHTML(file.name),
Util.fixHTML(ext)
]);
UI.prompt(msg, name, function (newName) {
if (newName === null) {
showNamePrompt = false;
return void cb (file.name);
}
if (!newName || !newName.trim()) { return void cb (file.name); }
var newExtIdx = newName.lastIndexOf('.');
var newExt = newExtIdx !== -1 ? newName.slice(newExtIdx) : "";
if (newExt !== ext) { newName += ext; }
cb(newName);
}, {cancel: Messages.doNotAskAgain}, true);
};
var handleFileState = {
queue: [],
inProgress: false
};
var handleFile = File.handleFile = function (file, e) {
//if (handleFileState.inProgress) { return void handleFileState.queue.push(file); }
handleFileState.inProgress = true;
var thumb; var thumb;
var file_arraybuffer; var file_arraybuffer;
var name = file.name;
var finish = function () { var finish = function () {
var metadata = { var metadata = {
name: file.name, name: name,
type: file.type, type: file.type,
}; };
if (thumb) { metadata.thumbnail = thumb; } if (thumb) { metadata.thumbnail = thumb; }
@ -234,33 +255,34 @@ define([
metadata: metadata, metadata: metadata,
dropEvent: e dropEvent: e
}); });
handleFileState.inProgress = false;
if (handleFileState.queue.length) { handleFile(handleFileState.queue.shift()); }
};
var getName = function () {
if (!showNamePrompt) { return void finish(); }
promptName(file, function (newName) {
name = newName;
finish();
});
}; };
blobToArrayBuffer(file, function (e, buffer) { blobToArrayBuffer(file, function (e, buffer) {
if (e) { console.error(e); } if (e) { console.error(e); }
file_arraybuffer = buffer; file_arraybuffer = buffer;
if (thumbnail) { // there is already a thumbnail if (!Thumb.isSupportedType(file.type)) { return getName(); }
return blobToArrayBuffer(thumbnail, function (e, buffer) {
if (e) { console.error(e); }
thumb = arrayBufferToString(buffer);
finish();
});
}
if (!Thumb.isSupportedType(file.type)) { return finish(); }
// make a resized thumbnail from the image.. // make a resized thumbnail from the image..
Thumb.fromBlob(file, function (e, thumb64) { Thumb.fromBlob(file, function (e, thumb64) {
if (e) { console.error(e); } if (e) { console.error(e); }
if (!thumb64) { return finish(); } if (!thumb64) { return getName(); }
thumb = thumb64; thumb = thumb64;
finish(); getName();
}); });
}); });
}; };
var onFileDrop = File.onFileDrop = function (file, e) { var onFileDrop = File.onFileDrop = function (file, e) {
if (!common.isLoggedIn()) { if (!common.isLoggedIn()) {
return Cryptpad.alert(common.Messages.upload_mustLogin); return UI.alert(common.Messages.upload_mustLogin);
} }
Array.prototype.slice.call(file).forEach(function (d) { Array.prototype.slice.call(file).forEach(function (d) {

@ -1,8 +1,10 @@
define([ define([
'jquery', 'jquery',
'/common/common-interface.js',
'/bower_components/chainpad-json-validator/json-ot.js', '/bower_components/chainpad-json-validator/json-ot.js',
'/bower_components/chainpad/chainpad.dist.js', '/bower_components/chainpad/chainpad.dist.js',
], function ($, JsonOT) { ], function ($, UI, JsonOT) {
var ChainPad = window.ChainPad; var ChainPad = window.ChainPad;
var History = {}; var History = {};
@ -83,7 +85,6 @@ define([
var onReady = function () { }; var onReady = function () { };
var Messages = common.Messages; var Messages = common.Messages;
var Cryptpad = common.getCryptpadCommon();
var realtime; var realtime;
@ -100,7 +101,7 @@ define([
$right.hide(); $right.hide();
$cke.hide(); $cke.hide();
Cryptpad.spinner($hist).get().show(); UI.spinner($hist).get().show();
var onUpdate; var onUpdate;
@ -203,11 +204,11 @@ define([
onClose(); onClose();
}); });
$rev.click(function () { $rev.click(function () {
Cryptpad.confirm(Messages.history_restorePrompt, function (yes) { UI.confirm(Messages.history_restorePrompt, function (yes) {
if (!yes) { return; } if (!yes) { return; }
close(); close();
onRevert(); onRevert();
Cryptpad.log(Messages.history_restoreDone); UI.log(Messages.history_restoreDone);
}); });
}); });

@ -15,9 +15,13 @@ define([
var Cryptpad; var Cryptpad;
var Crypto; var Crypto;
var Cryptget; var Cryptget;
var SFrameChannel;
var sframeChan; var sframeChan;
var FilePicker; var FilePicker;
var Messenger; var Messenger;
var Messaging;
var Notifier;
var Utils = {};
nThen(function (waitFor) { nThen(function (waitFor) {
// Load #2, the loading screen is up so grab whatever you need... // Load #2, the loading screen is up so grab whatever you need...
@ -29,14 +33,25 @@ define([
'/common/sframe-channel.js', '/common/sframe-channel.js',
'/filepicker/main.js', '/filepicker/main.js',
'/common/common-messenger.js', '/common/common-messenger.js',
], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, SFrameChannel, '/common/common-messaging.js',
_FilePicker, _Messenger) { '/common/common-notifier.js',
'/common/common-hash.js',
'/common/common-util.js',
'/common/common-realtime.js',
], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel,
_FilePicker, _Messenger, _Messaging, _Notifier, _Hash, _Util, _Realtime) {
CpNfOuter = _CpNfOuter; CpNfOuter = _CpNfOuter;
Cryptpad = _Cryptpad; Cryptpad = _Cryptpad;
Crypto = _Crypto; Crypto = _Crypto;
Cryptget = _Cryptget; Cryptget = _Cryptget;
SFrameChannel = _SFrameChannel;
FilePicker = _FilePicker; FilePicker = _FilePicker;
Messenger = _Messenger; Messenger = _Messenger;
Messaging = _Messaging;
Notifier = _Notifier;
Utils.Hash = _Hash;
Utils.Util = _Util;
Utils.Realtime = _Realtime;
if (localStorage.CRYPTPAD_URLARGS !== ApiConfig.requireConf.urlArgs) { if (localStorage.CRYPTPAD_URLARGS !== ApiConfig.requireConf.urlArgs) {
console.log("New version, flushing cache"); console.log("New version, flushing cache");
@ -82,16 +97,17 @@ define([
}); });
}); });
secret = cfg.getSecrets ? cfg.getSecrets(Cryptpad) : Cryptpad.getSecrets(); secret = cfg.getSecrets ? cfg.getSecrets(Cryptpad, Utils) : Utils.Hash.getSecrets();
if (!secret.channel) { if (!secret.channel) {
// New pad: create a new random channel id // New pad: create a new random channel id
secret.channel = Cryptpad.createChannelId(); secret.channel = Utils.Hash.createChannelId();
} }
Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; })); Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; }));
}).nThen(function () { }).nThen(function () {
var readOnly = secret.keys && !secret.keys.editKeyStr; var readOnly = secret.keys && !secret.keys.editKeyStr;
if (!secret.keys) { secret.keys = secret.key; } if (!secret.keys) { secret.keys = secret.key; }
var parsed = Cryptpad.parsePadUrl(window.location.href); var parsed = Utils.Hash.parsePadUrl(window.location.href);
if (!parsed.type) { throw new Error(); } if (!parsed.type) { throw new Error(); }
var defaultTitle = Cryptpad.getDefaultName(parsed); var defaultTitle = Cryptpad.getDefaultName(parsed);
var proxy = Cryptpad.getProxy(); var proxy = Cryptpad.getProxy();
@ -148,20 +164,45 @@ define([
sframeChan.onReg('EV_METADATA_UPDATE', updateMeta); sframeChan.onReg('EV_METADATA_UPDATE', updateMeta);
proxy.on('change', 'settings', updateMeta); proxy.on('change', 'settings', updateMeta);
Cryptpad.onError(function (info) { Cryptpad.onLogout(function () {
console.log('error'); sframeChan.event('EV_LOGOUT');
console.log(info);
if (info && info.type === "store") {
//onConnectError();
}
}); });
// Put in the following function the RPC queries that should also work in filepicker
var addCommonRpc = function (sframeChan) {
sframeChan.on('Q_ANON_RPC_MESSAGE', function (data, cb) { sframeChan.on('Q_ANON_RPC_MESSAGE', function (data, cb) {
Cryptpad.anonRpcMsg(data.msg, data.content, function (err, response) { Cryptpad.anonRpcMsg(data.msg, data.content, function (err, response) {
cb({error: err, response: response}); cb({error: err, response: response});
}); });
}); });
sframeChan.on('Q_GET_PIN_LIMIT_STATUS', function (data, cb) {
Cryptpad.isOverPinLimit(function (e, overLimit, limits) {
cb({
error: e,
overLimit: overLimit,
limits: limits
});
});
});
sframeChan.on('Q_THUMBNAIL_GET', function (data, cb) {
Cryptpad.getThumbnail(data.key, function (e, data) {
cb({
error: e,
data: data
});
});
});
sframeChan.on('Q_THUMBNAIL_SET', function (data, cb) {
Cryptpad.setThumbnail(data.key, data.value, function (e) {
cb({error:e});
});
});
};
addCommonRpc(sframeChan);
var currentTitle; var currentTitle;
var currentTabTitle; var currentTabTitle;
var setDocumentTitle = function () { var setDocumentTitle = function () {
@ -176,7 +217,7 @@ define([
currentTitle = newTitle; currentTitle = newTitle;
setDocumentTitle(); setDocumentTitle();
Cryptpad.renamePad(newTitle, undefined, function (err) { Cryptpad.renamePad(newTitle, undefined, function (err) {
if (err) { cb('ERROR'); } else { cb(); } cb(err);
}); });
}); });
sframeChan.on('EV_SET_TAB_TITLE', function (newTabTitle) { sframeChan.on('EV_SET_TAB_TITLE', function (newTabTitle) {
@ -203,7 +244,7 @@ define([
}); });
sframeChan.on('EV_NOTIFY', function () { sframeChan.on('EV_NOTIFY', function () {
Cryptpad.notify(); Notifier.notify();
}); });
sframeChan.on('Q_SET_LOGIN_REDIRECT', function (data, cb) { sframeChan.on('Q_SET_LOGIN_REDIRECT', function (data, cb) {
@ -211,16 +252,6 @@ define([
cb(); cb();
}); });
sframeChan.on('Q_GET_PIN_LIMIT_STATUS', function (data, cb) {
Cryptpad.isOverPinLimit(function (e, overLimit, limits) {
cb({
error: e,
overLimit: overLimit,
limits: limits
});
});
});
sframeChan.on('Q_MOVE_TO_TRASH', function (data, cb) { sframeChan.on('Q_MOVE_TO_TRASH', function (data, cb) {
cb = cb || $.noop; cb = cb || $.noop;
if (readOnly && hashes.editHash) { if (readOnly && hashes.editHash) {
@ -236,7 +267,7 @@ define([
}); });
sframeChan.on('Q_SEND_FRIEND_REQUEST', function (netfluxId, cb) { sframeChan.on('Q_SEND_FRIEND_REQUEST', function (netfluxId, cb) {
Cryptpad.inviteFromUserlist(Cryptpad, netfluxId); Messaging.inviteFromUserlist(Cryptpad, netfluxId);
cb(); cb();
}); });
Cryptpad.onFriendRequest = function (confirmText, cb) { Cryptpad.onFriendRequest = function (confirmText, cb) {
@ -310,20 +341,6 @@ define([
}); });
}); });
sframeChan.on('Q_THUMBNAIL_GET', function (data, cb) {
Cryptpad.getThumbnail(data.key, function (e, data) {
cb({
error: e,
data: data
});
});
});
sframeChan.on('Q_THUMBNAIL_SET', function (data, cb) {
Cryptpad.setThumbnail(data.key, data.value, function (e) {
cb({error:e});
});
});
sframeChan.on('Q_SESSIONSTORAGE_PUT', function (data, cb) { sframeChan.on('Q_SESSIONSTORAGE_PUT', function (data, cb) {
sessionStorage[data.key] = data.value; sessionStorage[data.key] = data.value;
cb(); cb();
@ -332,11 +349,11 @@ define([
// Present mode URL // Present mode URL
sframeChan.on('Q_PRESENT_URL_GET_VALUE', function (data, cb) { sframeChan.on('Q_PRESENT_URL_GET_VALUE', function (data, cb) {
var parsed = Cryptpad.parsePadUrl(window.location.href); var parsed = Utils.Hash.parsePadUrl(window.location.href);
cb(parsed.hashData && parsed.hashData.present); cb(parsed.hashData && parsed.hashData.present);
}); });
sframeChan.on('EV_PRESENT_URL_SET_VALUE', function (data) { sframeChan.on('EV_PRESENT_URL_SET_VALUE', function (data) {
var parsed = Cryptpad.parsePadUrl(window.location.href); var parsed = Utils.Hash.parsePadUrl(window.location.href);
window.location.href = parsed.getUrl({ window.location.href = parsed.getUrl({
embed: parsed.hashData.embed, embed: parsed.hashData.embed,
present: data present: data
@ -346,6 +363,7 @@ define([
// File upload // File upload
var onFileUpload = function (sframeChan, data, cb) { var onFileUpload = function (sframeChan, data, cb) {
require(['/common/outer/upload.js'], function (Files) {
var sendEvent = function (data) { var sendEvent = function (data) {
sframeChan.event("EV_FILE_UPLOAD_STATE", data); sframeChan.event("EV_FILE_UPLOAD_STATE", data);
}; };
@ -373,8 +391,9 @@ define([
}); });
}; };
data.blob = Crypto.Nacl.util.decodeBase64(data.blob); data.blob = Crypto.Nacl.util.decodeBase64(data.blob);
Cryptpad.uploadFileSecure(data, data.noStore, Cryptpad, updateProgress, onComplete, onError, onPending); Files.upload(data, data.noStore, Cryptpad, updateProgress, onComplete, onError, onPending);
cb(); cb();
});
}; };
sframeChan.on('Q_UPLOAD_FILE', function (data, cb) { sframeChan.on('Q_UPLOAD_FILE', function (data, cb) {
onFileUpload(sframeChan, data, cb); onFileUpload(sframeChan, data, cb);
@ -393,6 +412,11 @@ define([
}; };
config.onFileUpload = onFileUpload; config.onFileUpload = onFileUpload;
config.types = cfg; config.types = cfg;
config.addCommonRpc = addCommonRpc;
config.modules = {
Cryptpad: Cryptpad,
SFrameChannel: SFrameChannel
};
FP.$iframe = $('<iframe>', {id: 'sbox-filePicker-iframe'}).appendTo($('body')); FP.$iframe = $('<iframe>', {id: 'sbox-filePicker-iframe'}).appendTo($('body'));
FP.picker = FilePicker.create(config); FP.picker = FilePicker.create(config);
} else { } else {
@ -458,7 +482,7 @@ define([
}); });
if (cfg.addRpc) { if (cfg.addRpc) {
cfg.addRpc(sframeChan, Cryptpad); cfg.addRpc(sframeChan, Cryptpad, Utils);
} }
if (cfg.messaging) { if (cfg.messaging) {
@ -575,6 +599,18 @@ define([
if (!realtime) { return; } if (!realtime) { return; }
var replaceHash = function (hash) {
if (window.history && window.history.replaceState) {
if (!/^#/.test(hash)) { hash = '#' + hash; }
void window.history.replaceState({}, window.document.title, hash);
if (typeof(window.onhashchange) === 'function') {
window.onhashchange();
}
return;
}
window.location.hash = hash;
};
CpNfOuter.start({ CpNfOuter.start({
sframeChan: sframeChan, sframeChan: sframeChan,
channel: secret.channel, channel: secret.channel,
@ -591,7 +627,7 @@ define([
return; return;
} }
if (readOnly || cfg.noHash) { return; } if (readOnly || cfg.noHash) { return; }
Cryptpad.replaceHash(Cryptpad.getEditHashFromKeys(wc.id, secret.keys)); replaceHash(Utils.Hash.getEditHashFromKeys(wc.id, secret.keys));
} }
}); });
}); });

@ -1,7 +1,9 @@
define([ define([
'jquery', 'jquery',
'/common/common-util.js' '/common/common-util.js',
], function ($, Util) { '/common/common-interface.js',
'/customize/messages.js'
], function ($, Util, UI, Messages) {
var module = {}; var module = {};
module.create = function (Common, cfg) { module.create = function (Common, cfg) {
@ -54,7 +56,9 @@ define([
}); });
metadataMgr.onTitleChange(function (title) { metadataMgr.onTitleChange(function (title) {
sframeChan.query('Q_SET_PAD_TITLE_IN_DRIVE', title, function (err) { sframeChan.query('Q_SET_PAD_TITLE_IN_DRIVE', title, function (err) {
if (err) { return; } if (err === 'E_OVER_LIMIT') {
return void UI.alert(Messages.pinLimitNotPinned, null, true);
} else if (err) { return; }
evTitleChange.fire(title); evTitleChange.fire(title);
if (titleUpdated) { titleUpdated(undefined, title); } if (titleUpdated) { titleUpdated(undefined, title); }
}); });

@ -5,17 +5,18 @@ define([
'/common/sframe-chainpad-netflux-inner.js', '/common/sframe-chainpad-netflux-inner.js',
'/common/sframe-channel.js', '/common/sframe-channel.js',
'/common/sframe-common-title.js', '/common/sframe-common-title.js',
'/common/sframe-common-interface.js', '/common/common-ui-elements.js',
'/common/sframe-common-history.js', '/common/sframe-common-history.js',
'/common/sframe-common-file.js', '/common/sframe-common-file.js',
'/common/sframe-common-codemirror.js', '/common/sframe-common-codemirror.js',
'/common/metadata-manager.js', '/common/metadata-manager.js',
'/customize/application_config.js', '/customize/application_config.js',
'/common/cryptpad-common.js',
'/common/common-realtime.js', '/common/common-realtime.js',
'/common/common-util.js', '/common/common-util.js',
'/common/common-hash.js',
'/common/common-thumbnail.js', '/common/common-thumbnail.js',
'/common/common-interface.js',
'/bower_components/localforage/dist/localforage.min.js' '/bower_components/localforage/dist/localforage.min.js'
], function ( ], function (
$, $,
@ -24,16 +25,17 @@ define([
CpNfInner, CpNfInner,
SFrameChannel, SFrameChannel,
Title, Title,
UI, UIElements,
History, History,
File, File,
CodeMirror, CodeMirror,
MetadataMgr, MetadataMgr,
AppConfig, AppConfig,
Cryptpad,
CommonRealtime, CommonRealtime,
Util, Util,
Hash,
Thumb, Thumb,
UI,
localForage localForage
) { ) {
// Chainpad Netflux Inner // Chainpad Netflux Inner
@ -55,7 +57,6 @@ define([
}; };
funcs.getMetadataMgr = function () { return ctx.metadataMgr; }; funcs.getMetadataMgr = function () { return ctx.metadataMgr; };
funcs.getCryptpadCommon = function () { return Cryptpad; };
funcs.getSframeChannel = function () { return ctx.sframeChan; }; funcs.getSframeChannel = function () { return ctx.sframeChan; };
funcs.getAppConfig = function () { return AppConfig; }; funcs.getAppConfig = function () { return AppConfig; };
@ -74,16 +75,16 @@ define([
}; };
// UI // UI
funcs.createUserAdminMenu = callWithCommon(UI.createUserAdminMenu); funcs.createUserAdminMenu = callWithCommon(UIElements.createUserAdminMenu);
funcs.initFilePicker = callWithCommon(UI.initFilePicker); funcs.initFilePicker = callWithCommon(UIElements.initFilePicker);
funcs.openFilePicker = callWithCommon(UI.openFilePicker); funcs.openFilePicker = callWithCommon(UIElements.openFilePicker);
funcs.openTemplatePicker = callWithCommon(UI.openTemplatePicker); funcs.openTemplatePicker = callWithCommon(UIElements.openTemplatePicker);
funcs.displayMediatagImage = callWithCommon(UI.displayMediatagImage); funcs.displayMediatagImage = callWithCommon(UIElements.displayMediatagImage);
funcs.displayAvatar = callWithCommon(UI.displayAvatar); funcs.displayAvatar = callWithCommon(UIElements.displayAvatar);
funcs.createButton = callWithCommon(UI.createButton); funcs.createButton = callWithCommon(UIElements.createButton);
funcs.createUsageBar = callWithCommon(UI.createUsageBar); funcs.createUsageBar = callWithCommon(UIElements.createUsageBar);
funcs.updateTags = callWithCommon(UI.updateTags); funcs.updateTags = callWithCommon(UIElements.updateTags);
funcs.createLanguageSelector = callWithCommon(UI.createLanguageSelector); funcs.createLanguageSelector = callWithCommon(UIElements.createLanguageSelector);
// Thumb // Thumb
funcs.displayThumbnail = callWithCommon(Thumb.displayThumbnail); funcs.displayThumbnail = callWithCommon(Thumb.displayThumbnail);
@ -102,21 +103,21 @@ define([
return '<script src="' + origin + '/common/media-tag-nacl.min.js"></script>'; return '<script src="' + origin + '/common/media-tag-nacl.min.js"></script>';
}; };
funcs.getMediatagFromHref = function (href) { funcs.getMediatagFromHref = function (href) {
var parsed = Cryptpad.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
var secret = Cryptpad.getSecrets('file', parsed.hash); var secret = Hash.getSecrets('file', parsed.hash);
var data = ctx.metadataMgr.getPrivateData(); var data = ctx.metadataMgr.getPrivateData();
if (secret.keys && secret.channel) { if (secret.keys && secret.channel) {
var cryptKey = secret.keys && secret.keys.fileKeyStr; var cryptKey = secret.keys && secret.keys.fileKeyStr;
var hexFileName = Cryptpad.base64ToHex(secret.channel); var hexFileName = Util.base64ToHex(secret.channel);
var origin = data.fileHost || data.origin; var origin = data.fileHost || data.origin;
var src = origin + Cryptpad.getBlobPathFromHex(hexFileName); var src = origin + Hash.getBlobPathFromHex(hexFileName);
return '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + cryptKey + '">' + return '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + cryptKey + '">' +
'</media-tag>'; '</media-tag>';
} }
return; return;
}; };
funcs.getFileSize = function (href, cb) { funcs.getFileSize = function (href, cb) {
var channelId = Cryptpad.hrefToHexChannelId(href); var channelId = Hash.hrefToHexChannelId(href);
funcs.sendAnonRpcMsg("GET_FILE_SIZE", channelId, function (data) { funcs.sendAnonRpcMsg("GET_FILE_SIZE", channelId, function (data) {
if (!data) { return void cb("No response"); } if (!data) { return void cb("No response"); }
if (data.error) { return void cb(data.error); } if (data.error) { return void cb(data.error); }
@ -313,6 +314,14 @@ define([
funcs.whenRealtimeSyncs = evRealtimeSynced.reg; funcs.whenRealtimeSyncs = evRealtimeSynced.reg;
var logoutHandlers = [];
funcs.onLogout = function (h) {
if (typeof (h) !== "function") { return; }
if (logoutHandlers.indexOf(h) !== -1) { return; }
logoutHandlers.push(h);
};
Object.freeze(funcs); Object.freeze(funcs);
return { create: function (cb) { return { create: function (cb) {
@ -359,17 +368,30 @@ define([
UI.addTooltips(); UI.addTooltips();
ctx.sframeChan.on('EV_LOGOUT', function () {
$(window).on('keyup', function (e) {
if (e.keyCode === 27) {
UI.removeLoadingScreen();
}
});
UI.addLoadingScreen({hideTips: true});
UI.errorLoadingScreen(Messages.onLogout, true);
logoutHandlers.forEach(function (h) {
if (typeof (h) === "function") { h(); }
});
});
ctx.sframeChan.on('EV_RT_CONNECT', function () { CommonRealtime.setConnectionState(true); }); ctx.sframeChan.on('EV_RT_CONNECT', function () { CommonRealtime.setConnectionState(true); });
ctx.sframeChan.on('EV_RT_DISCONNECT', function () { CommonRealtime.setConnectionState(false); }); ctx.sframeChan.on('EV_RT_DISCONNECT', function () { CommonRealtime.setConnectionState(false); });
ctx.sframeChan.on('Q_INCOMING_FRIEND_REQUEST', function (confirmMsg, cb) { ctx.sframeChan.on('Q_INCOMING_FRIEND_REQUEST', function (confirmMsg, cb) {
Cryptpad.confirm(confirmMsg, cb, null, true); UI.confirm(confirmMsg, cb, null, true);
}); });
ctx.sframeChan.on('EV_FRIEND_REQUEST', function (data) { ctx.sframeChan.on('EV_FRIEND_REQUEST', function (data) {
var i = pendingFriends.indexOf(data.netfluxId); var i = pendingFriends.indexOf(data.netfluxId);
if (i !== -1) { pendingFriends.splice(i, 1); } if (i !== -1) { pendingFriends.splice(i, 1); }
Cryptpad.log(data.logText); UI.log(data.logText);
}); });
ctx.sframeChan.ready(); ctx.sframeChan.ready();

@ -1,677 +0,0 @@
define([
'jquery',
'/bower_components/chainpad-crypto/crypto.js',
'/common/curve.js',
'/common/common-hash.js',
], function ($, Crypto, Curve, Hash) {
'use strict';
var Msg = {
inputs: [],
};
var Types = {
message: 'MSG',
update: 'UPDATE',
unfriend: 'UNFRIEND',
mapId: 'MAP_ID',
mapIdAck: 'MAP_ID_ACK'
};
var clone = function (o) {
return JSON.parse(JSON.stringify(o));
};
// TODO
// - mute a channel (hide notifications or don't open it?)
var createData = Msg.createData = function (proxy, hash) {
return {
channel: hash || Hash.createChannelId(),
displayName: proxy['cryptpad.username'],
profile: proxy.profile && proxy.profile.view,
edPublic: proxy.edPublic,
curvePublic: proxy.curvePublic,
avatar: proxy.profile && proxy.profile.avatar
};
};
var getFriend = function (proxy, pubkey) {
if (pubkey === proxy.curvePublic) {
var data = createData(proxy);
delete data.channel;
return data;
}
return proxy.friends ? proxy.friends[pubkey] : undefined;
};
var getFriendList = Msg.getFriendList = function (proxy) {
if (!proxy.friends) { proxy.friends = {}; }
return proxy.friends;
};
var eachFriend = function (friends, cb) {
Object.keys(friends).forEach(function (id) {
if (id === 'me') { return; }
cb(friends[id], id, friends);
});
};
Msg.getFriendChannelsList = function (proxy) {
var list = [];
eachFriend(proxy, function (friend) {
list.push(friend.channel);
});
return list;
};
var msgAlreadyKnown = function (channel, sig) {
return channel.messages.some(function (message) {
return message[0] === sig;
});
};
Msg.messenger = function (common) {
var messenger = {
handlers: {
message: [],
join: [],
leave: [],
update: [],
friend: [],
unfriend: [],
},
range_requests: {},
};
var eachHandler = function (type, g) {
messenger.handlers[type].forEach(g);
};
messenger.on = function (type, f) {
var stack = messenger.handlers[type];
if (!Array.isArray(stack)) {
return void console.error('unsupported message type');
}
if (typeof(f) !== 'function') {
return void console.error('expected function');
}
stack.push(f);
};
var channels = messenger.channels = {};
var joining = {};
// declare common variables
var network = common.getNetwork();
var proxy = common.getProxy();
var realtime = common.getRealtime();
Msg.hk = network.historyKeeper;
var friends = getFriendList(proxy);
var getChannel = function (curvePublic) {
var friend = friends[curvePublic];
if (!friend) { return; }
var chanId = friend.channel;
if (!chanId) { return; }
return channels[chanId];
};
var initRangeRequest = function (txid, curvePublic, sig, cb) {
messenger.range_requests[txid] = {
messages: [],
cb: cb,
curvePublic: curvePublic,
sig: sig,
};
};
var getRangeRequest = function (txid) {
return messenger.range_requests[txid];
};
var deleteRangeRequest = function (txid) {
delete messenger.range_requests[txid];
};
messenger.getMoreHistory = function (curvePublic, hash, count, cb) {
if (typeof(cb) !== 'function') { return; }
if (typeof(hash) !== 'string') {
// FIXME hash is not necessarily defined.
// What does this mean?
console.error("not sure what to do here");
return;
}
var chan = getChannel(curvePublic);
if (typeof(chan) === 'undefined') {
console.error("chan is undefined. we're going to have a problem here");
return;
}
var txid = common.uid();
initRangeRequest(txid, curvePublic, hash, cb);
var msg = [ 'GET_HISTORY_RANGE', chan.id, {
from: hash,
count: count,
txid: txid,
}
];
network.sendto(network.historyKeeper, JSON.stringify(msg)).then(function () {
}, function (err) {
throw new Error(err);
});
};
var getCurveForChannel = function (id) {
var channel = channels[id];
if (!channel) { return; }
return channel.curve;
};
messenger.getChannelHead = function (curvePublic, cb) {
var friend = friends[curvePublic];
if (!friend) { return void cb('NO_SUCH_FRIEND'); }
cb(void 0, friend.lastKnownHash);
};
messenger.setChannelHead = function (curvePublic, hash, cb) {
var friend = friends[curvePublic];
if (!friend) { return void cb('NO_SUCH_FRIEND'); }
friend.lastKnownHash = hash;
cb();
};
// Id message allows us to map a netfluxId with a public curve key
var onIdMessage = function (msg, sender) {
var channel;
var isId = Object.keys(channels).some(function (chanId) {
if (channels[chanId].userList.indexOf(sender) !== -1) {
channel = channels[chanId];
return true;
}
});
if (!isId) { return; }
var decryptedMsg = channel.encryptor.decrypt(msg);
if (decryptedMsg === null) {
return void console.error("Failed to decrypt message");
}
if (!decryptedMsg) {
console.error('decrypted message was falsey but not null');
return;
}
var parsed;
try {
parsed = JSON.parse(decryptedMsg);
} catch (e) {
console.error(decryptedMsg);
return;
}
if (parsed[0] !== Types.mapId && parsed[0] !== Types.mapIdAck) { return; }
// check that the responding peer's encrypted netflux id matches
// the sender field. This is to prevent replay attacks.
if (parsed[2] !== sender || !parsed[1]) { return; }
channel.mapId[sender] = parsed[1];
eachHandler('join', function (f) {
f(parsed[1], channel.id);
});
if (parsed[0] !== Types.mapId) { return; } // Don't send your key if it's already an ACK
// Answer with your own key
var rMsg = [Types.mapIdAck, proxy.curvePublic, channel.wc.myID];
var rMsgStr = JSON.stringify(rMsg);
var cryptMsg = channel.encryptor.encrypt(rMsgStr);
network.sendto(sender, cryptMsg);
};
var orderMessages = function (curvePublic, new_messages /*, sig */) {
var channel = getChannel(curvePublic);
var messages = channel.messages;
// TODO improve performance, guarantee correct ordering
new_messages.reverse().forEach(function (msg) {
messages.unshift(msg);
});
};
var removeFromFriendList = function (curvePublic, cb) {
if (!proxy.friends) { return; }
var friends = proxy.friends;
delete friends[curvePublic];
common.whenRealtimeSyncs(realtime, cb);
};
var pushMsg = function (channel, cryptMsg) {
var msg = channel.encryptor.decrypt(cryptMsg);
var sig = cryptMsg.slice(0, 64);
if (msgAlreadyKnown(channel, sig)) { return; }
var parsedMsg = JSON.parse(msg);
var curvePublic;
if (parsedMsg[0] === Types.message) {
// TODO validate messages here
var res = {
type: parsedMsg[0],
sig: sig,
author: parsedMsg[1],
time: parsedMsg[2],
text: parsedMsg[3],
// this makes debugging a whole lot easier
curve: getCurveForChannel(channel.id),
};
channel.messages.push(res);
eachHandler('message', function (f) {
f(res);
});
return true;
}
if (parsedMsg[0] === Types.update) {
if (parsedMsg[1] === proxy.curvePublic) { return; }
curvePublic = parsedMsg[1];
var newdata = parsedMsg[3];
var data = getFriend(proxy, parsedMsg[1]);
var types = [];
Object.keys(newdata).forEach(function (k) {
if (data[k] !== newdata[k]) {
types.push(k);
data[k] = newdata[k];
}
});
eachHandler('update', function (f) {
f(clone(newdata), curvePublic);
});
return;
}
if (parsedMsg[0] === Types.unfriend) {
curvePublic = parsedMsg[1];
delete friends[curvePublic];
removeFromFriendList(parsedMsg[1], function () {
channel.wc.leave(Types.unfriend);
eachHandler('unfriend', function (f) {
f(curvePublic);
});
});
return;
}
};
/* Broadcast a display name, profile, or avatar change to all contacts
*/
// TODO send event...
messenger.updateMyData = function () {
var friends = getFriendList(proxy);
var mySyncData = friends.me;
var myData = createData(proxy);
if (!mySyncData || mySyncData.displayName !== myData.displayName
|| mySyncData.profile !== myData.profile
|| mySyncData.avatar !== myData.avatar) {
delete myData.channel;
Object.keys(channels).forEach(function (chan) {
var channel = channels[chan];
if (!channel) {
return void console.error('NO_SUCH_CHANNEL');
}
var msg = [Types.update, myData.curvePublic, +new Date(), myData];
var msgStr = JSON.stringify(msg);
var cryptMsg = channel.encryptor.encrypt(msgStr);
channel.wc.bcast(cryptMsg).then(function () {
// TODO send event
//channel.refresh();
}, function (err) {
console.error(err);
});
});
eachHandler('update', function (f) {
f(myData, myData.curvePublic);
});
friends.me = myData;
}
};
var onChannelReady = function (chanId) {
var cb = joining[chanId];
if (typeof(cb) !== 'function') {
return void console.error('channel ready without callback');
}
delete joining[chanId];
return cb();
};
var onDirectMessage = function (common, msg, sender) {
if (sender !== Msg.hk) { return void onIdMessage(msg, sender); }
var parsed = JSON.parse(msg);
if (/HISTORY_RANGE/.test(parsed[0])) {
//console.log(parsed);
var txid = parsed[1];
var req = getRangeRequest(txid);
var type = parsed[0];
if (!req) {
return void console.error("received response to unknown request");
}
if (type === 'HISTORY_RANGE') {
req.messages.push(parsed[2]);
} else if (type === 'HISTORY_RANGE_END') {
// process all the messages (decrypt)
var curvePublic = req.curvePublic;
var channel = getChannel(curvePublic);
var decrypted = req.messages.map(function (msg) {
if (msg[2] !== 'MSG') { return; }
try {
return {
d: JSON.parse(channel.encryptor.decrypt(msg[4])),
sig: msg[4].slice(0, 64),
};
} catch (e) {
console.log('failed to decrypt');
return null;
}
}).filter(function (decrypted) {
return decrypted;
}).map(function (O) {
return {
type: O.d[0],
sig: O.sig,
author: O.d[1],
time: O.d[2],
text: O.d[3],
curve: curvePublic,
};
});
orderMessages(curvePublic, decrypted, req.sig);
req.cb(void 0, decrypted);
return deleteRangeRequest(txid);
} else {
console.log(parsed);
}
return;
}
if ((parsed.validateKey || parsed.owners) && parsed.channel) {
return;
}
if (parsed.state && parsed.state === 1 && parsed.channel) {
if (channels[parsed.channel]) {
// parsed.channel is Ready
// channel[parsed.channel].ready();
channels[parsed.channel].ready = true;
onChannelReady(parsed.channel);
var updateTypes = channels[parsed.channel].updateOnReady;
if (updateTypes) {
//channels[parsed.channel].updateUI(updateTypes);
}
}
return;
}
var chan = parsed[3];
if (!chan || !channels[chan]) { return; }
pushMsg(channels[chan], parsed[4]);
};
var onMessage = function (msg, sender, chan) {
if (!channels[chan.id]) { return; }
var isMessage = pushMsg(channels[chan.id], msg);
if (isMessage) {
if (channels[chan.id].wc.myID !== sender) {
// Don't notify for your own messages
//channels[chan.id].notify();
}
//channels[chan.id].refresh();
// TODO emit message event
}
};
// listen for messages...
network.on('message', function(msg, sender) {
onDirectMessage(common, msg, sender);
});
messenger.removeFriend = function (curvePublic, cb) {
if (typeof(cb) !== 'function') { throw new Error('NO_CALLBACK'); }
var data = getFriend(proxy, curvePublic);
if (!data) {
// friend is not valid
console.error('friend is not valid');
return;
}
var channel = channels[data.channel];
if (!channel) {
return void cb("NO_SUCH_CHANNEL");
}
if (!network.webChannels.some(function (wc) {
return wc.id === channel.id;
})) {
console.error('bad channel: ', curvePublic);
}
var msg = [Types.unfriend, proxy.curvePublic, +new Date()];
var msgStr = JSON.stringify(msg);
var cryptMsg = channel.encryptor.encrypt(msgStr);
// TODO emit remove_friend event?
try {
channel.wc.bcast(cryptMsg).then(function () {
delete friends[curvePublic];
delete channels[curvePublic];
common.whenRealtimeSyncs(realtime, function () {
cb();
});
}, function (err) {
console.error(err);
cb(err);
});
} catch (e) {
cb(e);
}
};
var getChannelMessagesSince = function (chan, data, keys) {
console.log('Fetching [%s] messages since [%s]', data.curvePublic, data.lastKnownHash || '');
var cfg = {
validateKey: keys.validateKey,
owners: [proxy.edPublic, data.edPublic],
lastKnownHash: data.lastKnownHash
};
var msg = ['GET_HISTORY', chan.id, cfg];
network.sendto(network.historyKeeper, JSON.stringify(msg))
.then($.noop, function (err) {
throw new Error(err);
});
};
var openFriendChannel = function (data, f) {
var keys = Curve.deriveKeys(data.curvePublic, proxy.curvePrivate);
var encryptor = Curve.createEncryptor(keys);
network.join(data.channel).then(function (chan) {
var channel = channels[data.channel] = {
id: data.channel,
sending: false,
friendEd: f,
keys: keys,
curve: data.curvePublic,
encryptor: encryptor,
messages: [],
wc: chan,
userList: [],
mapId: {},
send: function (payload, cb) {
if (!network.webChannels.some(function (wc) {
if (wc.id === channel.wc.id) { return true; }
})) {
return void cb('NO_SUCH_CHANNEL');
}
var msg = [Types.message, proxy.curvePublic, +new Date(), payload];
var msgStr = JSON.stringify(msg);
var cryptMsg = channel.encryptor.encrypt(msgStr);
channel.wc.bcast(cryptMsg).then(function () {
pushMsg(channel, cryptMsg);
cb();
}, function (err) {
cb(err);
});
}
};
chan.on('message', function (msg, sender) {
onMessage(msg, sender, chan);
});
var onJoining = function (peer) {
if (peer === Msg.hk) { return; }
if (channel.userList.indexOf(peer) !== -1) { return; }
channel.userList.push(peer);
var msg = [Types.mapId, proxy.curvePublic, chan.myID];
var msgStr = JSON.stringify(msg);
var cryptMsg = channel.encryptor.encrypt(msgStr);
network.sendto(peer, cryptMsg);
};
chan.members.forEach(function (peer) {
if (peer === Msg.hk) { return; }
if (channel.userList.indexOf(peer) !== -1) { return; }
channel.userList.push(peer);
});
chan.on('join', onJoining);
chan.on('leave', function (peer) {
var curvePublic = channel.mapId[peer];
var i = channel.userList.indexOf(peer);
while (i !== -1) {
channel.userList.splice(i, 1);
i = channel.userList.indexOf(peer);
}
// update status
if (!curvePublic) { return; }
eachHandler('leave', function (f) {
f(curvePublic, channel.id);
});
});
// FIXME don't subscribe to the channel implicitly
getChannelMessagesSince(chan, data, keys);
}, function (err) {
console.error(err);
});
};
messenger.getFriendList = function (cb) {
var friends = proxy.friends;
if (!friends) { return void cb(void 0, []); }
cb(void 0, Object.keys(proxy.friends).filter(function (k) {
return k !== 'me';
}));
};
messenger.openFriendChannel = function (curvePublic, cb) {
if (typeof(curvePublic) !== 'string') { return void cb('INVALID_ID'); }
if (typeof(cb) !== 'function') { throw new Error('expected callback'); }
var friend = clone(friends[curvePublic]);
if (typeof(friend) !== 'object') {
return void cb('NO_FRIEND_DATA');
}
var channel = friend.channel;
if (!channel) { return void cb('E_NO_CHANNEL'); }
joining[channel] = cb;
openFriendChannel(friend, curvePublic);
};
messenger.sendMessage = function (curvePublic, payload, cb) {
var channel = getChannel(curvePublic);
if (!channel) { return void cb('NO_CHANNEL'); }
if (!network.webChannels.some(function (wc) {
if (wc.id === channel.wc.id) { return true; }
})) {
return void cb('NO_SUCH_CHANNEL');
}
var msg = [Types.message, proxy.curvePublic, +new Date(), payload];
var msgStr = JSON.stringify(msg);
var cryptMsg = channel.encryptor.encrypt(msgStr);
channel.wc.bcast(cryptMsg).then(function () {
pushMsg(channel, cryptMsg);
cb();
}, function (err) {
cb(err);
});
};
messenger.getStatus = function (curvePublic, cb) {
var channel = getChannel(curvePublic);
if (!channel) { return void cb('NO_SUCH_CHANNEL'); }
var online = channel.userList.some(function (nId) {
return channel.mapId[nId] === curvePublic;
});
cb(void 0, online);
};
messenger.getFriendInfo = function (curvePublic, cb) {
setTimeout(function () {
var friend = friends[curvePublic];
if (!friend) { return void cb('NO_SUCH_FRIEND'); }
// this clone will be redundant when ui uses postmessage
cb(void 0, clone(friend));
});
};
messenger.getMyInfo = function (cb) {
cb(void 0, {
curvePublic: proxy.curvePublic,
displayName: common.getDisplayName(),
});
};
// TODO listen for changes to your friend list
// emit 'update' events for clients
//var update = function (curvePublic
proxy.on('change', ['friends'], function (o, n, p) {
var curvePublic;
if (o === undefined) {
// new friend added
curvePublic = p.slice(-1)[0];
eachHandler('friend', function (f) {
f(curvePublic, clone(n));
});
return;
}
console.error(o, n, p);
}).on('remove', ['friends'], function (o, p) {
eachHandler('unfriend', function (f) {
f(p[1]); // TODO
});
});
Object.freeze(messenger);
return messenger;
};
return Msg;
});

@ -56,6 +56,8 @@ define({
// Log the user out in all the tabs // Log the user out in all the tabs
'Q_LOGOUT': true, 'Q_LOGOUT': true,
// Tell the user that he has been logged out from outside (probably from another tab)
'EV_LOGOUT': true,
// When moving to the login or register page from a pad, we need to redirect to that pad at the // When moving to the login or register page from a pad, we need to redirect to that pad at the
// end of the login process. This query set the current href to the sessionStorage. // end of the login process. This query set the current href to the sessionStorage.

@ -1,865 +0,0 @@
define([
'jquery',
'/customize/application_config.js',
'/api/config'
], function ($, Config, ApiConfig) {
var urlArgs = ApiConfig.requireConf.urlArgs;
var Messages = {};
var Bar = {
constants: {},
};
/** Id of the div containing the user list. */
var USER_LIST_CLS = Bar.constants.userlist = 'cryptpad-user-list';
/** Id of the div containing the lag info. */
var LAG_ELEM_CLS = Bar.constants.lag = 'cryptpad-lag';
var LIMIT_ELEM_CLS = Bar.constants.lag = 'cryptpad-limit';
/** The toolbar class which contains the user list, debug link and lag. */
var TOOLBAR_CLS = Bar.constants.toolbar = 'cryptpad-toolbar';
var TOP_CLS = Bar.constants.top = 'cryptpad-toolbar-top';
var LEFTSIDE_CLS = Bar.constants.leftside = 'cryptpad-toolbar-leftside';
var RIGHTSIDE_CLS = Bar.constants.rightside = 'cryptpad-toolbar-rightside';
var HISTORY_CLS = Bar.constants.history = 'cryptpad-toolbar-history';
var SPINNER_CLS = Bar.constants.spinner = 'cryptpad-spinner';
var STATE_CLS = Bar.constants.state = 'cryptpad-state';
var USERNAME_CLS = Bar.constants.username = 'cryptpad-toolbar-username';
var READONLY_CLS = Bar.constants.readonly = 'cryptpad-readonly';
var USERBUTTONS_CONTAINER_CLS = Bar.constants.userButtonsContainer = "cryptpad-userbuttons-container";
var USERLIST_CLS = Bar.constants.userlist = "cryptpad-dropdown-users";
var EDITSHARE_CLS = Bar.constants.editShare = "cryptpad-dropdown-editShare";
var VIEWSHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-viewShare";
var SHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-share";
var DROPDOWN_CONTAINER_CLS = Bar.constants.dropdownContainer = "cryptpad-dropdown-container";
var DROPDOWN_CLS = Bar.constants.dropdown = "cryptpad-dropdown";
var TITLE_CLS = Bar.constants.title = "cryptpad-title";
var USER_CLS = Bar.constants.userAdmin = "cryptpad-user";
var USERBUTTON_CLS = Bar.constants.changeUsername = "cryptpad-change-username";
var SPINNER_DISAPPEAR_TIME = 3000;
var uid = function () {
return 'cryptpad-uid-' + String(Math.random()).substring(2);
};
var $style;
var connected = false;
var firstConnection = true;
var lagErrors = 0;
var styleToolbar = function ($container, href, version) {
href = href || '/customize/toolbar.css' + (version?('?' + version): '');
$.ajax({
url: href,
dataType: 'text',
success: function (data) {
$container.append($('<style>').text(data));
},
});
};
var createRealtimeToolbar = function ($container, config) {
var $toolbar = $('<div>', {
'class': TOOLBAR_CLS,
id: uid(),
})
.append($('<div>', {'class': TOP_CLS}))
.append($('<div>', {'class': LEFTSIDE_CLS}))
.append($('<div>', {'class': RIGHTSIDE_CLS}))
.append($('<div>', {'class': HISTORY_CLS}));
// The 'notitle' class removes the line added for the title with a small screen
if (!config || typeof config !== "object") {
$toolbar.addClass('notitle');
}
$container.prepend($toolbar);
if (ApiConfig && ApiConfig.requireConf && ApiConfig.requireConf.urlArgs) {
styleToolbar($container, undefined, ApiConfig.requireConf.urlArgs);
} else {
styleToolbar($container);
}
return $toolbar;
};
var createSpinner = function ($container, config) {
if (config.displayed.indexOf('spinner') !== -1) {
var $spin = $('<span>', {'class':SPINNER_CLS});
var $spinner = $('<span>', {
id: uid(),
'class': 'spin fa fa-spinner fa-pulse',
}).appendTo($spin).hide();
$('<span>', {
id: uid(),
'class': 'synced fa fa-check',
title: Messages.synced
}).appendTo($spin);
$container.prepend($spin);
return $spin[0];
}
};
var kickSpinner = function (Cryptpad, realtime, local, spinnerElement) {
if (!spinnerElement) { return; }
$(spinnerElement).find('.spin').show();
$(spinnerElement).find('.synced').hide();
var onSynced = function () {
if (spinnerElement.timeout) { clearTimeout(spinnerElement.timeout); }
spinnerElement.timeout = setTimeout(function () {
$(spinnerElement).find('.spin').hide();
$(spinnerElement).find('.synced').show();
}, local ? 0 : SPINNER_DISAPPEAR_TIME);
};
if (Cryptpad) {
Cryptpad.whenRealtimeSyncs(realtime, onSynced);
return;
}
onSynced();
};
var createUserButtons = function ($userlistElement, config, readOnly, Cryptpad) {
// User list button
if (config.displayed.indexOf('userlist') !== -1) {
if (!config.userData) {
throw new Error("You must provide a `userData` object to display the userlist");
}
var dropdownConfig = {
options: [{
tag: 'p',
attributes: {'class': USERLIST_CLS},
}] // Entries displayed in the menu
};
var $block = Cryptpad.createDropdown(dropdownConfig);
$block.attr('id', 'userButtons');
$userlistElement.append($block);
}
// Share button
if (config.displayed.indexOf('share') !== -1) {
var secret = Cryptpad.find(config, ['share', 'secret']);
var channel = Cryptpad.find(config, ['share', 'channel']);
if (!secret || !channel) {
throw new Error("Unable to display the share button: share.secret and share.channel required");
}
Cryptpad.getRecentPads(function (err, recent) {
var $shareIcon = $('<span>', {'class': 'fa fa-share-alt'});
var $span = $('<span>', {'class': 'large'}).append(' ' +Messages.shareButton);
var hashes = Cryptpad.getHashes(channel, secret);
var options = [];
// If we have a stronger version in drive, add it and add a redirect button
var stronger = recent && Cryptpad.findStronger(null, recent);
if (stronger) {
var parsed = Cryptpad.parsePadUrl(stronger);
hashes.editHash = parsed.hash;
}
if (hashes.editHash) {
options.push({
tag: 'a',
attributes: {title: Messages.editShareTitle, 'class': 'editShare'},
content: '<span class="fa fa-users"></span> ' + Messages.editShare
});
if (stronger) {
// We're in view mode, display the "open editing link" button
options.push({
tag: 'a',
attributes: {
title: Messages.editOpenTitle,
'class': 'editOpen',
href: window.location.pathname + '#' + hashes.editHash,
target: '_blank'
},
content: '<span class="fa fa-users"></span> ' + Messages.editOpen
});
}
options.push({tag: 'hr'});
}
if (hashes.viewHash) {
options.push({
tag: 'a',
attributes: {title: Messages.viewShareTitle, 'class': 'viewShare'},
content: '<span class="fa fa-eye"></span> ' + Messages.viewShare
});
if (hashes.editHash && !stronger) {
// We're in edit mode, display the "open readonly" button
options.push({
tag: 'a',
attributes: {
title: Messages.viewOpenTitle,
'class': 'viewOpen',
href: window.location.pathname + '#' + hashes.viewHash,
target: '_blank'
},
content: '<span class="fa fa-eye"></span> ' + Messages.viewOpen
});
}
}
if (hashes.fileHash) {
options.push({
tag: 'a',
attributes: {title: Messages.viewShareTitle, 'class': 'fileShare'},
content: '<span class="fa fa-eye"></span> ' + Messages.viewShare
});
}
var dropdownConfigShare = {
text: $('<div>').append($shareIcon).append($span).html(),
options: options
};
var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare);
$shareBlock.find('button').attr('id', 'shareButton');
$shareBlock.find('.dropdown-bar-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS);
$userlistElement.append($shareBlock);
if (hashes.editHash) {
$shareBlock.find('a.editShare').click(function () {
var url = window.location.origin + window.location.pathname + '#' + hashes.editHash;
var success = Cryptpad.Clipboard.copy(url);
if (success) { Cryptpad.log(Messages.shareSuccess); }
});
}
if (hashes.viewHash) {
$shareBlock.find('a.viewShare').click(function () {
var url = window.location.origin + window.location.pathname + '#' + hashes.viewHash ;
var success = Cryptpad.Clipboard.copy(url);
if (success) { Cryptpad.log(Messages.shareSuccess); }
});
}
if (hashes.fileHash) {
$shareBlock.find('a.fileShare').click(function () {
var url = window.location.origin + window.location.pathname + '#' + hashes.fileHash ;
var success = Cryptpad.Clipboard.copy(url);
if (success) { Cryptpad.log(Messages.shareSuccess); }
});
}
});
}
};
var createUserList = function ($container, config, readOnly, Cryptpad) {
if (config.displayed.indexOf('userlist') === -1 && config.displayed.indexOf('share') === -1) { return; }
var $userlist = $('<div>', {
'class': USER_LIST_CLS,
id: uid(),
});
createUserButtons($userlist, config, readOnly, Cryptpad);
$container.append($userlist);
return $userlist[0];
};
var getOtherUsers = function(myUserName, userList, userData) {
var i = 0; // duplicates counter
var list = [];
var myUid = userData[myUserName] ? userData[myUserName].uid : undefined;
var uids = [];
userList.forEach(function(user) {
if(user !== myUserName) {
var data = (userData) ? (userData[user] || null) : null;
var userName = (data) ? data.name : null;
var userId = (data) ? data.uid : null;
if (userName && uids.indexOf(userId) === -1 && (!myUid || userId !== myUid)) {
uids.push(userId);
list.push(userName);
} else { i++; }
}
});
return {
list: list,
duplicates: i
};
};
var arrayIntersect = function(a, b) {
return $.grep(a, function(i) {
return $.inArray(i, b) > -1;
});
};
var getViewers = function (n) {
if (!n || !parseInt(n) || n === 0) { return ''; }
if (n === 1) { return '; + ' + Messages.oneViewer; }
return '; + ' + Messages._getKey('viewers', [n]);
};
var checkSynchronizing = function (userList, myUserName, $stateElement) {
var meIdx = userList.indexOf(myUserName);
if (meIdx === -1) {
$stateElement.text(Messages.synchronizing);
return;
}
$stateElement.text('');
};
var updateUserList = function (config, myUserName, userlistElement, userList, userData, readOnly, $userAdminElement) {
// Make sure the elements are displayed
var $userButtons = $(userlistElement).find("#userButtons");
$userButtons.attr('display', 'inline');
if (config.displayed.indexOf('userlist') !== -1) {
var numberOfUsers = userList.length;
// If we are using old pads (readonly unavailable), only editing users are in userList.
// With new pads, we also have readonly users in userList, so we have to intersect with
// the userData to have only the editing users. We can't use userData directly since it
// may contain data about users that have already left the channel.
userList = readOnly === -1 ? userList : arrayIntersect(userList, Object.keys(userData));
// Names of editing users
var others = getOtherUsers(myUserName, userList, userData);
var editUsersNames = others.list;
var duplicates = others.duplicates;
var numberOfEditUsers = userList.length - duplicates;
var numberOfViewUsers = numberOfUsers - numberOfEditUsers - duplicates;
// Number of anonymous editing users
var anonymous = numberOfEditUsers - editUsersNames.length;
// Update the userlist
var $usersTitle = $('<h2>').text(Messages.users);
var $editUsers = $userButtons.find('.' + USERLIST_CLS);
$editUsers.html('').append($usersTitle);
var $editUsersList = $('<pre>');
if (readOnly !== 1) { // Yourself - edit
$editUsers.append('<span class="yourself">' + Messages.yourself + '</span>');
anonymous--;
}
// Editors
$editUsersList.text(editUsersNames.join('\n')); // .text() to avoid XSS
$editUsers.append($editUsersList);
if (anonymous > 0) { // Anonymous editors
var text = anonymous === 1 ? Messages.anonymousUser : Messages.anonymousUsers;
$editUsers.append('<span class="anonymous">' + anonymous + ' ' + text + '</span>');
}
if (numberOfViewUsers > 0) { // Viewers
var viewText = '<span class="viewer">';
if (numberOfEditUsers > 0) {
$editUsers.append('<br>');
viewText += Messages.and + ' ';
}
var viewerText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer;
viewText += numberOfViewUsers + ' ' + viewerText + '</span>';
$editUsers.append(viewText);
}
// Update the buttons
var fa_editusers = '<span class="fa fa-users"></span>';
var fa_viewusers = '<span class="fa fa-eye"></span>';
var viewersText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer;
var editorsText = numberOfEditUsers !== 1 ? Messages.editors : Messages.editor;
var $span = $('<span>', {'class': 'large'}).html(fa_editusers + ' ' + numberOfEditUsers + ' ' + editorsText + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers + ' ' + viewersText);
var $spansmall = $('<span>', {'class': 'narrow'}).html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers);
$userButtons.find('.buttonTitle').html('').append($span).append($spansmall);
}
if (config.displayed.indexOf('useradmin') !== -1) {
// Change username in useradmin dropdown
var $userElement = $userAdminElement.find('.' + USERNAME_CLS);
$userElement.show();
if (readOnly === 1) {
$userElement.addClass(READONLY_CLS).text(Messages.readonly);
}
else {
var name = userData[myUserName] && userData[myUserName].name;
if (!name) {
name = Messages.anonymous;
}
$userElement.removeClass(READONLY_CLS).text(name);
}
}
};
var createLagElement = function () {
var $lag = $('<span>', {
'class': LAG_ELEM_CLS,
id: uid(),
});
var $a = $('<span>', {'class': 'cryptpad-lag', id: 'newLag'});
$('<span>', {'class': 'bar1'}).appendTo($a);
$('<span>', {'class': 'bar2'}).appendTo($a);
$('<span>', {'class': 'bar3'}).appendTo($a);
$('<span>', {'class': 'bar4'}).appendTo($a);
return $a[0];
};
var checkLag = function (getLag, lagElement) {
var lag;
if(typeof getLag === "function") {
lag = getLag();
}
var lagLight = $('<div>', {
'class': 'lag'
});
var title;
var $lag = $(lagElement);
if (lag) {
$lag.attr('class', 'cryptpad-lag');
firstConnection = false;
title = Messages.lag + ' : ' + lag + ' ms\n';
if (lag > 30000) {
$lag.addClass('lag0');
title = Messages.redLight;
} else if (lag > 5000) {
$lag.addClass('lag1');
title += Messages.orangeLight;
} else if (lag > 1000) {
$lag.addClass('lag2');
title += Messages.orangeLight;
} else if (lag > 300) {
$lag.addClass('lag3');
title += Messages.greenLight;
} else {
$lag.addClass('lag4');
title += Messages.greenLight;
}
}
else if (!firstConnection) {
$lag.attr('class', 'cryptpad-lag');
// Display the red light at the 2nd failed attemp to get the lag
lagLight.addClass('lag-red');
title = Messages.redLight;
}
if (title) {
$lag.attr('title', title);
}
};
var createLinkToMain = function ($topContainer) {
var $linkContainer = $('<span>', {
'class': "cryptpad-link"
}).appendTo($topContainer);
var $imgTag = $('<img>', {
src: "/customize/cryptofist_mini.png?" + urlArgs,
alt: "Cryptpad"
});
// We need to override the "a" tag action here because it is inside the iframe!
var $aTagSmall = $('<a>', {
href: "/",
title: Messages.header_logoTitle,
'class': "cryptpad-logo"
}).append($imgTag);
var $span = $('<span>').text('CryptPad');
var $aTagBig = $aTagSmall.clone().addClass('large').append($span);
$aTagSmall.addClass('narrow');
var onClick = function (e) {
e.preventDefault();
if (e.ctrlKey) {
window.open('/');
return;
}
window.location = "/";
};
var onContext = function (e) { e.stopPropagation(); };
$aTagBig.click(onClick).contextmenu(onContext);
$aTagSmall.click(onClick).contextmenu(onContext);
$linkContainer.append($aTagSmall).append($aTagBig);
};
var createUserAdmin = function ($topContainer, config, readOnly, lagElement, Cryptpad) {
var $lag = $(lagElement);
var $userContainer = $('<span>', {
'class': USER_CLS
}).appendTo($topContainer);
if (config.displayed.indexOf('state') !== -1) {
var $state = $('<span>', {
'class': STATE_CLS
}).text(Messages.synchronizing).appendTo($userContainer);
}
if (config.displayed.indexOf('lag') !== -1) {
$userContainer.append($lag);
}
if (config.displayed.indexOf('limit') !== -1 && Config.enablePinning) {
var usage;
var $limitIcon = $('<span>', {'class': 'fa fa-exclamation-triangle'});
var $limit = $('<span>', {
'class': LIMIT_ELEM_CLS,
'title': Messages.pinLimitReached
}).append($limitIcon).hide().appendTo($userContainer);
var todo = function (e, overLimit) {
if (e) { return void console.error("Unable to get the pinned usage"); }
if (overLimit) {
var message = Messages.pinLimitReachedAlert;
if (ApiConfig.noSubscriptionButton === true) {
message = Messages.pinLimitReachedAlertNoAccounts;
}
$limit.show().click(function () {
Cryptpad.alert(message, null, true);
});
}
};
Cryptpad.isOverPinLimit(todo);
}
if (config.displayed.indexOf('newpad') !== -1) {
var pads_options = [];
Config.availablePadTypes.forEach(function (p) {
if (p === 'drive') { return; }
pads_options.push({
tag: 'a',
attributes: {
'target': '_blank',
'href': '/' + p + '/',
},
content: Messages.type[p]
});
});
var $plusIcon = $('<span>', {'class': 'fa fa-plus'});
var $newbig = $('<span>', {'class': 'big'}).append(' ' +Messages.newButton);
var $newButton = $('<div>').append($plusIcon).append($newbig);
var dropdownConfig = {
text: $newButton.html(), // Button initial text
options: pads_options, // Entries displayed in the menu
left: true, // Open to the left of the button
};
var $newPadBlock = Cryptpad.createDropdown(dropdownConfig);
$newPadBlock.find('button').attr('title', Messages.newButtonTitle);
$newPadBlock.find('button').attr('id', 'newdoc');
$newPadBlock.appendTo($userContainer);
}
// User dropdown
if (config.displayed.indexOf('useradmin') !== -1) {
var userMenuCfg = {};
if (!config.hideDisplayName) {
userMenuCfg = {
displayNameCls: USERNAME_CLS,
changeNameButtonCls: USERBUTTON_CLS,
};
}
if (readOnly !== 1) {
userMenuCfg.displayName = 1;
userMenuCfg.displayChangeName = 1;
}
var $userAdmin = Cryptpad.createUserAdminMenu(userMenuCfg);
$userAdmin.attr('id', 'userDropdown');
$userContainer.append($userAdmin);
var $userButton = $userAdmin.find('a.' + USERBUTTON_CLS);
var renameAlertOpened;
$userButton.click(function (e) {
e.preventDefault();
e.stopPropagation();
Cryptpad.getLastName(function (err, lastName) {
if (err) { return void console.error("Cannot get last name", err); }
Cryptpad.prompt(Messages.changeNamePrompt, lastName || '', function (newName) {
if (newName === null && typeof(lastName) === "string") { return; }
if (newName === null) { newName = ''; }
Cryptpad.changeDisplayName(newName);
});
});
});
Cryptpad.onDisplayNameChanged(function (newName) {
Cryptpad.findCancelButton().click();
});
}
return $userContainer;
};
var createTitle = function ($container, readOnly, config, Cryptpad) {
var $titleContainer = $('<span>', {
id: 'toolbarTitle',
'class': TITLE_CLS
}).appendTo($container);
if (!config || typeof config !== "object") { return; }
var callback = config.onRename;
var placeholder = config.defaultName;
var suggestName = config.suggestName;
// Buttons
var $text = $('<span>', {
'class': 'title'
}).appendTo($titleContainer);
var $pencilIcon = $('<span>', {
'class': 'pencilIcon',
'title': Messages.clickToEdit
});
if (readOnly === 1 || typeof(Cryptpad) === "undefined") { return $titleContainer; }
var $input = $('<input>', {
type: 'text',
placeholder: placeholder
}).appendTo($titleContainer).hide();
if (readOnly !== 1) {
$text.attr("title", Messages.clickToEdit);
$text.addClass("editable");
var $icon = $('<span>', {
'class': 'fa fa-pencil readonly',
style: 'font-family: FontAwesome;'
});
$pencilIcon.append($icon).appendTo($titleContainer);
}
// Events
$input.on('mousedown', function (e) {
if (!$input.is(":focus")) {
$input.focus();
}
e.stopPropagation();
return true;
});
$input.on('keyup', function (e) {
if (e.which === 13 && connected === true) {
var name = $input.val().trim();
if (name === "") {
name = $input.attr('placeholder');
}
Cryptpad.renamePad(name, function (err, newtitle) {
if (err) { return; }
$text.text(newtitle);
callback(null, newtitle);
$input.hide();
$text.show();
//$pencilIcon.css('display', '');
});
}
else if (e.which === 27) {
$input.hide();
$text.show();
//$pencilIcon.css('display', '');
}
});
var displayInput = function () {
if (connected === false) { return; }
$text.hide();
//$pencilIcon.css('display', 'none');
var inputVal = suggestName() || "";
$input.val(inputVal);
$input.show();
$input.focus();
};
$text.on('click', displayInput);
$pencilIcon.on('click', displayInput);
return $titleContainer;
};
var create = Bar.create = function ($container, myUserName, realtime, getLag, userList, config) {
config = config || {};
var readOnly = (typeof config.readOnly !== "undefined") ? (config.readOnly ? 1 : 0) : -1;
var Cryptpad = config.common;
Messages = Cryptpad.Messages;
config.displayed = config.displayed || [];
var toolbar = createRealtimeToolbar($container, config.title);
var userListElement = createUserList(toolbar.find('.' + LEFTSIDE_CLS), config, readOnly, Cryptpad);
var $titleElement = createTitle(toolbar.find('.' + TOP_CLS), readOnly, config.title, Cryptpad);
var $linkElement = createLinkToMain(toolbar.find('.' + TOP_CLS));
var lagElement = createLagElement();
var $userAdminElement = createUserAdmin(toolbar.find('.' + TOP_CLS), config, readOnly, lagElement, Cryptpad);
var spinner = createSpinner($userAdminElement, config);
var userData = config.userData;
// readOnly = 1 (readOnly enabled), 0 (disabled), -1 (old pad without readOnly mode)
var saveElement;
var loadElement;
var $stateElement = toolbar.find('.' + STATE_CLS);
if (config.ifrw) {
var removeDropdowns = function (e) {
$container.find('.cryptpad-dropdown').hide();
};
var cancelEditTitle = function (e) {
// Now we want to apply the title even if we click somewhere else
if ($(e.target).parents('.' + TITLE_CLS).length || !$titleElement) {
return;
}
if (!$titleElement.find('input').is(':visible')) { return; }
var ev = $.Event("keyup");
ev.which = 13;
$titleElement.find('input').trigger(ev);
/*
$titleElement.find('input').hide();
$titleElement.find('span.title').show();
$titleElement.find('span.pencilIcon').css('display', '');
*/
};
$(config.ifrw).on('click', removeDropdowns);
$(config.ifrw).on('click', cancelEditTitle);
try {
if (config.ifrw.$('iframe').length) {
var innerIfrw = config.ifrw.$('iframe').each(function (i, el) {
$(el.contentWindow).on('click', removeDropdowns);
$(el.contentWindow).on('click', cancelEditTitle);
});
}
} catch (e) {
// empty try catch in case this iframe is problematic
}
}
// Update user list
if (userList) {
if (userData) {
userList.change.push(function (newUserData) {
var users = userList.users;
if (users.indexOf(myUserName) !== -1) { connected = true; }
if (!connected) { return; }
checkSynchronizing(users, myUserName, $stateElement);
updateUserList(config, myUserName, userListElement, users, userData, readOnly, $userAdminElement);
});
} else {
userList.change.push(function () {
var users = userList.users;
if (users.indexOf(myUserName) !== -1) { connected = true; }
if (!connected) { return; }
checkSynchronizing(users, myUserName, $stateElement);
});
}
}
// Display notifications when users are joining/leaving the session
var oldUserData;
if (typeof Cryptpad !== "undefined" && userList) {
var notify = function(type, name, oldname) {
// type : 1 (+1 user), 0 (rename existing user), -1 (-1 user)
if (typeof name === "undefined") { return; }
name = (name === "") ? Messages.anonymous : name;
switch(type) {
case 1:
Cryptpad.log(Messages._getKey("notifyJoined", [name]));
break;
case 0:
oldname = (oldname === "") ? Messages.anonymous : oldname;
Cryptpad.log(Messages._getKey("notifyRenamed", [oldname, name]));
break;
case -1:
Cryptpad.log(Messages._getKey("notifyLeft", [name]));
break;
default:
console.log("Invalid type of notification");
break;
}
};
var userPresent = function (id, user, data) {
if (!(user && user.uid)) {
console.log('no uid');
return 0;
}
if (!data) {
console.log('no data');
return 0;
}
var count = 0;
Object.keys(data).forEach(function (k) {
if (data[k] && data[k].uid === user.uid) { count++; }
});
return count;
};
userList.change.push(function (newdata) {
// Notify for disconnected users
if (typeof oldUserData !== "undefined") {
for (var u in oldUserData) {
// if a user's uid is still present after having left, don't notify
if (userList.users.indexOf(u) === -1) {
var temp = JSON.parse(JSON.stringify(oldUserData[u]));
delete oldUserData[u];
if (userPresent(u, temp, newdata || oldUserData) < 1) {
notify(-1, temp.name);
}
}
}
}
// Update the "oldUserData" object and notify for new users and names changed
if (typeof newdata === "undefined") { return; }
if (typeof oldUserData === "undefined") {
oldUserData = JSON.parse(JSON.stringify(newdata));
return;
}
if (readOnly === 0 && !oldUserData[myUserName]) {
oldUserData = JSON.parse(JSON.stringify(newdata));
return;
}
for (var k in newdata) {
if (k !== myUserName && userList.users.indexOf(k) !== -1) {
if (typeof oldUserData[k] === "undefined") {
// if the same uid is already present in the userdata, don't notify
if (!userPresent(k, newdata[k], oldUserData)) {
notify(1, newdata[k].name);
}
} else if (oldUserData[k].name !== newdata[k].name) {
notify(0, newdata[k].name, oldUserData[k].name);
}
}
}
oldUserData = JSON.parse(JSON.stringify(newdata));
});
}
var ks = function (local) {
return function () {
if (connected) { kickSpinner(Cryptpad, realtime, local, spinner); }
};
};
if (realtime) {
realtime.onPatch(ks());
realtime.onMessage(ks(true));
checkLag(getLag, lagElement);
setInterval(function () {
if (!connected) { return; }
checkLag(getLag, lagElement);
}, 3000);
} else { connected = true; }
var failed = function () {
connected = false;
$stateElement.text(Messages.disconnected);
checkLag(undefined, lagElement);
};
// On log out, remove permanently the realtime elements of the toolbar
Cryptpad.onLogout(function () {
failed();
$userAdminElement.find('#userDropdown').hide();
$(userListElement).hide();
});
return {
failed: failed,
reconnecting: function (userId) {
myUserName = userId;
connected = false;
$stateElement.text(Messages.reconnecting);
checkLag(getLag, lagElement);
},
connected: function () {
connected = true;
}
};
};
return Bar;
});

File diff suppressed because it is too large Load Diff

@ -2,9 +2,12 @@ define([
'jquery', 'jquery',
'/customize/application_config.js', '/customize/application_config.js',
'/api/config', '/api/config',
], function ($, Config, ApiConfig) { '/common/common-ui-elements.js',
var Messages = {}; '/common/common-interface.js',
var Cryptpad; '/common/common-hash.js',
'/customize/messages.js',
'/common/clipboard.js',
], function ($, Config, ApiConfig, UIElements, UI, Hash, Messages, Clipboard) {
var Common; var Common;
var Bar = { var Bar = {
@ -148,6 +151,18 @@ define([
}; };
var avatars = {}; var avatars = {};
var editingUserName = {
state: false
};
var setDisplayName = function (newName) {
Common.setDisplayName(newName, function (err) {
if (err) {
console.log("Couldn't set username");
console.error(err);
return;
}
});
};
var updateUserList = function (toolbar, config) { var updateUserList = function (toolbar, config) {
// Make sure the elements are displayed // Make sure the elements are displayed
var $userButtons = toolbar.userlist; var $userButtons = toolbar.userlist;
@ -182,6 +197,16 @@ define([
var numberOfEditUsers = Object.keys(userData).length - duplicates; var numberOfEditUsers = Object.keys(userData).length - duplicates;
var numberOfViewUsers = viewers; var numberOfViewUsers = viewers;
// If the user was changing his username, do not reste the input, store the current value
// and cursor
if (editingUserName.state) {
var $input = $userlistContent.find('.cp-toolbar-userlist-name-input');
editingUserName.value = $input.val();
editingUserName.select = [$input[0].selectionStart, $input[0].selectionEnd];
}
// Update the userlist // Update the userlist
var $editUsers = $userlistContent.find('.' + USERLIST_CLS).html(''); var $editUsers = $userlistContent.find('.' + USERLIST_CLS).html('');
@ -210,14 +235,58 @@ define([
var $span = $('<span>', {'class': 'cp-avatar'}); var $span = $('<span>', {'class': 'cp-avatar'});
var $rightCol = $('<span>', {'class': 'cp-toolbar-userlist-rightcol'}); var $rightCol = $('<span>', {'class': 'cp-toolbar-userlist-rightcol'});
var $nameSpan = $('<span>', {'class': 'cp-toolbar-userlist-name'}).text(name).appendTo($rightCol); var $nameSpan = $('<span>', {'class': 'cp-toolbar-userlist-name'}).text(name).appendTo($rightCol);
var isMe = data.curvePublic === user.curvePublic; var isMe = data.uid === user.uid;
if (Common.isLoggedIn() && data.curvePublic) {
if (isMe) { if (isMe) {
$span.attr('title', Messages._getKey('userlist_thisIsYou', [ $span.attr('title', Messages._getKey('userlist_thisIsYou', [
name name
])); ]));
$nameSpan.text(name); $nameSpan.html('');
} else if (!friends[data.curvePublic]) { var $nameValue = $('<span>', {
'class': 'cp-toolbar-userlist-name-value'
}).text(name).appendTo($nameSpan);
var $button = $('<button>', {
'class': 'fa fa-pencil cp-toolbar-userlist-name-edit',
title: "TODO: Edit your username"
}).appendTo($nameSpan);
var $nameInput = $('<input>', {
'class': 'cp-toolbar-userlist-name-input'
}).val(name).appendTo($rightCol);
$button.click(function (e) {
e.stopPropagation();
$nameSpan.hide();
$nameInput.show().focus().select();
editingUserName.state = true;
editingUserName.oldName = $nameInput.val();
});
$nameInput.click(function (e) {
e.stopPropagation();
});
$nameInput.on('keydown', function (e) {
if (e.which === 13 || e.which === 27) {
$nameInput.hide();
$nameSpan.show();
$button.show();
editingUserName.state = false;
}
if (e.which === 13) {
var newName = $nameInput.val(); // TODO clean
$nameValue.text(newName);
setDisplayName(newName);
return;
}
if (e.which === 27) {
$nameValue.text(editingUserName.oldName);
return;
}
});
if (editingUserName.state) {
$button.click();
$nameInput.val(editingUserName.value);
$nameInput[0].setSelectionRange(editingUserName.select[0],
editingUserName.select[1]);
setTimeout(function () { $nameInput.focus(); });
}
} else if (Common.isLoggedIn() && data.curvePublic && !friends[data.curvePublic]) {
if (pendingFriends.indexOf(data.netfluxId) !== -1) { if (pendingFriends.indexOf(data.netfluxId) !== -1) {
$('<span>', {'class': 'cp-toolbar-userlist-friend'}).text(Messages.userlist_pending) $('<span>', {'class': 'cp-toolbar-userlist-friend'}).text(Messages.userlist_pending)
.appendTo($rightCol); .appendTo($rightCol);
@ -233,7 +302,6 @@ define([
}); });
} }
} }
}
if (data.profile) { if (data.profile) {
$span.addClass('cp-userlist-clickable'); $span.addClass('cp-userlist-clickable');
$span.click(function () { $span.click(function () {
@ -426,7 +494,7 @@ define([
options: options, options: options,
feedback: 'SHARE_MENU', feedback: 'SHARE_MENU',
}; };
var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare); var $shareBlock = UIElements.createDropdown(dropdownConfigShare);
$shareBlock.find('.cp-dropdown-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS); $shareBlock.find('.cp-dropdown-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS);
$shareBlock.addClass('cp-toolbar-share-button'); $shareBlock.addClass('cp-toolbar-share-button');
$shareBlock.find('button').attr('title', Messages.shareButton); $shareBlock.find('button').attr('title', Messages.shareButton);
@ -434,25 +502,25 @@ define([
if (hashes.editHash) { if (hashes.editHash) {
$shareBlock.find('a.cp-toolbar-share-edit-copy').click(function () { $shareBlock.find('a.cp-toolbar-share-edit-copy').click(function () {
/*Common.storeLinkToClipboard(false, function (err) { /*Common.storeLinkToClipboard(false, function (err) {
if (!err) { Cryptpad.log(Messages.shareSuccess); } if (!err) { UI.log(Messages.shareSuccess); }
});*/ });*/
var url = origin + pathname + '#' + hashes.editHash; var url = origin + pathname + '#' + hashes.editHash;
var success = Cryptpad.Clipboard.copy(url); var success = Clipboard.copy(url);
if (success) { Cryptpad.log(Messages.shareSuccess); } if (success) { UI.log(Messages.shareSuccess); }
}); });
} }
if (hashes.viewHash) { if (hashes.viewHash) {
$shareBlock.find('a.cp-toolbar-share-view-copy').click(function () { $shareBlock.find('a.cp-toolbar-share-view-copy').click(function () {
/*Common.storeLinkToClipboard(true, function (err) { /*Common.storeLinkToClipboard(true, function (err) {
if (!err) { Cryptpad.log(Messages.shareSuccess); } if (!err) { UI.log(Messages.shareSuccess); }
});*/ });*/
var url = origin + pathname + '#' + hashes.viewHash; var url = origin + pathname + '#' + hashes.viewHash;
var success = Cryptpad.Clipboard.copy(url); var success = Clipboard.copy(url);
if (success) { Cryptpad.log(Messages.shareSuccess); } if (success) { UI.log(Messages.shareSuccess); }
}); });
$shareBlock.find('a.cp-toolbar-share-view-embed').click(function () { $shareBlock.find('a.cp-toolbar-share-view-embed').click(function () {
var url = origin + pathname + '#' + hashes.viewHash; var url = origin + pathname + '#' + hashes.viewHash;
var parsed = Cryptpad.parsePadUrl(url); var parsed = Hash.parsePadUrl(url);
url = origin + parsed.getUrl({embed: true, present: true}); url = origin + parsed.getUrl({embed: true, present: true});
// Alertify content // Alertify content
var $content = $('<div>'); var $content = $('<div>');
@ -468,12 +536,12 @@ define([
readonly: 'readonly', readonly: 'readonly',
value: iframeEmbed, value: iframeEmbed,
}).appendTo($tag); }).appendTo($tag);
Cryptpad.alert($content.html(), null, true); UI.alert($content.html(), null, true);
$('#'+iframeId).click(function () { $('#'+iframeId).click(function () {
this.select(); this.select();
}); });
//var success = Cryptpad.Clipboard.copy(url); //var success = Clipboard.copy(url);
//if (success) { Cryptpad.log(Messages.shareSuccess); } //if (success) { UI.log(Messages.shareSuccess); }
}); });
} }
@ -511,15 +579,15 @@ define([
options: options, options: options,
feedback: 'FILESHARE_MENU', feedback: 'FILESHARE_MENU',
}; };
var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare); var $shareBlock = UIElements.createDropdown(dropdownConfigShare);
$shareBlock.find('.cp-dropdown-content').addClass(SHARE_CLS); $shareBlock.find('.cp-dropdown-content').addClass(SHARE_CLS);
$shareBlock.addClass('cp-toolbar-share-button'); $shareBlock.addClass('cp-toolbar-share-button');
$shareBlock.find('button').attr('title', Messages.shareButton); $shareBlock.find('button').attr('title', Messages.shareButton);
// Add handlers // Add handlers
$shareBlock.find('a.cp-toolbar-share-file-copy').click(function () { $shareBlock.find('a.cp-toolbar-share-file-copy').click(function () {
var success = Cryptpad.Clipboard.copy(url); var success = Clipboard.copy(url);
if (success) { Cryptpad.log(Messages.shareSuccess); } if (success) { UI.log(Messages.shareSuccess); }
}); });
$shareBlock.find('a.cp-toolbar-share-file-embed').click(function () { $shareBlock.find('a.cp-toolbar-share-file-embed').click(function () {
var $content = $('<div>'); var $content = $('<div>');
@ -527,11 +595,11 @@ define([
$('<h3>').text(Messages.fileEmbedTitle).appendTo($content); $('<h3>').text(Messages.fileEmbedTitle).appendTo($content);
var $script = $('<p>').text(Messages.fileEmbedScript).appendTo($content); var $script = $('<p>').text(Messages.fileEmbedScript).appendTo($content);
$('<br>').appendTo($script); $('<br>').appendTo($script);
$script.append(Cryptpad.dialog.selectable(Common.getMediatagScript())); $script.append(UI.dialog.selectable(Common.getMediatagScript()));
var $tag = $('<p>').text(Messages.fileEmbedTag).appendTo($content); var $tag = $('<p>').text(Messages.fileEmbedTag).appendTo($content);
$('<br>').appendTo($tag); $('<br>').appendTo($tag);
$tag.append(Cryptpad.dialog.selectable(Common.getMediatagFromHref(url))); $tag.append(UI.dialog.selectable(Common.getMediatagFromHref(url)));
Cryptpad.alert($content[0], null, true); UI.alert($content[0], null, true);
}); });
toolbar.$leftside.append($shareBlock); toolbar.$leftside.append($shareBlock);
@ -568,8 +636,8 @@ define([
if (config.readOnly === 1) { if (config.readOnly === 1) {
$titleContainer.append($('<span>', {'class': 'cp-toolbar-title-readonly'}) $titleContainer.append($('<span>', {'class': 'cp-toolbar-title-readonly'})
.text('('+Messages.readonly+')')); .text('('+Messages.readonly+')'));
return $titleContainer;
} }
if (config.readOnly === 1 || typeof(Cryptpad) === "undefined") { return $titleContainer; }
var $input = $('<input>', { var $input = $('<input>', {
type: 'text', type: 'text',
placeholder: placeholder placeholder: placeholder
@ -760,7 +828,7 @@ define([
key = 'pinLimitReachedAlertNoAccounts'; key = 'pinLimitReachedAlertNoAccounts';
} }
$limit.show().click(function () { $limit.show().click(function () {
Cryptpad.alert(Messages._getKey(key, [encodeURIComponent(window.location.hostname)]), null, true); UI.alert(Messages._getKey(key, [encodeURIComponent(window.location.hostname)]), null, true);
}); });
} }
}; };
@ -785,7 +853,7 @@ define([
'target': '_blank', 'target': '_blank',
'href': origin + '/' + p + '/', 'href': origin + '/' + p + '/',
}, },
content: $('<div>').append(Cryptpad.getIcon(p)).html() + Messages.type[p] content: $('<div>').append(UI.getIcon(p)).html() + Messages.type[p]
}); });
}); });
var dropdownConfig = { var dropdownConfig = {
@ -796,7 +864,7 @@ define([
feedback: /drive/.test(window.location.pathname)? feedback: /drive/.test(window.location.pathname)?
'DRIVE_NEWPAD': 'NEWPAD', 'DRIVE_NEWPAD': 'NEWPAD',
}; };
var $newPadBlock = Cryptpad.createDropdown(dropdownConfig); var $newPadBlock = UIElements.createDropdown(dropdownConfig);
$newPadBlock.find('button').attr('title', Messages.newButtonTitle); $newPadBlock.find('button').attr('title', Messages.newButtonTitle);
$newPadBlock.find('button').addClass('fa fa-th'); $newPadBlock.find('button').addClass('fa fa-th');
return $newPadBlock; return $newPadBlock;
@ -821,8 +889,11 @@ define([
userMenuCfg.displayName = 1; userMenuCfg.displayName = 1;
userMenuCfg.displayChangeName = 1; userMenuCfg.displayChangeName = 1;
} }
/*if (config.displayed.indexOf('userlist') !== -1) {
userMenuCfg.displayChangeName = 0;
}*/
Common.createUserAdminMenu(userMenuCfg); Common.createUserAdminMenu(userMenuCfg);
$userAdmin.find('button').attr('title', Messages.userAccountButton); $userAdmin.find('> button').attr('title', Messages.userAccountButton);
var $userButton = toolbar.$userNameButton = $userAdmin.find('a.' + USERBUTTON_CLS); var $userButton = toolbar.$userNameButton = $userAdmin.find('a.' + USERBUTTON_CLS);
$userButton.click(function (e) { $userButton.click(function (e) {
@ -830,18 +901,11 @@ define([
e.stopPropagation(); e.stopPropagation();
var myData = metadataMgr.getMetadata().users[metadataMgr.getNetfluxId()]; var myData = metadataMgr.getMetadata().users[metadataMgr.getNetfluxId()];
var lastName = myData.name; var lastName = myData.name;
Cryptpad.prompt(Messages.changeNamePrompt, lastName || '', function (newName) { UI.prompt(Messages.changeNamePrompt, lastName || '', function (newName) {
if (newName === null && typeof(lastName) === "string") { return; } if (newName === null && typeof(lastName) === "string") { return; }
if (newName === null) { newName = ''; } if (newName === null) { newName = ''; }
else { Common.feedback('NAME_CHANGED'); } else { Common.feedback('NAME_CHANGED'); }
Common.setDisplayName(newName, function (err) { setDisplayName(newName);
if (err) {
console.log("Couldn't set username");
console.error(err);
return;
}
//Cryptpad.changeDisplayName(newName, true); Already done?
});
}); });
}); });
@ -892,7 +956,6 @@ define([
if (!config.metadataMgr) { return; } if (!config.metadataMgr) { return; }
var metadataMgr = config.metadataMgr; var metadataMgr = config.metadataMgr;
var userNetfluxId = metadataMgr.getNetfluxId(); var userNetfluxId = metadataMgr.getNetfluxId();
if (typeof Cryptpad !== "undefined") {
var notify = function(type, name, oldname) { var notify = function(type, name, oldname) {
// type : 1 (+1 user), 0 (rename existing user), -1 (-1 user) // type : 1 (+1 user), 0 (rename existing user), -1 (-1 user)
if (typeof name === "undefined") { return; } if (typeof name === "undefined") { return; }
@ -900,14 +963,14 @@ define([
if (Config.disableUserlistNotifications) { return; } if (Config.disableUserlistNotifications) { return; }
switch(type) { switch(type) {
case 1: case 1:
Cryptpad.log(Messages._getKey("notifyJoined", [name])); UI.log(Messages._getKey("notifyJoined", [name]));
break; break;
case 0: case 0:
oldname = (!oldname) ? Messages.anonymous : oldname; oldname = (!oldname) ? Messages.anonymous : oldname;
Cryptpad.log(Messages._getKey("notifyRenamed", [oldname, name])); UI.log(Messages._getKey("notifyRenamed", [oldname, name]));
break; break;
case -1: case -1:
Cryptpad.log(Messages._getKey("notifyLeft", [name])); UI.log(Messages._getKey("notifyLeft", [name]));
break; break;
default: default:
console.log("Invalid type of notification"); console.log("Invalid type of notification");
@ -975,7 +1038,6 @@ define([
joined = true; joined = true;
oldUserData = JSON.parse(JSON.stringify(newdata)); oldUserData = JSON.parse(JSON.stringify(newdata));
}); });
}
}; };
@ -984,9 +1046,7 @@ define([
Bar.create = function (cfg) { Bar.create = function (cfg) {
var config = cfg || {}; var config = cfg || {};
Cryptpad = config.common;
Common = config.sfCommon; Common = config.sfCommon;
Messages = Cryptpad.Messages;
config.readOnly = (typeof config.readOnly !== "undefined") ? (config.readOnly ? 1 : 0) : -1; config.readOnly = (typeof config.readOnly !== "undefined") ? (config.readOnly ? 1 : 0) : -1;
config.displayed = config.displayed || []; config.displayed = config.displayed || [];
@ -1081,7 +1141,7 @@ define([
}; };
// On log out, remove permanently the realtime elements of the toolbar // On log out, remove permanently the realtime elements of the toolbar
Cryptpad.onLogout(function () { Common.onLogout(function () {
failed(); failed();
if (toolbar.useradmin) { toolbar.useradmin.hide(); } if (toolbar.useradmin) { toolbar.useradmin.hide(); }
if (toolbar.userlist) { toolbar.userlist.hide(); } if (toolbar.userlist) { toolbar.userlist.hide(); }

@ -1,7 +1,10 @@
define([ define([
'jquery', 'jquery',
'/customize/application_config.js' '/customize/application_config.js',
], function ($, AppConfig) { '/common/common-util.js',
'/common/common-hash.js',
'/common/common-realtime.js'
], function ($, AppConfig, Util, Hash, Realtime) {
var module = {}; var module = {};
var ROOT = module.ROOT = "root"; var ROOT = module.ROOT = "root";
@ -74,7 +77,7 @@ define([
exp.isReadOnlyFile = function (element) { exp.isReadOnlyFile = function (element) {
if (!isFile(element)) { return false; } if (!isFile(element)) { return false; }
var data = exp.getFileData(element); var data = exp.getFileData(element);
var parsed = Cryptpad.parsePadUrl(data.href); var parsed = Hash.parsePadUrl(data.href);
if (!parsed) { return false; } if (!parsed) { return false; }
var pHash = parsed.hashData; var pHash = parsed.hashData;
if (!pHash || pHash.type !== "pad") { return; } if (!pHash || pHash.type !== "pad") { return; }
@ -243,7 +246,7 @@ define([
getHrefArray().forEach(function (c) { getHrefArray().forEach(function (c) {
ret = ret.concat(_getFiles[c]()); ret = ret.concat(_getFiles[c]());
}); });
return Cryptpad.deduplicateString(ret); return Util.deduplicateString(ret);
}; };
_getFiles[ROOT] = function () { _getFiles[ROOT] = function () {
var ret = []; var ret = [];
@ -294,7 +297,7 @@ define([
ret = ret.concat(_getFiles[c]()); ret = ret.concat(_getFiles[c]());
} }
}); });
return Cryptpad.deduplicateString(ret); return Util.deduplicateString(ret);
}; };
var getIdFromHref = exp.getIdFromHref = function (href) { var getIdFromHref = exp.getIdFromHref = function (href) {
@ -437,13 +440,13 @@ define([
}); });
// Search Href // Search Href
var href = Cryptpad.getRelativeHref(value); var href = Hash.getRelativeHref(value);
if (href) { if (href) {
var id = getIdFromHref(href); var id = getIdFromHref(href);
if (id) { res.push(id); } if (id) { res.push(id); }
} }
res = Cryptpad.deduplicateString(res); res = Util.deduplicateString(res);
var ret = []; var ret = [];
res.forEach(function (l) { res.forEach(function (l) {
@ -486,14 +489,14 @@ define([
// TODO: can only be called from outside atm // TODO: can only be called from outside atm
if (typeof cb !== "function") { cb = function () {}; } if (typeof cb !== "function") { cb = function () {}; }
var todo = function () { var todo = function () {
var id = Cryptpad.createRandomInteger(); var id = Util.createRandomInteger();
files[FILES_DATA][id] = data; files[FILES_DATA][id] = data;
cb(null, id); cb(null, id);
}; };
if (!loggedIn || !AppConfig.enablePinning || config.testMode) { if (!loggedIn || !AppConfig.enablePinning || config.testMode) {
return void todo(); return void todo();
} }
Cryptpad.pinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e) { Cryptpad.pinPads([Hash.hrefToHexChannelId(data.href)], function (e) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
todo(); todo();
}); });
@ -547,7 +550,7 @@ define([
} }
// Move to root // Move to root
var newName = isFile(element) ? var newName = isFile(element) ?
getAvailableName(newParent, Cryptpad.createChannelId()) : getAvailableName(newParent, Hash.createChannelId()) :
isInTrashRoot(elementPath) ? isInTrashRoot(elementPath) ?
elementPath[1] : elementPath.pop(); elementPath[1] : elementPath.pop();
@ -605,7 +608,7 @@ define([
if (path && isPathIn(newPath, [ROOT]) || filesList.indexOf(id) === -1) { if (path && isPathIn(newPath, [ROOT]) || filesList.indexOf(id) === -1) {
parentEl = find(newPath || [ROOT]); parentEl = find(newPath || [ROOT]);
if (parentEl) { if (parentEl) {
var newName = getAvailableName(parentEl, Cryptpad.createChannelId()); var newName = getAvailableName(parentEl, Hash.createChannelId());
parentEl[newName] = id; parentEl[newName] = id;
return; return;
} }
@ -865,10 +868,10 @@ define([
oldData.forEach(function (obj) { oldData.forEach(function (obj) {
if (!obj || !obj.href) { return; } if (!obj || !obj.href) { return; }
var href = obj.href; var href = obj.href;
var id = Cryptpad.createRandomInteger(); var id = Util.createRandomInteger();
var paths = findFile(href); var paths = findFile(href);
var data = obj; var data = obj;
var key = Cryptpad.createChannelId(); var key = Hash.createChannelId();
if (data) { if (data) {
newData[id] = data; newData[id] = data;
} else { } else {
@ -901,7 +904,7 @@ define([
if (exp.rt) { if (exp.rt) {
exp.rt.sync(); exp.rt.sync();
// TODO // TODO
Cryptpad.whenRealtimeSyncs(exp.rt, next); Realtime.whenRealtimeSyncs(exp.rt, next);
} else { } else {
window.setTimeout(next, 1000); window.setTimeout(next, 1000);
} }
@ -943,8 +946,8 @@ define([
} }
if (typeof element[el] === "string") { if (typeof element[el] === "string") {
// We have an old file (href) which is not in filesData: add it // We have an old file (href) which is not in filesData: add it
var id = Cryptpad.createRandomInteger(); var id = Util.createRandomInteger();
var key = Cryptpad.createChannelId(); var key = Hash.createChannelId();
files[FILES_DATA][id] = {href: element[el], filename: el}; files[FILES_DATA][id] = {href: element[el], filename: el};
element[key] = id; element[key] = id;
delete element[el]; delete element[el];
@ -968,7 +971,7 @@ define([
if (!$.isArray(obj.path)) { toClean.push(idx); return; } if (!$.isArray(obj.path)) { toClean.push(idx); return; }
if (typeof obj.element === "string") { if (typeof obj.element === "string") {
// We have an old file (href) which is not in filesData: add it // We have an old file (href) which is not in filesData: add it
var id = Cryptpad.createRandomInteger(); var id = Util.createRandomInteger();
files[FILES_DATA][id] = {href: obj.element, filename: el}; files[FILES_DATA][id] = {href: obj.element, filename: el};
obj.element = id; obj.element = id;
} }
@ -1002,7 +1005,7 @@ define([
}; };
var fixTemplate = function () { var fixTemplate = function () {
if (!Array.isArray(files[TEMPLATE])) { debug("TEMPLATE was not an array"); files[TEMPLATE] = []; } if (!Array.isArray(files[TEMPLATE])) { debug("TEMPLATE was not an array"); files[TEMPLATE] = []; }
files[TEMPLATE] = Cryptpad.deduplicateString(files[TEMPLATE].slice()); files[TEMPLATE] = Util.deduplicateString(files[TEMPLATE].slice());
var us = files[TEMPLATE]; var us = files[TEMPLATE];
var rootFiles = getFiles([ROOT]).slice(); var rootFiles = getFiles([ROOT]).slice();
var toClean = []; var toClean = [];
@ -1012,7 +1015,7 @@ define([
} }
if (typeof el === "string") { if (typeof el === "string") {
// We have an old file (href) which is not in filesData: add it // We have an old file (href) which is not in filesData: add it
var id = Cryptpad.createRandomInteger(); var id = Util.createRandomInteger();
files[FILES_DATA][id] = {href: el}; files[FILES_DATA][id] = {href: el};
us[idx] = id; us[idx] = id;
} }
@ -1050,7 +1053,7 @@ define([
toClean.push(id); toClean.push(id);
continue; continue;
} }
var parsed = Cryptpad.parsePadUrl(el.href); var parsed = Hash.parsePadUrl(el.href);
if (!parsed.hash) { if (!parsed.hash) {
debug("Removing an element in filesData with a invalid href.", el); debug("Removing an element in filesData with a invalid href.", el);
toClean.push(id); toClean.push(id);
@ -1059,7 +1062,7 @@ define([
if ((loggedIn || config.testMode) && rootFiles.indexOf(id) === -1) { if ((loggedIn || config.testMode) && rootFiles.indexOf(id) === -1) {
debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el); debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el);
var newName = Cryptpad.createChannelId(); var newName = Hash.createChannelId();
root[newName] = id; root[newName] = id;
continue; continue;
} }

@ -3,12 +3,12 @@ define([
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/common/toolbar3.js', '/common/toolbar3.js',
'/bower_components/chainpad-json-validator/json-ot.js', '/bower_components/chainpad-json-validator/json-ot.js',
'/common/cryptpad-common.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/common/hyperscript.js', '/common/hyperscript.js',
'/contacts/messenger-ui.js', '/contacts/messenger-ui.js',
'/common/sframe-messenger-inner.js', '/common/sframe-messenger-inner.js',
'/customize/messages.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
@ -18,35 +18,25 @@ define([
Crypto, Crypto,
Toolbar, Toolbar,
JsonOT, JsonOT,
Cryptpad,
nThen, nThen,
SFCommon, SFCommon,
h, h,
UI, UI,
Messenger Messenger,
Messages
) )
{ {
var Messages = Cryptpad.Messages;
var APP = {}; var APP = {};
var onConnectError = function () {
Cryptpad.errorLoadingScreen(Messages.websocketError);
};
var common; var common;
var sFrameChan; var sFrameChan;
nThen(function (waitFor) { nThen(function (waitFor) {
$(waitFor(Cryptpad.addLoadingScreen)); $(waitFor(UI.addLoadingScreen));
SFCommon.create(waitFor(function (c) { APP.common = common = c; })); SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
sFrameChan = common.getSframeChannel(); sFrameChan = common.getSframeChannel();
sFrameChan.onReady(waitFor()); sFrameChan.onReady(waitFor());
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
onConnectError();
}
});
var toolbarElement = h('div#cp-toolbar.cp-toolbar-container'); var toolbarElement = h('div#cp-toolbar.cp-toolbar-container');
document.body.appendChild(toolbarElement); document.body.appendChild(toolbarElement);
@ -73,10 +63,8 @@ define([
var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle']; var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle'];
var configTb = { var configTb = {
displayed: displayed, displayed: displayed,
common: Cryptpad,
sfCommon: common, sfCommon: common,
$container: $(toolbarElement), $container: $(toolbarElement),
network: Cryptpad.getNetwork(),
pageTitle: Messages.contacts_title, pageTitle: Messages.contacts_title,
metadataMgr: common.getMetadataMgr(), metadataMgr: common.getMetadataMgr(),
}; };
@ -87,15 +75,15 @@ define([
UI.create(messenger, $(friendList), $(messaging), common); UI.create(messenger, $(friendList), $(messaging), common);
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
/* /*
sFrameChan.query('Q_HEY_BUDDY', null, function (err, data) { sFrameChan.query('Q_HEY_BUDDY', null, function (err, data) {
if (!data) { return; } if (!data) { return; }
if (data.error) { if (data.error) {
Cryptpad.warn(data.error); UI.warn(data.error);
} else { } else {
Cryptpad.log(data.response); UI.log(data.response);
} }
});*/ });*/
}); });

@ -1,14 +1,17 @@
define([ define([
'jquery', 'jquery',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/customize/messages.js',
'/common/common-util.js',
'/common/common-interface.js',
'/common/common-notifier.js',
'/common/hyperscript.js', '/common/hyperscript.js',
'/bower_components/marked/marked.min.js', '/bower_components/marked/marked.min.js',
'/common/media-tag.js', '/common/media-tag.js',
], function ($, Cryptpad, h, Marked, MediaTag) { ], function ($, Cryptpad, Messages, Util, UI, Notifier, h, Marked, MediaTag) {
'use strict'; 'use strict';
var UI = {}; var MessengerUI = {};
var Messages = Cryptpad.Messages;
var m = function (md) { var m = function (md) {
var d = h('div.cp-app-contacts-content'); var d = h('div.cp-app-contacts-content');
@ -40,7 +43,7 @@ define([
}; };
}; };
UI.create = function (messenger, $userlist, $messages, common) { MessengerUI.create = function (messenger, $userlist, $messages, common) {
var origin = common.getMetadataMgr().getPrivateData().origin; var origin = common.getMetadataMgr().getPrivateData().origin;
var state = window.state = { var state = window.state = {
@ -163,12 +166,12 @@ define([
}); });
$(removeHistory).click(function () { $(removeHistory).click(function () {
Cryptpad.confirm(Messages.contacts_confirmRemoveHistory, function (yes) { UI.confirm(Messages.contacts_confirmRemoveHistory, function (yes) {
if (!yes) { return; } if (!yes) { return; }
Cryptpad.clearOwnedChannel(data.channel, function (e) { Cryptpad.clearOwnedChannel(data.channel, function (e) {
if (e) { if (e) {
console.error(e); console.error(e);
Cryptpad.alert(Messages.contacts_removeHistoryServerError); UI.alert(Messages.contacts_removeHistoryServerError);
return; return;
} }
}); });
@ -355,8 +358,8 @@ define([
$(remove).click(function (e) { $(remove).click(function (e) {
e.stopPropagation(); e.stopPropagation();
Cryptpad.confirm(Messages._getKey('contacts_confirmRemove', [ UI.confirm(Messages._getKey('contacts_confirmRemove', [
Cryptpad.fixHTML(data.displayName) Util.fixHTML(data.displayName)
]), function (yes) { ]), function (yes) {
if (!yes) { return; } if (!yes) { return; }
removeFriend(curvePublic, function (e) { removeFriend(curvePublic, function (e) {
@ -389,7 +392,7 @@ define([
var initializing = true; var initializing = true;
messenger.on('message', function (message) { messenger.on('message', function (message) {
if (!initializing) { Cryptpad.notify(); } if (!initializing) { Notifier.notify(); }
var curvePublic = message.curve; var curvePublic = message.curve;
var name = displayNames[curvePublic]; var name = displayNames[curvePublic];
@ -514,7 +517,7 @@ define([
count--; count--;
if (count === 0) { if (count === 0) {
initializing = false; initializing = false;
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
} }
}; };
ready(); ready();
@ -524,5 +527,5 @@ define([
}); });
}; };
return UI; return MessengerUI;
}); });

@ -5,6 +5,9 @@ define([
'json.sortify', 'json.sortify',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/common-util.js', '/common/common-util.js',
'/common/common-hash.js',
'/common/common-ui-elements.js',
'/common/common-interface.js',
'/common/cryptget.js', '/common/cryptget.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
@ -12,6 +15,7 @@ define([
'/common/userObject.js', '/common/userObject.js',
'/customize/application_config.js', '/customize/application_config.js',
'/common/sframe-chainpad-listmap.js', '/common/sframe-chainpad-listmap.js',
'/customize/messages.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
@ -23,16 +27,18 @@ define([
JSONSortify, JSONSortify,
Cryptpad, Cryptpad,
Util, Util,
Hash,
UIElements,
UI,
Cryptget, Cryptget,
nThen, nThen,
SFCommon, SFCommon,
CommonRealtime, CommonRealtime,
FO, FO,
AppConfig, AppConfig,
Listmap) Listmap,
Messages)
{ {
var Messages = Cryptpad.Messages;
var APP = window.APP = { var APP = window.APP = {
editable: false, editable: false,
Cryptpad: Cryptpad, Cryptpad: Cryptpad,
@ -43,10 +49,6 @@ define([
return JSONSortify(obj); return JSONSortify(obj);
}; };
var onConnectError = function () {
Cryptpad.errorLoadingScreen(Messages.websocketError);
};
var E_OVER_LIMIT = 'E_OVER_LIMIT'; var E_OVER_LIMIT = 'E_OVER_LIMIT';
var SEARCH = "search"; var SEARCH = "search";
@ -76,7 +78,7 @@ define([
var logError = config.logError = function () { var logError = config.logError = function () {
console.error.apply(console, arguments); console.error.apply(console, arguments);
}; };
var log = config.log = Cryptpad.log; var log = config.log = UI.log;
var localStore = window.cryptpadStore; var localStore = window.cryptpadStore;
APP.store = {}; APP.store = {};
@ -255,7 +257,7 @@ define([
currentPath = [FILES_DATA]; currentPath = [FILES_DATA];
$tree.hide(); $tree.hide();
if (Object.keys(files.root).length && !proxy.anonymousAlert) { if (Object.keys(files.root).length && !proxy.anonymousAlert) {
Cryptpad.alert(Messages.fm_alert_anonymous, null, true); UI.alert(Messages.fm_alert_anonymous, null, true);
proxy.anonymousAlert = true; proxy.anonymousAlert = true;
} }
} }
@ -417,10 +419,6 @@ define([
var ev = {}; var ev = {};
if (e.ctrlKey) { ev.ctrlKey = true; } if (e.ctrlKey) { ev.ctrlKey = true; }
if (e.shiftKey) { ev.shiftKey = true; } if (e.shiftKey) { ev.shiftKey = true; }
var click = function (el) {
if (!el) { return; }
APP.onElementClick(ev, $(el));
};
// Enter // Enter
if (e.which === 13) { if (e.which === 13) {
@ -442,6 +440,11 @@ define([
if ([37, 38, 39, 40].indexOf(e.which) === -1) { return; } if ([37, 38, 39, 40].indexOf(e.which) === -1) { return; }
e.preventDefault(); e.preventDefault();
var click = function (el) {
if (!el) { return; }
APP.onElementClick(ev, $(el));
};
var $selection = $content.find('.cp-app-drive-element.cp-app-drive-element-selected'); var $selection = $content.find('.cp-app-drive-element.cp-app-drive-element-selected');
if ($selection.length === 0) { return void click($elements.first()[0]); } if ($selection.length === 0) { return void click($elements.first()[0]); }
@ -1144,7 +1147,7 @@ define([
var data = filesOp.getFileData(element); var data = filesOp.getFileData(element);
if (!data) { return void logError("No data for the file", element); } if (!data) { return void logError("No data for the file", element); }
var hrefData = Cryptpad.parsePadUrl(data.href); var hrefData = Hash.parsePadUrl(data.href);
if (hrefData.type) { if (hrefData.type) {
$span.addClass('cp-border-color-'+hrefData.type); $span.addClass('cp-border-color-'+hrefData.type);
} }
@ -1211,9 +1214,9 @@ define([
// This is duplicated in cryptpad-common, it should be unified // This is duplicated in cryptpad-common, it should be unified
var getFileIcon = function (id) { var getFileIcon = function (id) {
var data = filesOp.getFileData(id); var data = filesOp.getFileData(id);
return Cryptpad.getFileIcon(data); return UI.getFileIcon(data);
}; };
var getIcon = Cryptpad.getIcon; var getIcon = UI.getIcon;
// Create the "li" element corresponding to the file/folder located in "path" // Create the "li" element corresponding to the file/folder located in "path"
var createElement = function (path, elPath, root, isFolder) { var createElement = function (path, elPath, root, isFolder) {
@ -1442,7 +1445,7 @@ define([
$button.addClass('cp-app-drive-toolbar-emptytrash'); $button.addClass('cp-app-drive-toolbar-emptytrash');
$button.attr('title', Messages.fc_empty); $button.attr('title', Messages.fc_empty);
$button.click(function () { $button.click(function () {
Cryptpad.confirm(Messages.fm_emptyTrashDialog, function(res) { UI.confirm(Messages.fm_emptyTrashDialog, function(res) {
if (!res) { return; } if (!res) { return; }
filesOp.emptyTrash(refresh); filesOp.emptyTrash(refresh);
}); });
@ -1457,7 +1460,7 @@ define([
if (type === 'contacts') { return; } if (type === 'contacts') { return; }
if (type === 'todo') { return; } if (type === 'todo') { return; }
if (type === 'file') { return; } if (type === 'file') { return; }
if (!Cryptpad.isLoggedIn() && AppConfig.registeredOnlyTypes && if (!APP.loggedIn && AppConfig.registeredOnlyTypes &&
AppConfig.registeredOnlyTypes.indexOf(type) !== -1) { AppConfig.registeredOnlyTypes.indexOf(type) !== -1) {
return; return;
} }
@ -1471,9 +1474,9 @@ define([
var onCreated = function (err, info) { var onCreated = function (err, info) {
if (err) { if (err) {
if (err === E_OVER_LIMIT) { if (err === E_OVER_LIMIT) {
return void Cryptpad.alert(Messages.pinLimitDrive, null, true); return void UI.alert(Messages.pinLimitDrive, null, true);
} }
return void Cryptpad.alert(Messages.fm_error_cantPin); return void UI.alert(Messages.fm_error_cantPin);
} }
APP.newFolder = info.newPath; APP.newFolder = info.newPath;
refresh(); refresh();
@ -1549,7 +1552,7 @@ define([
options: options, options: options,
feedback: 'DRIVE_NEWPAD_LOCALFOLDER', feedback: 'DRIVE_NEWPAD_LOCALFOLDER',
}; };
var $block = Cryptpad.createDropdown(dropdownConfig); var $block = UIElements.createDropdown(dropdownConfig);
// Custom style: // Custom style:
$block.find('button').addClass('cp-app-drive-toolbar-new'); $block.find('button').addClass('cp-app-drive-toolbar-new');
@ -1703,7 +1706,7 @@ define([
var data = filesOp.getFileData(id); var data = filesOp.getFileData(id);
if (!data) { return ''; } if (!data) { return ''; }
if (prop === 'type') { if (prop === 'type') {
var hrefData = Cryptpad.parsePadUrl(data.href); var hrefData = Hash.parsePadUrl(data.href);
return hrefData.type; return hrefData.type;
} }
if (prop === 'atime' || prop === 'ctime') { if (prop === 'atime' || prop === 'ctime') {
@ -1738,7 +1741,7 @@ define([
}; };
} }
if (prop === 'type') { if (prop === 'type') {
var hrefData = Cryptpad.parsePadUrl(e.href); var hrefData = Hash.parsePadUrl(e.href);
return hrefData.type; return hrefData.type;
} }
if (prop === 'atime' || prop === 'ctime') { if (prop === 'atime' || prop === 'ctime') {
@ -1799,7 +1802,7 @@ define([
.text(Messages.fm_newFile)); .text(Messages.fm_newFile));
$element.attr('title', Messages.fm_newFile); $element.attr('title', Messages.fm_newFile);
$element.click(function () { $element.click(function () {
var $modal = Cryptpad.createModal({ var $modal = UIElements.createModal({
id: 'cp-app-drive-new-ghost-dialog', id: 'cp-app-drive-new-ghost-dialog',
$body: $('body') $body: $('body')
}); });
@ -1953,7 +1956,7 @@ define([
filesList.forEach(function (r) { filesList.forEach(function (r) {
r.paths.forEach(function (path) { r.paths.forEach(function (path) {
var href = r.data.href; var href = r.data.href;
var parsed = Cryptpad.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
var $table = $('<table>'); var $table = $('<table>');
var $icon = $('<td>', {'rowspan': '3', 'class': 'cp-app-drive-search-icon'}) var $icon = $('<td>', {'rowspan': '3', 'class': 'cp-app-drive-search-icon'})
.append(getFileIcon(href)); .append(getFileIcon(href));
@ -2384,6 +2387,13 @@ define([
if (!filesOp.comparePath(newLocation, currentPath.slice())) { displayDirectory(newLocation); } if (!filesOp.comparePath(newLocation, currentPath.slice())) { displayDirectory(newLocation); }
return; return;
} }
if (e.which === 27) {
$input.val('');
setSearchCursor(0);
if (search.oldLocation && search.oldLocation.length) { displayDirectory(search.oldLocation); }
else { displayDirectory([ROOT]); }
return;
}
if (APP.mobile()) { return; } if (APP.mobile()) { return; }
search.to = window.setTimeout(function () { search.to = window.setTimeout(function () {
if (!isInSearchTmp) { search.oldLocation = currentPath.slice(); } if (!isInSearchTmp) { search.oldLocation = currentPath.slice(); }
@ -2443,13 +2453,13 @@ define([
if (!filesOp.isFile(id)) { return; } if (!filesOp.isFile(id)) { return; }
var data = filesOp.getFileData(id); var data = filesOp.getFileData(id);
if (!data) { return; } if (!data) { return; }
var parsed = Cryptpad.parsePadUrl(data.href); var parsed = Hash.parsePadUrl(data.href);
if (parsed.hashData.type !== "pad") { return; } if (parsed.hashData.type !== "pad") { return; }
var i = data.href.indexOf('#') + 1; var i = data.href.indexOf('#') + 1;
var base = data.href.slice(0, i); var base = data.href.slice(0, i);
var hrefsecret = Cryptpad.getSecrets(parsed.type, parsed.hash); var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash);
if (!hrefsecret.keys) { return; } if (!hrefsecret.keys) { return; }
var viewHash = Cryptpad.getViewHashFromKeys(hrefsecret.channel, hrefsecret.keys); var viewHash = Hash.getViewHashFromKeys(hrefsecret.channel, hrefsecret.keys);
return base + viewHash; return base + viewHash;
}; };
@ -2477,17 +2487,17 @@ define([
$('<br>').appendTo($d); $('<br>').appendTo($d);
if (!ro) { if (!ro) {
$('<label>', {'for': 'cp-app-drive-prop-link'}).text(Messages.editShare).appendTo($d); $('<label>', {'for': 'cp-app-drive-prop-link'}).text(Messages.editShare).appendTo($d);
$d.append(Cryptpad.dialog.selectable(base + data.href, { $d.append(UI.dialog.selectable(base + data.href, {
id: 'cp-app-drive-prop-link', id: 'cp-app-drive-prop-link',
})); }));
} }
var parsed = Cryptpad.parsePadUrl(data.href); var parsed = Hash.parsePadUrl(data.href);
if (parsed.hashData && parsed.hashData.type === 'pad') { if (parsed.hashData && parsed.hashData.type === 'pad') {
var roLink = ro ? base + data.href : base + getReadOnlyUrl(el); var roLink = ro ? base + data.href : base + getReadOnlyUrl(el);
if (roLink) { if (roLink) {
$('<label>', {'for': 'cp-app-drive-prop-rolink'}).text(Messages.viewShare).appendTo($d); $('<label>', {'for': 'cp-app-drive-prop-rolink'}).text(Messages.viewShare).appendTo($d);
$d.append(Cryptpad.dialog.selectable(roLink, { $d.append(UI.dialog.selectable(roLink, {
id: 'cp-app-drive-prop-rolink', id: 'cp-app-drive-prop-rolink',
})); }));
} }
@ -2495,20 +2505,20 @@ define([
if (data.tags && Array.isArray(data.tags)) { if (data.tags && Array.isArray(data.tags)) {
$('<label>', {'for': 'cp-app-drive-prop-tags'}).text(Messages.fm_prop_tagsList).appendTo($d); $('<label>', {'for': 'cp-app-drive-prop-tags'}).text(Messages.fm_prop_tagsList).appendTo($d);
$d.append(Cryptpad.dialog.selectable(data.tags.join(', '), { $d.append(UI.dialog.selectable(data.tags.join(', '), {
id: 'cp-app-drive-prop-tags', id: 'cp-app-drive-prop-tags',
})); }));
} }
$('<label>', {'for': 'cp-app-drive-prop-ctime'}).text(Messages.fm_creation) $('<label>', {'for': 'cp-app-drive-prop-ctime'}).text(Messages.fm_creation)
.appendTo($d); .appendTo($d);
$d.append(Cryptpad.dialog.selectable(new Date(data.ctime).toLocaleString(), { $d.append(UI.dialog.selectable(new Date(data.ctime).toLocaleString(), {
id: 'cp-app-drive-prop-ctime', id: 'cp-app-drive-prop-ctime',
})); }));
$('<label>', {'for': 'cp-app-drive-prop-atime'}).text(Messages.fm_lastAccess) $('<label>', {'for': 'cp-app-drive-prop-atime'}).text(Messages.fm_lastAccess)
.appendTo($d); .appendTo($d);
$d.append(Cryptpad.dialog.selectable(new Date(data.atime).toLocaleString(), { $d.append(UI.dialog.selectable(new Date(data.atime).toLocaleString(), {
id: 'cp-app-drive-prop-atime', id: 'cp-app-drive-prop-atime',
})); }));
@ -2524,7 +2534,7 @@ define([
return void cb(void 0, $d); return void cb(void 0, $d);
} }
var KB = Cryptpad.bytesToKilobytes(bytes); var KB = Util.bytesToKilobytes(bytes);
var formatted = Messages._getKey('formattedKB', [KB]); var formatted = Messages._getKey('formattedKB', [KB]);
$('<br>').appendTo($d); $('<br>').appendTo($d);
@ -2533,7 +2543,7 @@ define([
'for': 'cp-app-drive-prop-size' 'for': 'cp-app-drive-prop-size'
}).text(Messages.fc_sizeInKilobytes).appendTo($d); }).text(Messages.fc_sizeInKilobytes).appendTo($d);
$d.append(Cryptpad.dialog.selectable(formatted, { $d.append(UI.dialog.selectable(formatted, {
id: 'cp-app-drive-prop-size', id: 'cp-app-drive-prop-size',
})); }));
cb(void 0, $d); cb(void 0, $d);
@ -2593,7 +2603,7 @@ define([
el = filesOp.find(paths[0].path); el = filesOp.find(paths[0].path);
getProperties(el, function (e, $prop) { getProperties(el, function (e, $prop) {
if (e) { return void logError(e); } if (e) { return void logError(e); }
Cryptpad.alert($prop[0], undefined, true); UI.alert($prop[0], undefined, true);
}); });
} }
else if ($(this).hasClass("cp-app-drive-context-hashtag")) { else if ($(this).hasClass("cp-app-drive-context-hashtag")) {
@ -2639,7 +2649,7 @@ define([
if (paths.length === 1) { if (paths.length === 1) {
msg = Messages.fm_removePermanentlyDialog; msg = Messages.fm_removePermanentlyDialog;
} }
Cryptpad.confirm(msg, function(res) { UI.confirm(msg, function(res) {
$(window).focus(); $(window).focus();
if (!res) { return; } if (!res) { return; }
filesOp.delete(pathsList, refresh); filesOp.delete(pathsList, refresh);
@ -2653,7 +2663,7 @@ define([
el = filesOp.find(paths[0].path); el = filesOp.find(paths[0].path);
getProperties(el, function (e, $prop) { getProperties(el, function (e, $prop) {
if (e) { return void logError(e); } if (e) { return void logError(e); }
Cryptpad.alert($prop[0], undefined, true); UI.alert($prop[0], undefined, true);
}); });
} }
else if ($(this).hasClass("cp-app-drive-context-hashtag")) { else if ($(this).hasClass("cp-app-drive-context-hashtag")) {
@ -2672,10 +2682,10 @@ define([
var path = $(this).data('path'); var path = $(this).data('path');
var onCreated = function (err, info) { var onCreated = function (err, info) {
if (err === E_OVER_LIMIT) { if (err === E_OVER_LIMIT) {
return void Cryptpad.alert(Messages.pinLimitDrive, null, true); return void UI.alert(Messages.pinLimitDrive, null, true);
} }
if (err) { if (err) {
return void Cryptpad.alert(Messages.fm_error_cantPin); return void UI.alert(Messages.fm_error_cantPin);
} }
APP.newFolder = info.newPath; APP.newFolder = info.newPath;
refresh(); refresh();
@ -2700,7 +2710,7 @@ define([
return; return;
} }
if ($(this).hasClass("cp-app-drive-context-empty")) { if ($(this).hasClass("cp-app-drive-context-empty")) {
Cryptpad.confirm(Messages.fm_emptyTrashDialog, function(res) { UI.confirm(Messages.fm_emptyTrashDialog, function(res) {
if (!res) { return; } if (!res) { return; }
filesOp.emptyTrash(refresh); filesOp.emptyTrash(refresh);
}); });
@ -2720,7 +2730,7 @@ define([
var name = paths[0].path[paths[0].path.length - 1]; var name = paths[0].path[paths[0].path.length - 1];
if ($(this).hasClass("cp-app-drive-context-remove")) { if ($(this).hasClass("cp-app-drive-context-remove")) {
if (paths.length === 1) { if (paths.length === 1) {
Cryptpad.confirm(Messages.fm_removePermanentlyDialog, function(res) { UI.confirm(Messages.fm_removePermanentlyDialog, function(res) {
if (!res) { return; } if (!res) { return; }
filesOp.delete([path], refresh); filesOp.delete([path], refresh);
}); });
@ -2729,7 +2739,7 @@ define([
var pathsList = []; var pathsList = [];
paths.forEach(function (p) { pathsList.push(p.path); }); paths.forEach(function (p) { pathsList.push(p.path); });
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]); var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]);
Cryptpad.confirm(msg, function(res) { UI.confirm(msg, function(res) {
if (!res) { return; } if (!res) { return; }
filesOp.delete(pathsList, refresh); filesOp.delete(pathsList, refresh);
}); });
@ -2744,7 +2754,7 @@ define([
name = path[1]; name = path[1];
} }
} }
Cryptpad.confirm(Messages._getKey("fm_restoreDialog", [name]), function(res) { UI.confirm(Messages._getKey("fm_restoreDialog", [name]), function(res) {
if (!res) { return; } if (!res) { return; }
filesOp.restore(path, refresh); filesOp.restore(path, refresh);
}); });
@ -2753,7 +2763,7 @@ define([
if (paths.length !== 1 || path.length !== 4) { return; } if (paths.length !== 1 || path.length !== 4) { return; }
var element = filesOp.find(path.slice(0,3)); // element containing the oldpath var element = filesOp.find(path.slice(0,3)); // element containing the oldpath
var sPath = stringifyPath(element.path); var sPath = stringifyPath(element.path);
Cryptpad.alert('<strong>' + Messages.fm_originalPath + "</strong>:<br>" + sPath, undefined, true); UI.alert('<strong>' + Messages.fm_originalPath + "</strong>:<br>" + sPath, undefined, true);
} }
APP.hideMenu(); APP.hideMenu();
}); });
@ -2802,7 +2812,7 @@ define([
msg = Messages.fm_removePermanentlyDialog; msg = Messages.fm_removePermanentlyDialog;
} }
Cryptpad.confirm(msg, function(res) { UI.confirm(msg, function(res) {
$(window).focus(); $(window).focus();
if (!res) { return; } if (!res) { return; }
filesOp.delete(paths, refresh); filesOp.delete(paths, refresh);
@ -2813,6 +2823,19 @@ define([
moveElements(paths, [TRASH], false, refresh); moveElements(paths, [TRASH], false, refresh);
} }
}); });
var isCharacterKey = function (e) {
return e.which === "undefined" /* IE */ ||
(e.which > 0 && e.which !== 13 && e.which !== 27 && !e.ctrlKey && !e.altKey);
};
$appContainer.on('keypress', function (e) {
var $searchBar = $tree.find('#cp-app-drive-tree-search-input');
if ($searchBar.is(':focus')) { return; }
if (isCharacterKey(e)) {
$searchBar.focus();
e.preventDefault();
return;
}
});
$appContainer.contextmenu(function () { $appContainer.contextmenu(function () {
APP.hideMenu(); APP.hideMenu();
return false; return false;
@ -2884,7 +2907,7 @@ define([
APP.FM = common.createFileManager(fmConfig); APP.FM = common.createFileManager(fmConfig);
refresh(); refresh();
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
}; };
var setHistory = function (bool, update) { var setHistory = function (bool, update) {
@ -2902,7 +2925,7 @@ define([
nThen(function (waitFor) { nThen(function (waitFor) {
$(waitFor(function () { $(waitFor(function () {
Cryptpad.addLoadingScreen(); UI.addLoadingScreen();
})); }));
window.cryptpadStore.getAll(waitFor(function (val) { window.cryptpadStore.getAll(waitFor(function (val) {
APP.store = JSON.parse(JSON.stringify(val)); APP.store = JSON.parse(JSON.stringify(val));
@ -2955,7 +2978,6 @@ define([
metadataMgr: metadataMgr, metadataMgr: metadataMgr,
readOnly: readOnly, readOnly: readOnly,
realtime: info.realtime, realtime: info.realtime,
common: Cryptpad,
sfCommon: common, sfCommon: common,
$container: APP.$bar $container: APP.$bar
}; };
@ -2996,7 +3018,7 @@ define([
$backupButton.on('click', function() { $backupButton.on('click', function() {
var url = window.location.origin + window.location.pathname + '#' + editHash; var url = window.location.origin + window.location.pathname + '#' + editHash;
var msg = Messages.fm_alert_backupUrl + '<input type="text" readonly="readonly" id="fm_backupUrl" value="'+url+'">'; var msg = Messages.fm_alert_backupUrl + '<input type="text" readonly="readonly" id="fm_backupUrl" value="'+url+'">';
Cryptpad.alert(msg, undefined, true); UI.alert(msg, undefined, true);
$('#fm_backupUrl').val(url); $('#fm_backupUrl').val(url);
$('#fm_backupUrl').click(function () { $('#fm_backupUrl').click(function () {
$(this).select(); $(this).select();
@ -3019,19 +3041,19 @@ define([
APP.files = proxy; APP.files = proxy;
if (!proxy.drive || typeof(proxy.drive) !== 'object') { proxy.drive = {}; } if (!proxy.drive || typeof(proxy.drive) !== 'object') { proxy.drive = {}; }
andThen(common, proxy); andThen(common, proxy);
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
}; };
var onDisconnect = APP.onDisconnect = function (noAlert) { var onDisconnect = APP.onDisconnect = function (noAlert) {
setEditable(false); setEditable(false);
if (APP.refresh) { APP.refresh(); } if (APP.refresh) { APP.refresh(); }
APP.toolbar.failed(); APP.toolbar.failed();
if (!noAlert) { Cryptpad.alert(Messages.common_connectionLost, undefined, true); } if (!noAlert) { UI.alert(Messages.common_connectionLost, undefined, true); }
}; };
var onReconnect = function (info) { var onReconnect = function (info) {
setEditable(true); setEditable(true);
if (APP.refresh) { APP.refresh(); } if (APP.refresh) { APP.refresh(); }
APP.toolbar.reconnecting(info.myId); APP.toolbar.reconnecting(info.myId);
Cryptpad.findOKButton().click(); UI.findOKButton().click();
}; };
proxy.on('create', function (info) { proxy.on('create', function (info) {
@ -3045,13 +3067,7 @@ define([
proxy.on('reconnect', function (info) { proxy.on('reconnect', function (info) {
onReconnect(info); onReconnect(info);
}); });
common.onLogout(function () { setEditable(false); });
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
onConnectError();
}
});
//Cryptpad.onLogout(function () { setEditable(false); });
}); });
}; };
main(); main();

@ -38,9 +38,9 @@ define([
}; };
window.addEventListener('message', onMsg); window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var getSecrets = function (Cryptpad) { var getSecrets = function (Cryptpad, Utils) {
var hash = window.location.hash.slice(1) || Cryptpad.getUserHash() || localStorage.FS_hash; var hash = window.location.hash.slice(1) || Cryptpad.getUserHash() || localStorage.FS_hash;
return Cryptpad.getSecrets('drive', hash); return Utils.Hash.getSecrets('drive', hash);
}; };
Netflux.connect(Cryptpad.getWebsocketURL()).then(function (network) { Netflux.connect(Cryptpad.getWebsocketURL()).then(function (network) {
SFCommonO.start({ SFCommonO.start({

@ -7,10 +7,11 @@ define([
'/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/common-interface.js',
//'/common/visible.js', //'/common/visible.js',
//'/common/notify.js', //'/common/notify.js',
'/bower_components/file-saver/FileSaver.min.js' '/bower_components/file-saver/FileSaver.min.js'
], function ($, Config, Messages, Board, TextPatcher, Listmap, Crypto, Cryptpad /*, Visible, Notify*/) { ], function ($, Config, Messages, Board, TextPatcher, Listmap, Crypto, Cryptpad, UI /*, Visible, Notify*/) {
// var saveAs = window.saveAs; // var saveAs = window.saveAs;
@ -49,7 +50,7 @@ define([
}); });
var firstUser = function () { var firstUser = function () {
Cryptpad.log("You are the first user to visit this board"); UI.log("You are the first user to visit this board");
}; };
var whenReady = function () { var whenReady = function () {
@ -81,13 +82,13 @@ define([
window.location.hash = info.channel + secret.key; window.location.hash = info.channel + secret.key;
}) })
.on('ready', function () { .on('ready', function () {
Cryptpad.log("Ready!"); UI.log("Ready!");
whenReady({ whenReady({
}); });
}) })
.on('disconnect', function () { .on('disconnect', function () {
Cryptpad.warn("Disconnected!"); UI.warn("Disconnected!");
}); });
}); });
}); });

@ -2,10 +2,13 @@ define([
'jquery', 'jquery',
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/common/toolbar3.js', '/common/toolbar3.js',
'/common/cryptpad-common.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/common/common-realtime.js', '/common/common-realtime.js',
'/common/common-util.js',
'/common/common-hash.js',
'/common/common-interface.js',
'/customize/messages.js',
'/file/file-crypto.js', '/file/file-crypto.js',
'/common/media-tag.js', '/common/media-tag.js',
@ -20,20 +23,20 @@ define([
$, $,
Crypto, Crypto,
Toolbar, Toolbar,
Cryptpad,
nThen, nThen,
SFCommon, SFCommon,
CommonRealtime, CommonRealtime,
Util,
Hash,
UI,
Messages,
FileCrypto, FileCrypto,
MediaTag) MediaTag)
{ {
var Messages = Cryptpad.Messages;
var saveAs = window.saveAs; var saveAs = window.saveAs;
var Nacl = window.nacl; var Nacl = window.nacl;
var APP = window.APP = { var APP = window.APP = {};
Cryptpad: Cryptpad,
};
var andThen = function (common) { var andThen = function (common) {
var $appContainer = $('#cp-app-file-content'); var $appContainer = $('#cp-app-file-content');
@ -58,9 +61,9 @@ define([
if (!priv.filehash) { if (!priv.filehash) {
uploadMode = true; uploadMode = true;
} else { } else {
secret = Cryptpad.getSecrets('file', priv.filehash); secret = Hash.getSecrets('file', priv.filehash);
if (!secret.keys) { throw new Error("You need a hash"); } if (!secret.keys) { throw new Error("You need a hash"); }
hexFileName = Cryptpad.base64ToHex(secret.channel); hexFileName = Util.base64ToHex(secret.channel);
} }
var Title = common.createTitle({}); var Title = common.createTitle({});
@ -70,7 +73,6 @@ define([
} }
var configTb = { var configTb = {
displayed: displayed, displayed: displayed,
common: Cryptpad,
//hideDisplayName: true, //hideDisplayName: true,
$container: $bar, $container: $bar,
metadataMgr: metadataMgr, metadataMgr: metadataMgr,
@ -84,7 +86,7 @@ define([
toolbar.$rightside.html(''); toolbar.$rightside.html('');
if (!uploadMode) { if (!uploadMode) {
var src = Cryptpad.getBlobPathFromHex(hexFileName); var src = Hash.getBlobPathFromHex(hexFileName);
var cryptKey = secret.keys && secret.keys.fileKeyStr; var cryptKey = secret.keys && secret.keys.fileKeyStr;
var key = Nacl.util.decodeBase64(cryptKey); var key = Nacl.util.decodeBase64(cryptKey);
@ -106,7 +108,7 @@ define([
var $mt = $dlview.find('media-tag'); var $mt = $dlview.find('media-tag');
var cryptKey = secret.keys && secret.keys.fileKeyStr; var cryptKey = secret.keys && secret.keys.fileKeyStr;
var hexFileName = Cryptpad.base64ToHex(secret.channel); var hexFileName = Util.base64ToHex(secret.channel);
$mt.attr('src', '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName); $mt.attr('src', '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName);
$mt.attr('data-crypto-key', 'cryptpad:'+cryptKey); $mt.attr('data-crypto-key', 'cryptpad:'+cryptKey);
@ -128,7 +130,7 @@ define([
} }
$dlButton.addClass('btn btn-success'); $dlButton.addClass('btn btn-success');
var text = Messages.download_mt_button + '<br>'; var text = Messages.download_mt_button + '<br>';
text += '<b>' + Cryptpad.fixHTML(title) + '</b><br>'; text += '<b>' + Util.fixHTML(title) + '</b><br>';
text += '<em>' + Messages._getKey('formattedMB', [sizeMb]) + '</em>'; text += '<em>' + Messages._getKey('formattedMB', [sizeMb]) + '</em>';
$dlButton.html(text); $dlButton.html(text);
@ -165,7 +167,7 @@ define([
}) })
.on('decryptionError', function (e) { .on('decryptionError', function (e) {
var error = e.originalEvent; var error = e.originalEvent;
//Cryptpad.alert(error.message); //UI.alert(error.message);
cb(error.message); cb(error.message);
}) })
.on('decryptionProgress', function (e) { .on('decryptionProgress', function (e) {
@ -203,9 +205,9 @@ define([
var todoBigFile = function (sizeMb) { var todoBigFile = function (sizeMb) {
$dlform.show(); $dlform.show();
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
$dllabel.append($('<br>')); $dllabel.append($('<br>'));
$dllabel.append(Cryptpad.fixHTML(metadata.name)); $dllabel.append(Util.fixHTML(metadata.name));
// don't display the size if you don't know it. // don't display the size if you don't know it.
if (typeof(sizeM) === 'number') { if (typeof(sizeM) === 'number') {
@ -217,7 +219,7 @@ define([
if (decrypting) { return; } if (decrypting) { return; }
decrypting = true; decrypting = true;
displayFile(ev, sizeMb, function (err) { displayFile(ev, sizeMb, function (err) {
if (err) { Cryptpad.alert(err); } if (err) { UI.alert(err); }
}); });
}; };
if (typeof(sizeMb) === 'number' && sizeMb < 5) { return void onClick(); } if (typeof(sizeMb) === 'number' && sizeMb < 5) { return void onClick(); }
@ -226,9 +228,9 @@ define([
var href = priv.origin + priv.pathname + priv.filehash; var href = priv.origin + priv.pathname + priv.filehash;
common.getFileSize(href, function (e, data) { common.getFileSize(href, function (e, data) {
if (e) { if (e) {
return void Cryptpad.errorLoadingScreen(e); return void UI.errorLoadingScreen(e);
} }
var size = Cryptpad.bytesToMegabytes(data); var size = Util.bytesToMegabytes(data);
return void todoBigFile(size); return void todoBigFile(size);
}); });
}); });
@ -238,8 +240,8 @@ define([
// we're in upload mode // we're in upload mode
if (!common.isLoggedIn()) { if (!common.isLoggedIn()) {
return Cryptpad.alert(Messages.upload_mustLogin, function () { return UI.alert(Messages.upload_mustLogin, function () {
Cryptpad.errorLoadingScreen(Messages.upload_mustLogin); UI.errorLoadingScreen(Messages.upload_mustLogin);
common.setLoginRedirect(function () { common.setLoginRedirect(function () {
common.gotoURL('/login/'); common.gotoURL('/login/');
}); });
@ -264,7 +266,7 @@ define([
FM.handleFile(file); FM.handleFile(file);
}); });
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
}; };
var main = function () { var main = function () {
@ -272,7 +274,7 @@ define([
nThen(function (waitFor) { nThen(function (waitFor) {
$(waitFor(function () { $(waitFor(function () {
Cryptpad.addLoadingScreen(); UI.addLoadingScreen();
})); }));
SFCommon.create(waitFor(function (c) { APP.common = common = c; })); SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {

@ -50,6 +50,7 @@
img { img {
max-width: 100px; max-width: 100px;
max-height: 100px; max-height: 100px;
background: #fff;
} }
.cp-filepicker-content-element-name { .cp-filepicker-content-element-name {

@ -3,10 +3,14 @@ define([
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.js', '/bower_components/textpatcher/TextPatcher.js',
'/bower_components/chainpad-json-validator/json-ot.js', '/bower_components/chainpad-json-validator/json-ot.js',
'/common/cryptpad-common.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/common/common-interface.js',
'/common/common-ui-elements.js',
'/common/common-util.js',
'/common/common-hash.js',
'json.sortify', 'json.sortify',
'/customize/messages.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
@ -16,20 +20,16 @@ define([
Crypto, Crypto,
TextPatcher, TextPatcher,
JsonOT, JsonOT,
Cryptpad,
nThen, nThen,
SFCommon, SFCommon,
Sortify) UI,
UIElements,
Util,
Hash,
Sortify,
Messages)
{ {
var Messages = Cryptpad.Messages; var APP = window.APP = {};
var APP = window.APP = {
Cryptpad: Cryptpad,
};
var onConnectError = function () {
Cryptpad.errorLoadingScreen(Messages.websocketError);
};
var andThen = function (common) { var andThen = function (common) {
var metadataMgr = common.getMetadataMgr(); var metadataMgr = common.getMetadataMgr();
@ -41,10 +41,10 @@ define([
sframeChan.event('EV_FILE_PICKER_CLOSE'); sframeChan.event('EV_FILE_PICKER_CLOSE');
}; };
var onFilePicked = function (data) { var onFilePicked = function (data) {
var parsed = Cryptpad.parsePadUrl(data.url); var parsed = Hash.parsePadUrl(data.url);
hideFileDialog(); hideFileDialog();
if (parsed.type === 'file') { if (parsed.type === 'file') {
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel); var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
sframeChan.event("EV_FILE_PICKED", { sframeChan.event("EV_FILE_PICKED", {
type: parsed.type, type: parsed.type,
@ -80,7 +80,7 @@ define([
var createFileDialog = function () { var createFileDialog = function () {
var types = filters.types || []; var types = filters.types || [];
// Create modal // Create modal
var $blockContainer = Cryptpad.createModal({ var $blockContainer = UIElements.createModal({
id: 'cp-filepicker-dialog', id: 'cp-filepicker-dialog',
$body: $body, $body: $body,
onClose: hideFileDialog onClose: hideFileDialog
@ -132,7 +132,7 @@ define([
'class': 'cp-filepicker-content-element', 'class': 'cp-filepicker-content-element',
'title': name, 'title': name,
}).appendTo($container); }).appendTo($container);
$span.append(Cryptpad.getFileIcon(data)); $span.append(UI.getFileIcon(data));
$('<span>', {'class': 'cp-filepicker-content-element-name'}).text(name) $('<span>', {'class': 'cp-filepicker-content-element-name'}).text(name)
.appendTo($span); .appendTo($span);
$span.click(function () { $span.click(function () {
@ -149,8 +149,6 @@ define([
updateContainer(); updateContainer();
}; };
sframeChan.on('EV_FILE_PICKER_REFRESH', function (newFilters) { sframeChan.on('EV_FILE_PICKER_REFRESH', function (newFilters) {
console.log(Sortify(filters));
console.log(Sortify(newFilters));
if (Sortify(filters) !== Sortify(newFilters)) { if (Sortify(filters) !== Sortify(newFilters)) {
$body.html(''); $body.html('');
filters = newFilters; filters = newFilters;
@ -160,7 +158,7 @@ define([
}); });
createFileDialog(); createFileDialog();
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
}; };
var main = function () { var main = function () {
@ -168,16 +166,11 @@ define([
nThen(function (waitFor) { nThen(function (waitFor) {
$(waitFor(function () { $(waitFor(function () {
Cryptpad.addLoadingScreen({hideTips: true, hideLogo: true}); UI.addLoadingScreen({hideTips: true, hideLogo: true});
})); }));
SFCommon.create(waitFor(function (c) { APP.common = common = c; })); SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var metadataMgr = common.getMetadataMgr(); var metadataMgr = common.getMetadataMgr();
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
onConnectError();
}
});
if (metadataMgr.getMetadataLazy() !== 'uninitialized') { if (metadataMgr.getMetadataLazy() !== 'uninitialized') {
andThen(common); andThen(common);
return; return;

@ -38,25 +38,13 @@ define([
window.addEventListener('message', onMsg); window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var Cryptpad; var Cryptpad;
var Crypto;
var Cryptget;
nThen(function (waitFor) { nThen(function (waitFor) {
// Load #2, the loading screen is up so grab whatever you need... Cryptpad = config.modules.Cryptpad;
require([ config.modules.SFrameChannel.create($('#sbox-filePicker-iframe')[0].contentWindow,
'/common/cryptpad-common.js', waitFor(function (sfc) {
'/bower_components/chainpad-crypto/crypto.js',
'/common/cryptget.js',
'/common/sframe-channel.js',
], waitFor(function (_Cryptpad, _Crypto, _Cryptget, SFrameChannel) {
Cryptpad = _Cryptpad;
Crypto = _Crypto;
Cryptget = _Cryptget;
SFrameChannel.create($('#sbox-filePicker-iframe')[0].contentWindow, waitFor(function (sfc) {
sframeChan = sfc; sframeChan = sfc;
})); }));
Cryptpad.ready(waitFor());
}));
}).nThen(function () { }).nThen(function () {
var proxy = Cryptpad.getProxy(); var proxy = Cryptpad.getProxy();
var updateMeta = function () { var updateMeta = function () {
@ -94,29 +82,7 @@ define([
sframeChan.onReg('EV_METADATA_UPDATE', updateMeta); sframeChan.onReg('EV_METADATA_UPDATE', updateMeta);
proxy.on('change', 'settings', updateMeta); proxy.on('change', 'settings', updateMeta);
Cryptpad.onError(function (info) { config.addCommonRpc(sframeChan);
console.log('error');
console.log(info);
if (info && info.type === "store") {
//onConnectError();
}
});
sframeChan.on('Q_ANON_RPC_MESSAGE', function (data, cb) {
Cryptpad.anonRpcMsg(data.msg, data.content, function (err, response) {
cb({error: err, response: response});
});
});
sframeChan.on('Q_GET_PIN_LIMIT_STATUS', function (data, cb) {
Cryptpad.isOverPinLimit(function (e, overLimit, limits) {
cb({
error: e,
overLimit: overLimit,
limits: limits
});
});
});
sframeChan.on('Q_GET_FILES_LIST', function (types, cb) { sframeChan.on('Q_GET_FILES_LIST', function (types, cb) {
console.error("TODO: make sure Q_GET_FILES_LIST is only available from filepicker"); console.error("TODO: make sure Q_GET_FILES_LIST is only available from filepicker");

@ -1,10 +1,12 @@
define([ define([
'jquery', 'jquery',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/bower_components/chainpad-listmap/chainpad-listmap.js', '/common/common-interface.js',
//'/common/common-hash.js',
//'/bower_components/chainpad-listmap/chainpad-listmap.js',
//'/common/curve.js', //'/common/curve.js',
'less!/invite/main.less', 'less!/invite/main.less',
], function ($, Cryptpad /*, Listmap, Curve*/) { ], function ($, Cryptpad, UI/*, Hash , Listmap, Curve*/) {
var Messages = Cryptpad.Messages; var Messages = Cryptpad.Messages;
var comingSoon = function () { var comingSoon = function () {
return $('<div>', { return $('<div>', {
@ -15,7 +17,7 @@ define([
}; };
$(function () { $(function () {
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
console.log("wut"); console.log("wut");
$('body #mainBlock').append(comingSoon()); $('body #mainBlock').append(comingSoon());
}); });
@ -33,12 +35,12 @@ define([
var andThen = function () { var andThen = function () {
var hash = window.location.hash.slice(1); var hash = window.location.hash.slice(1);
var info = Cryptpad.parseTypeHash('invite', hash); var info = Hash.parseTypeHash('invite', hash);
console.log(info); console.log(info);
if (!info.pubkey) { if (!info.pubkey) {
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
Cryptpad.alert('invalid invite'); UI.alert('invalid invite');
return; return;
} }
@ -48,7 +50,7 @@ define([
var keys = Curve.deriveKeys(info.pubkey, mySecret); var keys = Curve.deriveKeys(info.pubkey, mySecret);
var encryptor = Curve.createEncryptor(keys); var encryptor = Curve.createEncryptor(keys);
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
var listmapConfig = { var listmapConfig = {
data: {}, data: {},
@ -72,23 +74,6 @@ define([
$(function () { $(function () {
var $main = $('#mainBlock'); var $main = $('#mainBlock');
// Language selector
var $sel = $('#language-selector');
Cryptpad.createLanguageSelector(undefined, $sel);
$sel.find('button').addClass('btn').addClass('btn-secondary');
$sel.show();
// User admin menu
var $userMenu = $('#user-menu');
var userMenuCfg = {
$initBlock: $userMenu
};
var $userAdmin = Cryptpad.createUserAdminMenu(userMenuCfg);
$userAdmin.find('button').addClass('btn').addClass('btn-secondary');
$(window).click(function () {
$('.cp-dropdown-content').hide();
});
// main block is hidden in case javascript is disabled // main block is hidden in case javascript is disabled
$main.removeClass('hidden'); $main.removeClass('hidden');

@ -2,31 +2,15 @@ define([
'jquery', 'jquery',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/login.js', '/common/login.js',
'/common/common-interface.js',
'/common/common-realtime.js',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
], function ($, Cryptpad, Login) { ], function ($, Cryptpad, Login, UI, Realtime) {
$(function () { $(function () {
var $main = $('#mainBlock'); var $main = $('#mainBlock');
var Messages = Cryptpad.Messages; var Messages = Cryptpad.Messages;
// Language selector
var $sel = $('#language-selector');
Cryptpad.createLanguageSelector(undefined, $sel);
$sel.find('button').addClass('btn').addClass('btn-secondary');
$sel.show();
// User admin menu
var $userMenu = $('#user-menu');
var userMenuCfg = {
$initBlock: $userMenu
};
var $userAdmin = Cryptpad.createUserAdminMenu(userMenuCfg);
$userAdmin.find('button').addClass('btn').addClass('btn-secondary');
$(window).click(function () {
$('.cp-dropdown-content').hide();
});
// main block is hidden in case javascript is disabled // main block is hidden in case javascript is disabled
$main.removeClass('hidden'); $main.removeClass('hidden');
@ -74,7 +58,7 @@ define([
// setTimeout 100ms to remove the keyboard on mobile devices before the loading screen pops up // setTimeout 100ms to remove the keyboard on mobile devices before the loading screen pops up
window.setTimeout(function () { window.setTimeout(function () {
Cryptpad.addLoadingScreen({ UI.addLoadingScreen({
loadingText: Messages.login_hashing, loadingText: Messages.login_hashing,
hideTips: true, hideTips: true,
}); });
@ -100,7 +84,7 @@ define([
proxy.curvePublic = result.curvePublic; proxy.curvePublic = result.curvePublic;
Cryptpad.feedback('LOGIN', true); Cryptpad.feedback('LOGIN', true);
Cryptpad.whenRealtimeSyncs(result.realtime, function() { Realtime.whenRealtimeSyncs(result.realtime, function() {
Cryptpad.login(result.userHash, result.userName, function () { Cryptpad.login(result.userHash, result.userName, function () {
hashing = false; hashing = false;
if (sessionStorage.redirectTo) { if (sessionStorage.redirectTo) {
@ -120,28 +104,28 @@ define([
} }
switch (err) { switch (err) {
case 'NO_SUCH_USER': case 'NO_SUCH_USER':
Cryptpad.removeLoadingScreen(function () { UI.removeLoadingScreen(function () {
Cryptpad.alert(Messages.login_noSuchUser, function () { UI.alert(Messages.login_noSuchUser, function () {
hashing = false; hashing = false;
}); });
}); });
break; break;
case 'INVAL_USER': case 'INVAL_USER':
Cryptpad.removeLoadingScreen(function () { UI.removeLoadingScreen(function () {
Cryptpad.alert(Messages.login_invalUser, function () { UI.alert(Messages.login_invalUser, function () {
hashing = false; hashing = false;
}); });
}); });
break; break;
case 'INVAL_PASS': case 'INVAL_PASS':
Cryptpad.removeLoadingScreen(function () { UI.removeLoadingScreen(function () {
Cryptpad.alert(Messages.login_invalPass, function () { UI.alert(Messages.login_invalPass, function () {
hashing = false; hashing = false;
}); });
}); });
break; break;
default: // UNHANDLED ERROR default: // UNHANDLED ERROR
Cryptpad.errorLoadingScreen(Messages.login_unhandledError); UI.errorLoadingScreen(Messages.login_unhandledError);
} }
}); });
}); });

@ -1,16 +0,0 @@
<!DOCTYPE html>
<html class="cp">
<!-- If this file is not called customize.dist/src/template.html, it is generated -->
<head>
<title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
</head>
<body class="html">
<noscript>
<p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
<p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
</noscript>
</html>

@ -1,569 +0,0 @@
define([
'jquery',
'/common/cryptpad-common.js',
'/common/cryptget.js',
'/common/mergeDrive.js',
'/common/toolbar2.js',
'/bower_components/file-saver/FileSaver.min.js',
'less!/customize/src/less/cryptpad.less',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/customize/src/less/toolbar.less',
'less!/settings/main.less',
], function ($, Cryptpad, Crypt, Merge, Toolbar) {
var saveAs = window.saveAs;
var USERNAME_KEY = 'cryptpad.username';
var APP = window.APP = {
Cryptpad: Cryptpad,
_onRefresh: []
};
var Messages = Cryptpad.Messages;
// Manage changes in the realtime object made from another page
var onRefresh = function (h) {
if (typeof(h) !== "function") { return; }
if (APP._onRefresh.indexOf(h) !== -1) { return; }
APP._onRefresh.push(h);
};
var refresh = APP.refresh = function () {
APP._onRefresh.forEach(function (h) {
h();
});
};
var categories = {
'account': [
'infoBlock',
'displayName',
'languageSelector',
'logoutEverywhere',
'resetTips',
'thumbnails',
'userFeedback'
],
'drive': [
'backupDrive',
'importLocalPads',
'resetDrive'
],
'code': [
'indentUnit',
'indentType'
]
};
var createInfoBlock = function (store) {
var obj = store.proxy;
var $div = $('<div>', {'class': 'infoBlock'});
var $account = $('<div>', {'class': 'element'}).appendTo($div);
var accountName = obj.login_name || localStorage[Cryptpad.userNameKey];
var $label = $('<span>', {'class': 'label'}).text(Messages.user_accountName);
var $name = $('<span>').text(accountName || '');
if (!accountName) {
$label.text('');
$name.text(Messages.settings_anonymous);
}
$account.append($label).append($name);
var publicKey = obj.edPublic;
if (publicKey) {
var $key = $('<div>', {'class': 'element'}).appendTo($div);
var userHref = Cryptpad.getUserHrefFromKeys(accountName, publicKey);
var $pubLabel = $('<span>', {'class': 'label'})
.text(Messages.settings_publicSigningKey);
$key.append($pubLabel).append(Cryptpad.dialog.selectable(userHref));
}
return $div;
};
// Create the block containing the display name field
var createDisplayNameInput = function (store) {
var obj = store.proxy;
var $div = $('<div>', {'class': 'displayName element'});
$('<label>', {'for' : 'displayName'}).text(Messages.user_displayName).appendTo($div);
var $inputBlock = $('<div>', {'class': 'inputBlock'}).appendTo($div);
var $input = $('<input>', {
'type': 'text',
'id': 'displayName',
'placeholder': Messages.anonymous}).appendTo($inputBlock);
var $save = $('<button>', {'class': 'btn btn-primary'}).text(Messages.settings_save).appendTo($inputBlock);
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
var displayName = obj[USERNAME_KEY] || '';
$input.val(displayName);
// When the display name is changed (enter or button clicked)
var todo = function () {
displayName = $input.val();
if (displayName === obj[USERNAME_KEY]) { return; }
obj[USERNAME_KEY] = displayName;
Cryptpad.changeDisplayName(displayName);
$spinner.show();
Cryptpad.whenRealtimeSyncs(store.info.realtime, function () {
$spinner.hide();
$ok.show();
});
};
$input.on('keyup', function (e) {
if ($input.val() !== displayName) { $ok.hide(); }
if (e.which === 13) { todo(); }
});
$save.click(todo);
// On remote change
var onChange = function () {
if (obj[USERNAME_KEY] !== $input.val()) {
$input.val(obj[USERNAME_KEY]);
$input.focusout();
}
};
onRefresh(onChange);
return $div;
};
var createIndentUnitSelector = function (obj) {
var proxy = obj.proxy;
proxy.settings = proxy.settings || {};
var $div = $('<div>', {
'class': 'indentUnit element'
});
$('<label>').text(Messages.settings_codeIndentation).appendTo($div);
var $inputBlock = $('<div>', {
'class': 'inputBlock',
}).appendTo($div);
var $input = $('<input>', {
'min': 1,
'max': 8,
type: 'number',
}).on('change', function () {
var val = parseInt($input.val());
if (typeof(val) !== 'number') { return; }
Cryptpad.setAttribute(['codemirror', 'indentUnit'], val);
}).appendTo($inputBlock);
proxy.on('change', [ 'settings', 'codemirror', 'indentUnit', ], function (o, n) {
$input.val(n);
});
Cryptpad.getAttribute(['codemirror', 'indentUnit'], function (e, val) {
if (e) { return void console.error(e); }
if (typeof(val) !== 'number') {
$input.val(2);
} else {
$input.val(val);
}
});
return $div;
};
var createIndentTypeSelector = function (obj) {
var proxy = obj.proxy;
proxy.settings = proxy.settings || {};
var key = 'indentWithTabs';
var $div = $('<div>', {
'class': 'indentType element'
});
$('<label>').text(Messages.settings_codeUseTabs).appendTo($div);
var $inputBlock = $('<div>', {
'class': 'inputBlock',
}).css('flex-flow', 'column')
.appendTo($div);
var $input = $('<input>', {
type: 'checkbox',
}).on('change', function () {
var val = $input.is(':checked');
if (typeof(val) !== 'boolean') { return; }
Cryptpad.setAttribute(['codemirror', key], val);
}).appendTo($inputBlock);
$input[0].checked = !!proxy.settings[key];
proxy.on('change', ['settings', 'codemirror', key], function (o, n) {
$input[0].checked = !!n;
});
Cryptpad.getAttribute(['codemirror', key], function (e, val) {
if (e) { return void console.error(e); }
$input[0].checked = !!val;
});
return $div;
};
var createResetTips = function () {
var $div = $('<div>', {'class': 'resetTips element'});
$('<label>', {'for' : 'resetTips'}).text(Messages.settings_resetTips).appendTo($div);
$('<span>', {'class': 'description'})
.text(Messages.settings_resetTipsButton).appendTo($div);
var $button = $('<button>', {'id': 'resetTips', 'class': 'btn btn-primary'})
.text(Messages.settings_resetTipsAction).appendTo($div);
$button.click(function () {
Object.keys(localStorage).forEach(function (k) {
if(k.slice(0, 9) === "hide-info") {
delete localStorage[k];
}
});
Cryptpad.alert(Messages.settings_resetTipsDone);
});
return $div;
};
var createThumbnails = function (obj) {
var $div = $('<div>', {'class': 'thumbnails element'});
$('<label>').text(Messages.settings_thumbnails).appendTo($div);
// Disable
$('<span>', {'class': 'description'})
.text(Messages.settings_disableThumbnailsDescription).appendTo($div);
var $label = $('<label>', { 'for': 'disableThumbnails', 'class': 'noTitle' })
.text(Messages.settings_disableThumbnailsAction);
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
var $checkbox = $('<input>', {
'type': 'checkbox',
id: 'disableThumbnails'
}).on('change', function () {
$spinner.show();
$ok.hide();
if (!obj.proxy.settings) { obj.proxy.settings = {}; }
if (!obj.proxy.settings.general) { obj.proxy.settings.general = {}; }
obj.proxy.settings.general.disableThumbnails = $checkbox.is(':checked') || false;
Cryptpad.whenRealtimeSyncs(obj.info.realtime, function () {
$spinner.hide();
$ok.show();
});
});
$checkbox.appendTo($div);
$label.appendTo($div);
$ok.hide().appendTo($div);
$spinner.hide().appendTo($div);
if (obj.proxy.settings && obj.proxy.settings.general
&& obj.proxy.settings.general.disableThumbnails) {
$checkbox[0].checked = true;
}
// Reset
/*$('<span>', {'class': 'description'})
.text(Messages.settings_resetThumbnailsDescription).appendTo($div);
var $button = $('<button>', {'id': 'resetThumbnails', 'class': 'btn btn-primary'})
.text(Messages.settings_resetThumbnailsAction).appendTo($div);
$button.click(function () {
// TODO we need to have access to the sandbox localforage first...
Cryptpad.alert(Messages.settings_resetThumbnailsDone);
});*/
return $div;
};
var createBackupDrive = function (store) {
var obj = store.proxy;
var $div = $('<div>', {'class': 'backupDrive element'});
var exportFile = function () {
var sjson = JSON.stringify(obj);
var name = obj.login_name || obj[USERNAME_KEY] || Messages.anonymous;
var suggestion = name + '-' + new Date().toDateString();
Cryptpad.prompt(Cryptpad.Messages.exportPrompt,
Cryptpad.fixFileName(suggestion) + '.json', function (filename) {
if (!(typeof(filename) === 'string' && filename)) { return; }
var blob = new Blob([sjson], {type: "application/json;charset=utf-8"});
saveAs(blob, filename);
});
};
var importFile = function (content) {
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).appendTo($div);
Crypt.put(Cryptpad.getUserHash() || localStorage[Cryptpad.fileHashKey], content, function (e) {
if (e) { console.error(e); }
$spinner.remove();
});
};
$('<label>', {'for' : 'exportDrive'}).text(Messages.settings_backupCategory).appendTo($div);
$('<span>', {'class': 'description'})
.text(Messages.settings_backupTitle).appendTo($div);
/* add an export button */
var $export = Cryptpad.createButton('export', true, {}, exportFile);
$export.attr('class', 'btn btn-success').text(Messages.settings_backup);
$div.append($export);
/* add an import button */
var $import = Cryptpad.createButton('import', true, {}, importFile);
$import.attr('class', 'btn btn-success').text(Messages.settings_restore);
$div.append($import);
return $div;
};
var createResetDrive = function (obj) {
var $div = $('<div>', {'class': 'resetDrive element'});
$('<label>', {'for' : 'resetDrive'}).text(Messages.settings_resetNewTitle).appendTo($div);
$('<span>', {'class': 'description'})
.text(Messages.settings_reset).appendTo($div);
var $button = $('<button>', {'id': 'resetDrive', 'class': 'btn btn-danger'})
.text(Messages.settings_resetButton).appendTo($div);
$button.click(function () {
Cryptpad.prompt(Messages.settings_resetPrompt, "", function (val) {
if (val !== "I love CryptPad") {
Cryptpad.alert(Messages.settings_resetError);
return;
}
obj.proxy.drive = Cryptpad.getStore().getEmptyObject();
Cryptpad.alert(Messages.settings_resetDone);
}, undefined, true);
});
return $div;
};
var createUserFeedbackToggle = function (obj) {
var $div = $('<div>', { 'class': 'userFeedback element'});
$('<span>', {'class': 'label'}).text(Messages.settings_userFeedbackTitle).appendTo($div);
var $label = $('<label>', { 'for': 'userFeedback', 'class': 'noTitle' })
.text(Messages.settings_userFeedback);
$('<span>', {'class': 'description'})
.append(Messages.settings_userFeedbackHint1)
.append(Messages.settings_userFeedbackHint2).appendTo($div);
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
var $checkbox = $('<input>', {
'type': 'checkbox',
}).on('change', function () {
$spinner.show();
$ok.hide();
obj.proxy.allowUserFeedback = $checkbox.is(':checked') || false;
Cryptpad.whenRealtimeSyncs(obj.info.realtime, function () {
$spinner.hide();
$ok.show();
});
});
$checkbox.appendTo($div);
$label.appendTo($div);
$ok.hide().appendTo($div);
$spinner.hide().appendTo($div);
if (obj.proxy.allowUserFeedback) {
$checkbox[0].checked = true;
}
return $div;
};
var createUsageButton = function () {
Cryptpad.createUsageBar(function (err, $bar) {
if (err) { return void console.error(err); }
APP.$usage.html('').append($bar);
}, true);
};
var createLogoutEverywhere = function (obj) {
var proxy = obj.proxy;
var $div = $('<div>', { 'class': 'logoutEverywhere element'});
$('<label>', { 'for': 'logoutEverywhere'})
.text(Messages.settings_logoutEverywhereTitle).appendTo($div);
$('<span>', {'class': 'description'})
.text(Messages.settings_logoutEverywhere).appendTo($div);
var $button = $('<button>', { id: 'logoutEverywhere', 'class': 'btn btn-primary' })
.text(Messages.settings_logoutEverywhereButton)
.appendTo($div);
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
$button.click(function () {
var realtime = obj.info.realtime;
console.log(realtime);
Cryptpad.confirm(Messages.settings_logoutEverywhereConfirm, function (yes) {
if (!yes) { return; }
$spinner.show();
$ok.hide();
var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
localStorage.setItem('loginToken', token);
proxy.loginToken = token;
Cryptpad.whenRealtimeSyncs(realtime, function () {
$spinner.hide();
$ok.show();
window.setTimeout(function () {
$ok.fadeOut(1500);
}, 2500);
});
});
});
return $div;
};
var createImportLocalPads = function (obj) {
if (!Cryptpad.isLoggedIn()) { return; }
var $div = $('<div>', {'class': 'importLocalPads element'});
$('<label>', {'for' : 'importLocalPads'}).text(Messages.settings_import).appendTo($div);
$('<span>', {'class': 'description'})
.text(Messages.settings_importTitle).appendTo($div);
var $button = $('<button>', {'id': 'importLocalPads', 'class': 'btn btn-primary'})
.text(Messages.settings_import).appendTo($div);
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
$button.click(function () {
Cryptpad.confirm(Messages.settings_importConfirm, function (yes) {
if (!yes) { return; }
$spinner.show();
$ok.hide();
Merge.anonDriveIntoUser(obj, localStorage.FS_hash, function () {
$spinner.hide();
$ok.show();
Cryptpad.alert(Messages.settings_importDone);
});
}, undefined, true);
});
return $div;
};
var createLanguageSelector = function () {
var $div = $('<div>', {'class': 'languageSelector element'});
$('<label>').text(Messages.language).appendTo($div);
var $b = Cryptpad.createLanguageSelector().appendTo($div);
$b.find('button').addClass('btn btn-secondary');
return $div;
};
var hideCategories = function () {
APP.$rightside.find('> div').hide();
};
var showCategories = function (cat) {
hideCategories();
cat.forEach(function (c) {
APP.$rightside.find('.'+c).show();
});
};
var createLeftside = function () {
var $categories = $('<div>', {'class': 'categories'}).appendTo(APP.$leftside);
APP.$usage = $('<div>', {'class': 'usage'}).appendTo(APP.$leftside);
var active = 'account';
Object.keys(categories).forEach(function (key) {
var $category = $('<div>', {'class': 'category'}).appendTo($categories);
if (key === 'account') { $category.append($('<span>', {'class': 'fa fa-user-o'})); }
if (key === 'drive') { $category.append($('<span>', {'class': 'fa fa-hdd-o'})); }
if (key === 'code') { $category.append($('<span>', {'class': 'fa fa-file-code-o' })); }
if (key === active) {
$category.addClass('active');
}
$category.click(function () {
active = key;
$categories.find('.active').removeClass('active');
$category.addClass('active');
showCategories(categories[key]);
});
$category.append(Messages['settings_cat_'+key]);
});
showCategories(categories[active]);
};
var createToolbar = function () {
var displayed = ['useradmin', 'newpad', 'limit', 'upgrade', 'pageTitle'];
var configTb = {
displayed: displayed,
ifrw: window,
common: Cryptpad,
$container: APP.$toolbar,
pageTitle: Messages.settings_title
};
var toolbar = APP.toolbar = Toolbar.create(configTb);
toolbar.$rightside.html(''); // Remove the drawer if we don't use it to hide the toolbar
};
var andThen = function (obj) {
APP.$leftside = $('<div>', {id: 'leftSide'}).appendTo(APP.$container);
var $rightside = APP.$rightside = $('<div>', {id: 'rightSide'}).appendTo(APP.$container);
createToolbar();
//$rightside.append(createTitle());
$rightside.append(createInfoBlock(obj));
$rightside.append(createDisplayNameInput(obj));
$rightside.append(createLanguageSelector());
$rightside.append(createIndentUnitSelector(obj));
$rightside.append(createIndentTypeSelector(obj));
if (Cryptpad.isLoggedIn()) {
$rightside.append(createLogoutEverywhere(obj));
}
$rightside.append(createResetTips());
$rightside.append(createThumbnails(obj));
$rightside.append(createBackupDrive(obj));
$rightside.append(createImportLocalPads(obj));
$rightside.append(createResetDrive(obj));
$rightside.append(createUserFeedbackToggle(obj));
obj.proxy.on('change', [], refresh);
obj.proxy.on('remove', [], refresh);
Cryptpad.onDisplayNameChanged(refresh);
createLeftside();
createUsageButton();
Cryptpad.removeLoadingScreen();
};
$(function () {
$(window).click(function () {
$('.cp-dropdown-content').hide();
});
APP.$container = $('#container');
APP.$toolbar = $('#toolbar');
Cryptpad.ready(function () {
//if (!Cryptpad.getUserHash()) { return redirectToMain(); }
var storeObj = Cryptpad.getStore().getProxy && Cryptpad.getStore().getProxy().proxy
? Cryptpad.getStore().getProxy() : undefined;
andThen(storeObj);
Cryptpad.reportAppUsage();
});
});
window.addEventListener('storage', function (e) {
if (e.key !== Cryptpad.userHashKey) { return; }
var o = e.oldValue;
var n = e.newValue;
window.location.reload();
if (o && !n) { // disconnect
//redirectToMain();
}
});
});

@ -1,54 +0,0 @@
@import '/customize/src/less/variables.less';
@import '/customize/src/less/mixins.less';
@import '/customize/src/less/sidebar-layout.less';
.cp {
#container {
#rightSide {
.userFeedback, .thumbnails {
input[type="checkbox"] {
vertical-align: middle;
margin-right: 5px;
}
}
.languageSelector {
button.btn {
width: @button-width;
background-color: @button-alt-bg;
border-color: #adadad;
color: black;
&:hover {
background-color: darken(@button-alt-bg, 15%);
}
}
}
.inputBlock {
input {
border-top-right-radius: 0.25em;
border-bottom-right-radius: 0.25em;
padding: 5px;
padding-left: 15px;
&[type="number"] {
border-right: 1px solid #adadad;
}
&[type="checkbox"] {
margin-right: 100%;
}
}
}
.infoBlock {
[type="text"] {
width: @button-width;
}
}
.backupDrive {
button {
span.fa {
margin-right: 5px;
}
margin-right: 5px;
}
}
}
}
}

@ -26,10 +26,6 @@
height: 28px; height: 28px;
padding: 2px 0; padding: 2px 0;
} }
#cke_1_top .cp-toolbar {
padding: 0;
display: block;
}
.cke_wysiwyg_frame { .cke_wysiwyg_frame {
min-width: 60%; min-width: 60%;
} }

@ -2,7 +2,6 @@ define([
'jquery', 'jquery',
'/bower_components/textpatcher/TextPatcher.js', '/bower_components/textpatcher/TextPatcher.js',
'/common/toolbar3.js', '/common/toolbar3.js',
'/common/cryptpad-common.js',
'/common/common-util.js', '/common/common-util.js',
'/common/cryptget.js', '/common/cryptget.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
@ -14,8 +13,9 @@ define([
'/poll/render.js', '/poll/render.js',
'/common/diffMarked.js', '/common/diffMarked.js',
'/common/sframe-common-codemirror.js', '/common/sframe-common-codemirror.js',
'/common/sframe-common-interface.js',
'/common/common-thumbnail.js', '/common/common-thumbnail.js',
'/common/common-interface.js',
'/customize/messages.js',
'cm/lib/codemirror', 'cm/lib/codemirror',
'cm/addon/display/placeholder', 'cm/addon/display/placeholder',
@ -32,7 +32,6 @@ define([
$, $,
TextPatcher, TextPatcher,
Toolbar, Toolbar,
Cryptpad,
Util, Util,
Cryptget, Cryptget,
nThen, nThen,
@ -44,11 +43,11 @@ define([
Renderer, Renderer,
DiffMd, DiffMd,
SframeCM, SframeCM,
SFUI,
Thumb, Thumb,
UI,
Messages,
CMeditor) CMeditor)
{ {
var Messages = Cryptpad.Messages;
var saveAs = window.saveAs; var saveAs = window.saveAs;
var APP = window.APP = { var APP = window.APP = {
@ -57,10 +56,9 @@ define([
col: [] col: []
}, },
readOnly: false, readOnly: false,
Cryptpad: Cryptpad,
mobile: function () { return $('body').width() <= 600; } // Menu and content area are not inline-block anymore for mobiles mobile: function () { return $('body').width() <= 600; } // Menu and content area are not inline-block anymore for mobiles
}; };
var Render = Renderer(Cryptpad, APP); var Render = Renderer(APP);
var debug = $.noop; //console.log; var debug = $.noop; //console.log;
@ -118,8 +116,8 @@ define([
var exportFile = function () { var exportFile = function () {
var csv = getCSV(); var csv = getCSV();
var suggestion = Title.suggestTitle(Title.defaultTitle); var suggestion = Title.suggestTitle(Title.defaultTitle);
Cryptpad.prompt(Messages.exportPrompt, UI.prompt(Messages.exportPrompt,
Cryptpad.fixFileName(suggestion) + '.csv', function (filename) { Util.fixFileName(suggestion) + '.csv', function (filename) {
if (!(typeof(filename) === 'string' && filename)) { return; } if (!(typeof(filename) === 'string' && filename)) { return; }
var blob = new Blob([csv], {type: "application/csv;charset=utf-8"}); var blob = new Blob([csv], {type: "application/csv;charset=utf-8"});
saveAs(blob, filename); saveAs(blob, filename);
@ -332,7 +330,7 @@ define([
/* 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 && !Array.isArray(path)) {
return; return;
} }
if (path && path.join) { if (path && path.join) {
@ -495,7 +493,7 @@ define([
var isLocked = span.className && span.className.split(' ').indexOf('fa-lock') !== -1; var isLocked = span.className && span.className.split(' ').indexOf('fa-lock') !== -1;
if (type === 'row') { if (type === 'row') {
if (isRemove) { if (isRemove) {
Cryptpad.confirm(Messages.poll_removeOption, function (res) { UI.confirm(Messages.poll_removeOption, function (res) {
if (!res) { return; } if (!res) { return; }
Render.removeRow(APP.proxy, id, function () { Render.removeRow(APP.proxy, id, function () {
change(); change();
@ -508,7 +506,7 @@ define([
} }
} else if (type === 'col') { } else if (type === 'col') {
if (isRemove) { if (isRemove) {
Cryptpad.confirm(Messages.poll_removeUser, function (res) { UI.confirm(Messages.poll_removeUser, function (res) {
if (!res) { return; } if (!res) { return; }
Render.removeColumn(APP.proxy, id, function () { Render.removeColumn(APP.proxy, id, function () {
change(); change();
@ -884,7 +882,7 @@ define([
(proxy.metadata && typeof(proxy.metadata.type) !== 'undefined' && (proxy.metadata && typeof(proxy.metadata.type) !== 'undefined' &&
proxy.metadata.type !== 'poll')) { proxy.metadata.type !== 'poll')) {
var errorText = Messages.typeError; var errorText = Messages.typeError;
Cryptpad.errorLoadingScreen(errorText); UI.errorLoadingScreen(errorText);
throw new Error(errorText); throw new Error(errorText);
} }
} else { } else {
@ -1043,7 +1041,7 @@ define([
publish(true); publish(true);
} }
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
if (isNew) { if (isNew) {
common.openTemplatePicker(); common.openTemplatePicker();
} }
@ -1051,12 +1049,12 @@ define([
var onDisconnect = function () { var onDisconnect = function () {
setEditable(false); setEditable(false);
Cryptpad.alert(Messages.common_connectionLost, undefined, true); UI.alert(Messages.common_connectionLost, undefined, true);
}; };
var onReconnect = function () { var onReconnect = function () {
setEditable(true); setEditable(true);
Cryptpad.findOKButton().click(); UI.findOKButton().click();
}; };
var getHeadingText = function () { var getHeadingText = function () {
@ -1086,7 +1084,6 @@ define([
metadataMgr: metadataMgr, metadataMgr: metadataMgr,
readOnly: APP.readOnly, readOnly: APP.readOnly,
realtime: info.realtime, realtime: info.realtime,
common: Cryptpad,
sfCommon: common, sfCommon: common,
$container: APP.$bar, $container: APP.$bar,
$contentContainer: APP.$content $contentContainer: APP.$content
@ -1167,7 +1164,7 @@ define([
nThen(function (waitFor) { nThen(function (waitFor) {
$(waitFor(function () { $(waitFor(function () {
Cryptpad.addLoadingScreen(); UI.addLoadingScreen();
var $div = $('<div>').append(Pages['/poll/']()); var $div = $('<div>').append(Pages['/poll/']());
$('body').append($div.html()); $('body').append($div.html());
})); }));

@ -1,10 +1,11 @@
define([ define([
//'/common/cryptpad-common.js',
'jquery', 'jquery',
'/bower_components/hyperjson/hyperjson.js', '/bower_components/hyperjson/hyperjson.js',
'/bower_components/textpatcher/TextPatcher.js', '/bower_components/textpatcher/TextPatcher.js',
'/common/common-util.js',
'/customize/messages.js',
'/bower_components/diff-dom/diffDOM.js', '/bower_components/diff-dom/diffDOM.js',
], function ($, Hyperjson, TextPatcher) { ], function ($, Hyperjson, TextPatcher, Util, Messages) {
var DiffDOM = window.diffDOM; var DiffDOM = window.diffDOM;
var Example = { var Example = {
@ -31,7 +32,7 @@ by maintaining indexes in rowsOrder and colsOrder
} }
}; };
var Renderer = function (Cryptpad, APP) { var Renderer = function (APP) {
var Render = { var Render = {
Example: Example Example: Example
@ -64,15 +65,15 @@ var Renderer = function (Cryptpad, APP) {
}; };
var getColumnValue = Render.getColumnValue = function (obj, colId) { var getColumnValue = Render.getColumnValue = function (obj, colId) {
return Cryptpad.find(obj, ['content', 'cols'].concat([colId])); return Util.find(obj, ['content', 'cols'].concat([colId]));
}; };
var getRowValue = Render.getRowValue = function (obj, rowId) { var getRowValue = Render.getRowValue = function (obj, rowId) {
return Cryptpad.find(obj, ['content', 'rows'].concat([rowId])); return Util.find(obj, ['content', 'rows'].concat([rowId]));
}; };
var getCellValue = Render.getCellValue = function (obj, cellId) { var getCellValue = Render.getCellValue = function (obj, cellId) {
var value = Cryptpad.find(obj, ['content', 'cells'].concat([cellId])); var value = Util.find(obj, ['content', 'cells'].concat([cellId]));
if (typeof value === 'boolean') { if (typeof value === 'boolean') {
return (value === true ? 1 : 0); return (value === true ? 1 : 0);
} else { } else {
@ -81,25 +82,25 @@ var Renderer = function (Cryptpad, APP) {
}; };
var setRowValue = Render.setRowValue = function (obj, rowId, value) { var setRowValue = Render.setRowValue = function (obj, rowId, value) {
var parent = Cryptpad.find(obj, ['content', 'rows']); var parent = Util.find(obj, ['content', 'rows']);
if (typeof(parent) === 'object') { return (parent[rowId] = value); } if (typeof(parent) === 'object') { return (parent[rowId] = value); }
return null; return null;
}; };
var setColumnValue = Render.setColumnValue = function (obj, colId, value) { var setColumnValue = Render.setColumnValue = function (obj, colId, value) {
var parent = Cryptpad.find(obj, ['content', 'cols']); var parent = Util.find(obj, ['content', 'cols']);
if (typeof(parent) === 'object') { return (parent[colId] = value); } if (typeof(parent) === 'object') { return (parent[colId] = value); }
return null; return null;
}; };
var setCellValue = Render.setCellValue = function (obj, cellId, value) { var setCellValue = Render.setCellValue = function (obj, cellId, value) {
var parent = Cryptpad.find(obj, ['content', 'cells']); var parent = Util.find(obj, ['content', 'cells']);
if (typeof(parent) === 'object') { return (parent[cellId] = value); } if (typeof(parent) === 'object') { return (parent[cellId] = value); }
return null; return null;
}; };
Render.createColumn = function (obj, cb, id, value) { Render.createColumn = function (obj, cb, id, value) {
var order = Cryptpad.find(obj, ['content', 'colsOrder']); var order = Util.find(obj, ['content', 'colsOrder']);
if (!order) { throw new Error("Uninitialized realtime object!"); } if (!order) { throw new Error("Uninitialized realtime object!"); }
id = id || coluid(); id = id || coluid();
value = value || ""; value = value || "";
@ -109,8 +110,8 @@ var Renderer = function (Cryptpad, APP) {
}; };
Render.removeColumn = function (obj, id, cb) { Render.removeColumn = function (obj, id, cb) {
var order = Cryptpad.find(obj, ['content', 'colsOrder']); var order = Util.find(obj, ['content', 'colsOrder']);
var parent = Cryptpad.find(obj, ['content', 'cols']); var parent = Util.find(obj, ['content', 'cols']);
if (!(order && parent)) { throw new Error("Uninitialized realtime object!"); } if (!(order && parent)) { throw new Error("Uninitialized realtime object!"); }
@ -134,7 +135,7 @@ var Renderer = function (Cryptpad, APP) {
}; };
Render.createRow = function (obj, cb, id, value) { Render.createRow = function (obj, cb, id, value) {
var order = Cryptpad.find(obj, ['content', 'rowsOrder']); var order = Util.find(obj, ['content', 'rowsOrder']);
if (!order) { throw new Error("Uninitialized realtime object!"); } if (!order) { throw new Error("Uninitialized realtime object!"); }
id = id || rowuid(); id = id || rowuid();
value = value || ""; value = value || "";
@ -144,8 +145,8 @@ var Renderer = function (Cryptpad, APP) {
}; };
Render.removeRow = function (obj, id, cb) { Render.removeRow = function (obj, id, cb) {
var order = Cryptpad.find(obj, ['content', 'rowsOrder']); var order = Util.find(obj, ['content', 'rowsOrder']);
var parent = Cryptpad.find(obj, ['content', 'rows']); var parent = Util.find(obj, ['content', 'rows']);
if (!(order && parent)) { throw new Error("Uninitialized realtime object!"); } if (!(order && parent)) { throw new Error("Uninitialized realtime object!"); }
@ -185,15 +186,15 @@ var Renderer = function (Cryptpad, APP) {
}; };
var getRowIds = Render.getRowIds = function (obj) { var getRowIds = Render.getRowIds = function (obj) {
return Cryptpad.find(obj, ['content', 'rowsOrder']); return Util.find(obj, ['content', 'rowsOrder']);
}; };
var getColIds = Render.getColIds = function (obj) { var getColIds = Render.getColIds = function (obj) {
return Cryptpad.find(obj, ['content', 'colsOrder']); return Util.find(obj, ['content', 'colsOrder']);
}; };
var getCells = Render.getCells = function (obj) { var getCells = Render.getCells = function (obj) {
return Cryptpad.find(obj, ['content', 'cells']); return Util.find(obj, ['content', 'cells']);
}; };
/* cellMatrix takes a proxy object, and optionally an alternate ordering /* cellMatrix takes a proxy object, and optionally an alternate ordering
@ -219,13 +220,13 @@ var Renderer = function (Cryptpad, APP) {
'data-rt-id': col, 'data-rt-id': col,
type: 'text', type: 'text',
value: getColumnValue(obj, col) || "", value: getColumnValue(obj, col) || "",
title: getColumnValue(obj, col) || Cryptpad.Messages.anonymous, title: getColumnValue(obj, col) || Messages.anonymous,
placeholder: Cryptpad.Messages.anonymous, placeholder: Messages.anonymous,
disabled: 'disabled' disabled: 'disabled'
}; };
return result; return result;
})).concat([{ })).concat([{
content: Cryptpad.Messages.poll_total content: Messages.poll_total
}]); }]);
} }
if (i === rows.length) { if (i === rows.length) {
@ -239,9 +240,9 @@ var Renderer = function (Cryptpad, APP) {
return [{ return [{
'data-rt-id': row, 'data-rt-id': row,
value: getRowValue(obj, row) || '', value: getRowValue(obj, row) || '',
title: getRowValue(obj, row) || Cryptpad.Messages.poll_optionPlaceholder, title: getRowValue(obj, row) || Messages.poll_optionPlaceholder,
type: 'text', type: 'text',
placeholder: Cryptpad.Messages.poll_optionPlaceholder, placeholder: Messages.poll_optionPlaceholder,
disabled: 'disabled', disabled: 'disabled',
}].concat(cols.map(function (col) { }].concat(cols.map(function (col) {
var id = [col, rows[i-1]].join('_'); var id = [col, rows[i-1]].join('_');
@ -269,7 +270,7 @@ var Renderer = function (Cryptpad, APP) {
var makeRemoveElement = Render.makeRemoveElement = function (id) { var makeRemoveElement = Render.makeRemoveElement = function (id) {
return ['SPAN', { return ['SPAN', {
'data-rt-id': id, 'data-rt-id': id,
'title': Cryptpad.Messages.poll_remove, 'title': Messages.poll_remove,
class: 'cp-app-poll-table-remove', class: 'cp-app-poll-table-remove',
}, ['✖']]; }, ['✖']];
}; };
@ -277,7 +278,7 @@ var Renderer = function (Cryptpad, APP) {
var makeEditElement = Render.makeEditElement = function (id) { var makeEditElement = Render.makeEditElement = function (id) {
return ['SPAN', { return ['SPAN', {
'data-rt-id': id, 'data-rt-id': id,
'title': Cryptpad.Messages.poll_edit, 'title': Messages.poll_edit,
class: 'cp-app-poll-table-edit', class: 'cp-app-poll-table-edit',
}, ['✐']]; }, ['✐']];
}; };
@ -285,7 +286,7 @@ var Renderer = function (Cryptpad, APP) {
var makeLockElement = Render.makeLockElement = function (id) { var makeLockElement = Render.makeLockElement = function (id) {
return ['SPAN', { return ['SPAN', {
'data-rt-id': id, 'data-rt-id': id,
'title': Cryptpad.Messages.poll_locked, 'title': Messages.poll_locked,
class: 'cp-app-poll-table-lock fa fa-lock', class: 'cp-app-poll-table-lock fa fa-lock',
}, []]; }, []];
}; };
@ -293,7 +294,7 @@ var Renderer = function (Cryptpad, APP) {
var makeBookmarkElement = Render.makeBookmarkElement = function (id) { var makeBookmarkElement = Render.makeBookmarkElement = function (id) {
return ['SPAN', { return ['SPAN', {
'data-rt-id': id, 'data-rt-id': id,
'title': Cryptpad.Messages.poll_bookmark_col, 'title': Messages.poll_bookmark_col,
'style': 'visibility: hidden;', 'style': 'visibility: hidden;',
class: 'cp-app-poll-table-bookmark fa fa-thumb-tack', class: 'cp-app-poll-table-bookmark fa fa-thumb-tack',
}, []]; }, []];
@ -383,13 +384,13 @@ var Renderer = function (Cryptpad, APP) {
}; };
var diffIsInput = Render.diffIsInput = function (info) { var diffIsInput = Render.diffIsInput = function (info) {
var nodeName = Cryptpad.find(info, ['node', 'nodeName']); var nodeName = Util.find(info, ['node', 'nodeName']);
if (nodeName !== 'INPUT') { return; } if (nodeName !== 'INPUT') { return; }
return true; return true;
}; };
var getInputType = Render.getInputType = function (info) { var getInputType = Render.getInputType = function (info) {
return Cryptpad.find(info, ['node', 'type']); return Util.find(info, ['node', 'type']);
}; };
var preserveCursor = Render.preserveCursor = function (info) { var preserveCursor = Render.preserveCursor = function (info) {
@ -424,6 +425,10 @@ var Renderer = function (Cryptpad, APP) {
var diffOptions = { var diffOptions = {
preDiffApply: function (info) { preDiffApply: function (info) {
if (!diffIsInput(info)) { return; } if (!diffIsInput(info)) { return; }
if (info.diff.action === "removeAttribute" &&
(info.diff.name === "aria-describedby" || info.diff.name === "data-original-title")) {
return;
}
switch (getInputType(info)) { switch (getInputType(info)) {
case 'number': case 'number':
//console.log('checkbox'); //console.log('checkbox');
@ -457,14 +462,14 @@ var Renderer = function (Cryptpad, APP) {
// Enable input for the userid column // Enable input for the userid column
APP.enableColumn(userid, table); APP.enableColumn(userid, table);
$(table).find('input[disabled="disabled"][data-rt-id^="' + userid + '"]') $(table).find('input[disabled="disabled"][data-rt-id^="' + userid + '"]')
.attr('placeholder', Cryptpad.Messages.poll_userPlaceholder); .attr('placeholder', Messages.poll_userPlaceholder);
$(table).find('.cp-app-poll-table-lock[data-rt-id="' + userid + '"]').remove(); $(table).find('.cp-app-poll-table-lock[data-rt-id="' + userid + '"]').remove();
$(table).find('[data-rt-id^="' + userid + '"]').closest('td') $(table).find('[data-rt-id^="' + userid + '"]').closest('td')
.addClass("cp-app-poll-table-own"); .addClass("cp-app-poll-table-own");
$(table).find('.cp-app-poll-table-bookmark[data-rt-id="' + userid + '"]') $(table).find('.cp-app-poll-table-bookmark[data-rt-id="' + userid + '"]')
.css('visibility', '') .css('visibility', '')
.addClass('cp-app-poll-table-bookmark-full') .addClass('cp-app-poll-table-bookmark-full')
.attr('title', Cryptpad.Messages.poll_bookmarked_col); .attr('title', Messages.poll_bookmarked_col);
}; };
var styleUncommittedColumn = function (table) { var styleUncommittedColumn = function (table) {
APP.uncommitted.content.colsOrder.forEach(function(id) { APP.uncommitted.content.colsOrder.forEach(function(id) {

@ -3,11 +3,15 @@ define([
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/common/sframe-chainpad-listmap.js', '/common/sframe-chainpad-listmap.js',
'/common/toolbar3.js', '/common/toolbar3.js',
'/common/cryptpad-common.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/common/common-util.js',
'/common/common-interface.js',
'/common/common-realtime.js',
'/customize/messages.js',
'/bower_components/marked/marked.min.js', '/bower_components/marked/marked.min.js',
'cm/lib/codemirror', 'cm/lib/codemirror',
'cm/mode/markdown/markdown', 'cm/mode/markdown/markdown',
'css!/bower_components/codemirror/lib/codemirror.css', 'css!/bower_components/codemirror/lib/codemirror.css',
@ -21,21 +25,19 @@ define([
Crypto, Crypto,
Listmap, Listmap,
Toolbar, Toolbar,
Cryptpad,
nThen, nThen,
SFCommon, SFCommon,
Util,
UI,
Realtime,
Messages,
Marked, Marked,
CodeMirror CodeMirror
) )
{ {
var Messages = Cryptpad.Messages;
var APP = window.APP = { var APP = window.APP = {
Cryptpad: Cryptpad,
_onRefresh: [] _onRefresh: []
}; };
var onConnectError = function () {
Cryptpad.errorLoadingScreen(Messages.websocketError);
};
// Decryption event for avatar mediatag (TODO not needed anymore?) // Decryption event for avatar mediatag (TODO not needed anymore?)
$(window.document).on('decryption', function (e) { $(window.document).on('decryption', function (e) {
@ -44,7 +46,7 @@ define([
}) })
.on('decryptionError', function (e) { .on('decryptionError', function (e) {
var error = e.originalEvent; var error = e.originalEvent;
Cryptpad.alert(error.message); UI.alert(error.message);
}); });
$(window).click(function () { $(window).click(function () {
@ -114,7 +116,7 @@ define([
setValue(newVal, function (err) { setValue(newVal, function (err) {
if (err) { return void console.error(err); } if (err) { return void console.error(err); }
lastVal = newVal; lastVal = newVal;
Cryptpad.log(Messages._getKey('profile_fieldSaved', [newVal || fallbackValue])); UI.log(Messages._getKey('profile_fieldSaved', [newVal || fallbackValue]));
editing = false; editing = false;
}); });
}; };
@ -135,16 +137,16 @@ define([
/* jshint ignore:start */ /* jshint ignore:start */
var isFriend = function (proxy, edKey) { var isFriend = function (proxy, edKey) {
var friends = Cryptpad.find(proxy, ['friends']); var friends = Util.find(proxy, ['friends']);
return typeof(edKey) === 'string' && friends && (edKey in friends); return typeof(edKey) === 'string' && friends && (edKey in friends);
}; };
var addCreateInviteLinkButton = function ($container) { var addCreateInviteLinkButton = function ($container) {
return; return;
var obj = APP.lm.proxy; /*var obj = APP.lm.proxy;
var proxy = Cryptpad.getProxy(); var proxy = Cryptpad.getProxy();
var userViewHash = Cryptpad.find(proxy, ['profile', 'view']); var userViewHash = Util.find(proxy, ['profile', 'view']);
var edKey = obj.edKey; var edKey = obj.edKey;
var curveKey = obj.curveKey; var curveKey = obj.curveKey;
@ -158,7 +160,7 @@ define([
var unsafeName = obj.name || ''; var unsafeName = obj.name || '';
console.log(unsafeName); console.log(unsafeName);
var name = Cryptpad.fixHTML(unsafeName) || Messages.anonymous; var name = Util.fixHTML(unsafeName) || Messages.anonymous;
console.log(name); console.log(name);
console.log("Creating invite button"); console.log("Creating invite button");
@ -169,17 +171,17 @@ define([
.addClass('btn btn-success') .addClass('btn btn-success')
.text(Messages.profile_inviteButton) .text(Messages.profile_inviteButton)
.click(function () { .click(function () {
Cryptpad.confirm(Messages._getKey('profile_inviteExplanation', [name]), function (yes) { UI.confirm(Messages._getKey('profile_inviteExplanation', [name]), function (yes) {
if (!yes) { return; } if (!yes) { return; }
console.log(obj.curveKey); console.log(obj.curveKey);
Cryptpad.alert("TODO"); UI.alert("TODO");
// TODO create a listmap object using your curve keys // TODO create a listmap object using your curve keys
// TODO fill the listmap object with your invite data // TODO fill the listmap object with your invite data
// TODO generate link to invite object // TODO generate link to invite object
// TODO copy invite link to clipboard // TODO copy invite link to clipboard
}, null, true); }, null, true);
}) })
.appendTo($container); .appendTo($container);*/
}; };
/* jshint ignore:end */ /* jshint ignore:end */
@ -221,7 +223,7 @@ define([
} }
var setValue = function (value, cb) { var setValue = function (value, cb) {
APP.lm.proxy.name = value; APP.lm.proxy.name = value;
Cryptpad.whenRealtimeSyncs(APP.lm.realtime, cb); Realtime.whenRealtimeSyncs(APP.lm.realtime, cb);
}; };
createEditableInput($block, DISPLAYNAME_ID, placeholder, getValue, setValue, Messages.anonymous); createEditableInput($block, DISPLAYNAME_ID, placeholder, getValue, setValue, Messages.anonymous);
}; };
@ -247,16 +249,21 @@ define([
} }
var setValue = function (value, cb) { var setValue = function (value, cb) {
APP.lm.proxy.url = value; APP.lm.proxy.url = value;
Cryptpad.whenRealtimeSyncs(APP.lm.realtime, cb); Realtime.whenRealtimeSyncs(APP.lm.realtime, cb);
}; };
var placeholder = Messages.profile_urlPlaceholder; var placeholder = Messages.profile_urlPlaceholder;
createEditableInput($block, LINK_ID, placeholder, getValue, setValue); createEditableInput($block, LINK_ID, placeholder, getValue, setValue);
}; };
var allowedMediaTypes = [
'image/png',
'image/jpeg',
'image/jpg',
'image/gif',
];
var addAvatar = function ($container) { var addAvatar = function ($container) {
var $block = $('<div>', {id: AVATAR_ID}).appendTo($container); var $block = $('<div>', {id: AVATAR_ID}).appendTo($container);
var $span = $('<span>').appendTo($block); var $span = $('<span>').appendTo($block);
var allowedMediaTypes = Cryptpad.avatarAllowedTypes;
var sframeChan = common.getSframeChannel(); var sframeChan = common.getSframeChannel();
var displayAvatar = function () { var displayAvatar = function () {
$span.html(''); $span.html('');
@ -280,7 +287,7 @@ define([
$delButton.click(function () { $delButton.click(function () {
var old = common.getMetadataMgr().getUserData().avatar; var old = common.getMetadataMgr().getUserData().avatar;
sframeChan.query("Q_PROFILE_AVATAR_REMOVE", old, function (err, err2) { sframeChan.query("Q_PROFILE_AVATAR_REMOVE", old, function (err, err2) {
if (err || err2) { return void Cryptpad.log(err || err2); } if (err || err2) { return void UI.log(err || err2); }
delete APP.lm.proxy.avatar; delete APP.lm.proxy.avatar;
displayAvatar(); displayAvatar();
}); });
@ -297,14 +304,14 @@ define([
var old = common.getMetadataMgr().getUserData().avatar; var old = common.getMetadataMgr().getUserData().avatar;
var todo = function () { var todo = function () {
sframeChan.query("Q_PROFILE_AVATAR_ADD", data.url, function (err, err2) { sframeChan.query("Q_PROFILE_AVATAR_ADD", data.url, function (err, err2) {
if (err || err2) { return void Cryptpad.log(err || err2); } if (err || err2) { return void UI.log(err || err2); }
APP.lm.proxy.avatar = data.url; APP.lm.proxy.avatar = data.url;
displayAvatar(); displayAvatar();
}); });
}; };
if (old) { if (old) {
sframeChan.query("Q_PROFILE_AVATAR_REMOVE", old, function (err, err2) { sframeChan.query("Q_PROFILE_AVATAR_REMOVE", old, function (err, err2) {
if (err || err2) { return void Cryptpad.log(err || err2); } if (err || err2) { return void UI.log(err || err2); }
todo(); todo();
}); });
return; return;
@ -316,7 +323,7 @@ define([
var data = { var data = {
FM: APP.FM, FM: APP.FM,
filter: function (file) { filter: function (file) {
var sizeMB = Cryptpad.bytesToMegabytes(file.size); var sizeMB = Util.bytesToMegabytes(file.size);
var type = file.type; var type = file.type;
return sizeMB <= 0.5 && allowedMediaTypes.indexOf(type) !== -1; return sizeMB <= 0.5 && allowedMediaTypes.indexOf(type) !== -1;
}, },
@ -361,7 +368,7 @@ define([
$spinner.show(); $spinner.show();
var val = editor.getValue(); var val = editor.getValue();
APP.lm.proxy.description = val; APP.lm.proxy.description = val;
Cryptpad.whenRealtimeSyncs(APP.lm.realtime, function () { Realtime.whenRealtimeSyncs(APP.lm.realtime, function () {
$ok.show(); $ok.show();
$spinner.hide(); $spinner.hide();
}); });
@ -386,14 +393,14 @@ define([
var onReady = function () { var onReady = function () {
APP.$container.find('#'+CREATE_ID).remove(); APP.$container.find('#'+CREATE_ID).remove();
var obj = APP.lm && APP.lm.proxy; /*var obj = APP.lm && APP.lm.proxy;
if (!APP.readOnly) { if (!APP.readOnly) {
var pubKeys = Cryptpad.getPublicKeys(); var pubKeys = Cryptpad.getPublicKeys();
if (pubKeys && pubKeys.curve) { if (pubKeys && pubKeys.curve) {
obj.curveKey = pubKeys.curve; obj.curveKey = pubKeys.curve;
obj.edKey = pubKeys.ed; obj.edKey = pubKeys.ed;
} }
} }*/
if (!APP.initialized) { if (!APP.initialized) {
var $header = $('<div>', {id: HEADER_ID}).appendTo(APP.$rightside); var $header = $('<div>', {id: HEADER_ID}).appendTo(APP.$rightside);
@ -408,14 +415,13 @@ define([
createLeftside(); createLeftside();
} }
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
}; };
var createToolbar = function () { var createToolbar = function () {
var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle']; var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle'];
var configTb = { var configTb = {
displayed: displayed, displayed: displayed,
common: Cryptpad,
sfCommon: common, sfCommon: common,
$container: APP.$toolbar, $container: APP.$toolbar,
pageTitle: Messages.profileButton, pageTitle: Messages.profileButton,
@ -426,7 +432,7 @@ define([
}; };
nThen(function (waitFor) { nThen(function (waitFor) {
$(waitFor(Cryptpad.addLoadingScreen)); $(waitFor(UI.addLoadingScreen));
SFCommon.create(waitFor(function (c) { APP.common = common = c; })); SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
APP.$container = $('#cp-sidebarlayout-container'); APP.$container = $('#cp-sidebarlayout-container');
@ -436,12 +442,6 @@ define([
sFrameChan = common.getSframeChannel(); sFrameChan = common.getSframeChannel();
sFrameChan.onReady(waitFor()); sFrameChan.onReady(waitFor());
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
onConnectError();
}
});
createToolbar(); createToolbar();
var metadataMgr = common.getMetadataMgr(); var metadataMgr = common.getMetadataMgr();
var privateData = metadataMgr.getPrivateData(); var privateData = metadataMgr.getPrivateData();
@ -451,7 +451,7 @@ define([
// If not logged in, you can only view other users's profile // If not logged in, you can only view other users's profile
if (!privateData.readOnly && !common.isLoggedIn()) { if (!privateData.readOnly && !common.isLoggedIn()) {
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
var $p = $('<p>', {id: CREATE_ID}).append(Messages.profile_register); var $p = $('<p>', {id: CREATE_ID}).append(Messages.profile_register);
var $a = $('<a>', { var $a = $('<a>', {

@ -4,7 +4,7 @@ define([
'/api/config', '/api/config',
'jquery', 'jquery',
'/common/requireconfig.js', '/common/requireconfig.js',
'/common/sframe-common-outer.js' '/common/sframe-common-outer.js',
], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) { ], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) {
var requireConfig = RequireConfig(); var requireConfig = RequireConfig();
@ -36,49 +36,50 @@ define([
}; };
window.addEventListener('message', onMsg); window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var getSecrets = function (Cryptpad) { var getSecrets = function (Cryptpad, Utils) {
var Hash = Utils.Hash;
// 1st case: visiting someone else's profile with hash in the URL // 1st case: visiting someone else's profile with hash in the URL
if (window.location.hash) { if (window.location.hash) {
return Cryptpad.getSecrets('profile', window.location.hash.slice(1)); return Hash.getSecrets('profile', window.location.hash.slice(1));
} }
// 2nd case: visiting our own existing profile // 2nd case: visiting our own existing profile
var obj = Cryptpad.getProxy(); var obj = Cryptpad.getProxy();
if (obj.profile && obj.profile.view && obj.profile.edit) { if (obj.profile && obj.profile.view && obj.profile.edit) {
return Cryptpad.getSecrets('profile', obj.profile.edit); return Hash.getSecrets('profile', obj.profile.edit);
} }
// 3rd case: profile creation (create a new random hash, store it later if needed) // 3rd case: profile creation (create a new random hash, store it later if needed)
if (!Cryptpad.isLoggedIn()) { return; } if (!Cryptpad.isLoggedIn()) { return; }
var hash = Cryptpad.createRandomHash(); var hash = Hash.createRandomHash();
var secret = Cryptpad.getSecrets('profile', hash); var secret = Hash.getSecrets('profile', hash);
Cryptpad.pinPads([secret.channel], function (e) { Cryptpad.pinPads([secret.channel], function (e) {
if (e) { if (e) {
if (e === 'E_OVER_LIMIT') { if (e === 'E_OVER_LIMIT') {
// TODO // TODO
} }
return; return;
//return void Cryptpad.log(Messages._getKey('profile_error', [e])) // TODO //return void UI.log(Messages._getKey('profile_error', [e])) // TODO
} }
obj.profile = {}; obj.profile = {};
obj.profile.edit = Cryptpad.getEditHashFromKeys(secret.channel, secret.keys); obj.profile.edit = Utils.Hash.getEditHashFromKeys(secret.channel, secret.keys);
obj.profile.view = Cryptpad.getViewHashFromKeys(secret.channel, secret.keys); obj.profile.view = Utils.Hash.getViewHashFromKeys(secret.channel, secret.keys);
}); });
return secret; return secret;
}; };
var addRpc = function (sframeChan, Cryptpad) { var addRpc = function (sframeChan, Cryptpad, Utils) {
// Adding a new avatar from the profile: pin it and store it in the object // Adding a new avatar from the profile: pin it and store it in the object
sframeChan.on('Q_PROFILE_AVATAR_ADD', function (data, cb) { sframeChan.on('Q_PROFILE_AVATAR_ADD', function (data, cb) {
var chanId = Cryptpad.hrefToHexChannelId(data); var chanId = Utils.Hash.hrefToHexChannelId(data);
Cryptpad.pinPads([chanId], function (e) { Cryptpad.pinPads([chanId], function (e) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
Cryptpad.getProxy().profile.avatar = data; Cryptpad.getProxy().profile.avatar = data;
Cryptpad.whenRealtimeSyncs(Cryptpad.getRealtime(), function () { Utils.Realtime.whenRealtimeSyncs(Cryptpad.getRealtime(), function () {
cb(); cb();
}); });
}); });
}); });
// Removing the avatar from the profile: unpin it // Removing the avatar from the profile: unpin it
sframeChan.on('Q_PROFILE_AVATAR_REMOVE', function (data, cb) { sframeChan.on('Q_PROFILE_AVATAR_REMOVE', function (data, cb) {
var chanId = Cryptpad.hrefToHexChannelId(data); var chanId = Utils.Hash.hrefToHexChannelId(data);
Cryptpad.unpinPads([chanId], function (e) { Cryptpad.unpinPads([chanId], function (e) {
delete Cryptpad.getProxy().profile.avatar; delete Cryptpad.getProxy().profile.avatar;
cb(e); cb(e);

@ -4,32 +4,17 @@ define([
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/test.js', '/common/test.js',
'/common/credential.js', // preloaded for login.js '/common/credential.js', // preloaded for login.js
'/common/common-interface.js',
'/common/common-util.js',
'/common/common-realtime.js',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
], function ($, Login, Cryptpad, Test, Cred) { ], function ($, Login, Cryptpad, Test, Cred, UI, Util, Realtime) {
var Messages = Cryptpad.Messages; var Messages = Cryptpad.Messages;
$(function () { $(function () {
var $main = $('#mainBlock'); var $main = $('#mainBlock');
// Language selector
var $sel = $('#language-selector');
Cryptpad.createLanguageSelector(undefined, $sel);
$sel.find('button').addClass('btn').addClass('btn-secondary');
$sel.show();
// User admin menu
var $userMenu = $('#user-menu');
var userMenuCfg = {
$initBlock: $userMenu
};
var $userAdmin = Cryptpad.createUserAdminMenu(userMenuCfg);
$userAdmin.find('button').addClass('btn').addClass('btn-secondary');
$(window).click(function () {
$('.cp-dropdown-content').hide();
});
// main block is hidden in case javascript is disabled // main block is hidden in case javascript is disabled
$main.removeClass('hidden'); $main.removeClass('hidden');
@ -82,7 +67,7 @@ define([
Cryptpad.feedback('REGISTRATION', true); Cryptpad.feedback('REGISTRATION', true);
Cryptpad.whenRealtimeSyncs(result.realtime, function () { Realtime.whenRealtimeSyncs(result.realtime, function () {
Cryptpad.login(result.userHash, result.userName, function () { Cryptpad.login(result.userHash, result.userName, function () {
registering = false; registering = false;
if (sessionStorage.redirectTo) { if (sessionStorage.redirectTo) {
@ -118,28 +103,28 @@ define([
var warning = Messages._getKey('register_passwordTooShort', [ var warning = Messages._getKey('register_passwordTooShort', [
Cred.MINIMUM_PASSWORD_LENGTH Cred.MINIMUM_PASSWORD_LENGTH
]); ]);
return void Cryptpad.alert(warning, function () { return void UI.alert(warning, function () {
registering = false; registering = false;
}); });
} }
if (passwd !== confirmPassword) { // do their passwords match? if (passwd !== confirmPassword) { // do their passwords match?
return void Cryptpad.alert(Messages.register_passwordsDontMatch); return void UI.alert(Messages.register_passwordsDontMatch);
} }
if (!doesAccept) { // do they accept the terms of service? if (!doesAccept) { // do they accept the terms of service?
return void Cryptpad.alert(Messages.register_mustAcceptTerms); return void UI.alert(Messages.register_mustAcceptTerms);
} }
setTimeout(function () { setTimeout(function () {
Cryptpad.confirm("<h2 class='bright msg'>" + Messages.register_warning + "</h2>", UI.confirm("<h2 class='bright msg'>" + Messages.register_warning + "</h2>",
function (yes) { function (yes) {
if (!yes) { return; } if (!yes) { return; }
registering = true; registering = true;
// setTimeout 100ms to remove the keyboard on mobile devices before the loading screen pops up // setTimeout 100ms to remove the keyboard on mobile devices before the loading screen pops up
window.setTimeout(function () { window.setTimeout(function () {
Cryptpad.addLoadingScreen({ UI.addLoadingScreen({
loadingText: Messages.login_hashing, loadingText: Messages.login_hashing,
hideTips: true, hideTips: true,
}); });
@ -152,40 +137,40 @@ define([
if (err) { if (err) {
switch (err) { switch (err) {
case 'NO_SUCH_USER': case 'NO_SUCH_USER':
Cryptpad.removeLoadingScreen(function () { UI.removeLoadingScreen(function () {
Cryptpad.alert(Messages.login_noSuchUser, function () { UI.alert(Messages.login_noSuchUser, function () {
registering = false; registering = false;
}); });
}); });
break; break;
case 'INVAL_USER': case 'INVAL_USER':
Cryptpad.removeLoadingScreen(function () { UI.removeLoadingScreen(function () {
Cryptpad.alert(Messages.login_invalUser, function () { UI.alert(Messages.login_invalUser, function () {
registering = false; registering = false;
}); });
}); });
break; break;
case 'INVAL_PASS': case 'INVAL_PASS':
Cryptpad.removeLoadingScreen(function () { UI.removeLoadingScreen(function () {
Cryptpad.alert(Messages.login_invalPass, function () { UI.alert(Messages.login_invalPass, function () {
registering = false; registering = false;
}); });
}); });
break; break;
case 'PASS_TOO_SHORT': case 'PASS_TOO_SHORT':
Cryptpad.removeLoadingScreen(function () { UI.removeLoadingScreen(function () {
var warning = Messages._getKey('register_passwordTooShort', [ var warning = Messages._getKey('register_passwordTooShort', [
Cred.MINIMUM_PASSWORD_LENGTH Cred.MINIMUM_PASSWORD_LENGTH
]); ]);
Cryptpad.alert(warning, function () { UI.alert(warning, function () {
registering = false; registering = false;
}); });
}); });
break; break;
case 'ALREADY_REGISTERED': case 'ALREADY_REGISTERED':
// logMeIn should reset registering = false // logMeIn should reset registering = false
Cryptpad.removeLoadingScreen(function () { UI.removeLoadingScreen(function () {
Cryptpad.confirm(Messages.register_alreadyRegistered, function (yes) { UI.confirm(Messages.register_alreadyRegistered, function (yes) {
if (!yes) { return; } if (!yes) { return; }
proxy.login_name = uname; proxy.login_name = uname;
@ -199,7 +184,7 @@ define([
break; break;
default: // UNHANDLED ERROR default: // UNHANDLED ERROR
registering = false; registering = false;
Cryptpad.errorLoadingScreen(Messages.login_unhandledError); UI.errorLoadingScreen(Messages.login_unhandledError);
} }
return; return;
} }
@ -232,7 +217,7 @@ define([
}, 150); }, 150);
}); });
var clickRegister = Cryptpad.notAgainForAnother(function () { var clickRegister = Util.notAgainForAnother(function () {
$register.click(); $register.click();
}, 500); }, 500);
@ -258,7 +243,7 @@ define([
$register.click(); $register.click();
window.setTimeout(function () { window.setTimeout(function () {
Cryptpad.findOKButton().click(); UI.findOKButton().click();
}, 1000); }, 1000);
}); });
}); });

@ -1,9 +1,12 @@
define([ define([
'jquery', 'jquery',
'/common/toolbar3.js', '/common/toolbar3.js',
'/common/cryptpad-common.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/common/common-interface.js',
'/common/common-util.js',
'/common/common-hash.js',
'/customize/messages.js',
'/bower_components/file-saver/FileSaver.min.js', '/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
@ -12,19 +15,16 @@ define([
], function ( ], function (
$, $,
Toolbar, Toolbar,
Cryptpad,
nThen, nThen,
SFCommon SFCommon,
UI,
Util,
Hash,
Messages
) )
{ {
var saveAs = window.saveAs; var saveAs = window.saveAs;
var Messages = Cryptpad.Messages; var APP = window.APP = {};
var APP = window.APP = {
Cryptpad: Cryptpad,
};
var onConnectError = function () {
Cryptpad.errorLoadingScreen(Messages.websocketError);
};
var common; var common;
var metadataMgr; var metadataMgr;
@ -68,10 +68,10 @@ define([
var publicKey = privateData.edPublic; var publicKey = privateData.edPublic;
if (publicKey) { if (publicKey) {
var $key = $('<div>', {'class': 'cp-sidebarlayout-element'}).appendTo($div); var $key = $('<div>', {'class': 'cp-sidebarlayout-element'}).appendTo($div);
var userHref = Cryptpad.getUserHrefFromKeys(accountName, publicKey); var userHref = Hash.getUserHrefFromKeys(privateData.origin, accountName, publicKey);
var $pubLabel = $('<span>', {'class': 'label'}) var $pubLabel = $('<span>', {'class': 'label'})
.text(Messages.settings_publicSigningKey); .text(Messages.settings_publicSigningKey);
$key.append($pubLabel).append(Cryptpad.dialog.selectable(userHref)); $key.append($pubLabel).append(UI.dialog.selectable(userHref));
} }
return $div; return $div;
@ -198,7 +198,7 @@ define([
localStore.put(k, undefined); localStore.put(k, undefined);
} }
}); });
Cryptpad.alert(Messages.settings_resetTipsDone); UI.alert(Messages.settings_resetTipsDone);
}); });
return $div; return $div;
@ -249,7 +249,7 @@ define([
$button.click(function () { $button.click(function () {
sframeChan.query("Q_THUMBNAIL_CLEAR", null, function (err) { sframeChan.query("Q_THUMBNAIL_CLEAR", null, function (err) {
if (err) { return void console.error("Cannot clear localForage"); } if (err) { return void console.error("Cannot clear localForage"); }
Cryptpad.alert(Messages.settings_resetThumbnailsDone); UI.alert(Messages.settings_resetThumbnailsDone);
}); });
}); });
@ -268,8 +268,8 @@ define([
var sjson = JSON.stringify(data); var sjson = JSON.stringify(data);
var name = displayName || accountName || Messages.anonymous; var name = displayName || accountName || Messages.anonymous;
var suggestion = name + '-' + new Date().toDateString(); var suggestion = name + '-' + new Date().toDateString();
Cryptpad.prompt(Cryptpad.Messages.exportPrompt, UI.prompt(Messages.exportPrompt,
Cryptpad.fixFileName(suggestion) + '.json', function (filename) { Util.fixFileName(suggestion) + '.json', function (filename) {
if (!(typeof(filename) === 'string' && filename)) { return; } if (!(typeof(filename) === 'string' && filename)) { return; }
var blob = new Blob([sjson], {type: "application/json;charset=utf-8"}); var blob = new Blob([sjson], {type: "application/json;charset=utf-8"});
saveAs(blob, filename); saveAs(blob, filename);
@ -314,14 +314,14 @@ define([
.text(Messages.settings_resetButton).appendTo($div); .text(Messages.settings_resetButton).appendTo($div);
$button.click(function () { $button.click(function () {
Cryptpad.prompt(Messages.settings_resetPrompt, "", function (val) { UI.prompt(Messages.settings_resetPrompt, "", function (val) {
if (val !== "I love CryptPad") { if (val !== "I love CryptPad") {
Cryptpad.alert(Messages.settings_resetError); UI.alert(Messages.settings_resetError);
return; return;
} }
sframeChan.query("Q_SETTINGS_DRIVE_RESET", null, function (err) { sframeChan.query("Q_SETTINGS_DRIVE_RESET", null, function (err) {
if (err) { return void console.error(err); } if (err) { return void console.error(err); }
Cryptpad.alert(Messages.settings_resetDone); UI.alert(Messages.settings_resetDone);
}); });
}, undefined, true); }, undefined, true);
}); });
@ -392,7 +392,7 @@ define([
$button.click(function () { $button.click(function () {
Cryptpad.confirm(Messages.settings_logoutEverywhereConfirm, function (yes) { UI.confirm(Messages.settings_logoutEverywhereConfirm, function (yes) {
if (!yes) { return; } if (!yes) { return; }
$spinner.show(); $spinner.show();
$ok.hide(); $ok.hide();
@ -424,14 +424,14 @@ define([
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div); var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
$button.click(function () { $button.click(function () {
Cryptpad.confirm(Messages.settings_importConfirm, function (yes) { UI.confirm(Messages.settings_importConfirm, function (yes) {
if (!yes) { return; } if (!yes) { return; }
$spinner.show(); $spinner.show();
$ok.hide(); $ok.hide();
sframeChan.query('Q_SETTINGS_IMPORT_LOCAL', null, function () { sframeChan.query('Q_SETTINGS_IMPORT_LOCAL', null, function () {
$spinner.hide(); $spinner.hide();
$ok.show(); $ok.show();
Cryptpad.alert(Messages.settings_importDone); UI.alert(Messages.settings_importDone);
}); });
}, undefined, true); }, undefined, true);
}); });
@ -488,7 +488,7 @@ define([
nThen(function (waitFor) { nThen(function (waitFor) {
$(waitFor(Cryptpad.addLoadingScreen)); $(waitFor(UI.addLoadingScreen));
SFCommon.create(waitFor(function (c) { APP.common = common = c; })); SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
APP.$container = $('#cp-sidebarlayout-container'); APP.$container = $('#cp-sidebarlayout-container');
@ -498,12 +498,6 @@ define([
sframeChan = common.getSframeChannel(); sframeChan = common.getSframeChannel();
sframeChan.onReady(waitFor()); sframeChan.onReady(waitFor());
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
onConnectError();
}
});
metadataMgr = common.getMetadataMgr(); metadataMgr = common.getMetadataMgr();
privateData = metadataMgr.getPrivateData(); privateData = metadataMgr.getPrivateData();
@ -511,7 +505,6 @@ define([
var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle']; var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle'];
var configTb = { var configTb = {
displayed: displayed, displayed: displayed,
common: Cryptpad,
sfCommon: common, sfCommon: common,
$container: APP.$toolbar, $container: APP.$toolbar,
pageTitle: Messages.settings_title, pageTitle: Messages.settings_title,
@ -546,6 +539,6 @@ define([
createLeftside(); createLeftside();
createUsageButton(); createUsageButton();
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
}); });
}); });

@ -47,7 +47,7 @@ define([
} }
}); });
*/ */
var addRpc = function (sframeChan, Cryptpad) { var addRpc = function (sframeChan, Cryptpad, Utils) {
sframeChan.on('Q_THUMBNAIL_CLEAR', function (d, cb) { sframeChan.on('Q_THUMBNAIL_CLEAR', function (d, cb) {
Cryptpad.clearThumbnail(function (err, data) { Cryptpad.clearThumbnail(function (err, data) {
cb({err:err, data:data}); cb({err:err, data:data});
@ -69,7 +69,7 @@ define([
var proxy = Cryptpad.getProxy(); var proxy = Cryptpad.getProxy();
var realtime = Cryptpad.getRealtime(); var realtime = Cryptpad.getRealtime();
proxy.drive = Cryptpad.getStore().getEmptyObject(); proxy.drive = Cryptpad.getStore().getEmptyObject();
Cryptpad.whenRealtimeSyncs(realtime, cb); Utils.Realtime.whenRealtimeSyncs(realtime, cb);
}); });
sframeChan.on('Q_SETTINGS_LOGOUT', function (data, cb) { sframeChan.on('Q_SETTINGS_LOGOUT', function (data, cb) {
var proxy = Cryptpad.getProxy(); var proxy = Cryptpad.getProxy();
@ -77,7 +77,7 @@ define([
var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER); var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
localStorage.setItem('loginToken', token); localStorage.setItem('loginToken', token);
proxy.loginToken = token; proxy.loginToken = token;
Cryptpad.whenRealtimeSyncs(realtime, cb); Utils.Realtime.whenRealtimeSyncs(realtime, cb);
}); });
sframeChan.on('Q_SETTINGS_IMPORT_LOCAL', function (data, cb) { sframeChan.on('Q_SETTINGS_IMPORT_LOCAL', function (data, cb) {
var proxyData = Cryptpad.getStore().getProxy(); var proxyData = Cryptpad.getStore().getProxy();

@ -1,528 +0,0 @@
define([
'jquery',
'/common/cryptpad-common.js',
'/common/hyperscript.js',
'/bower_components/marked/marked.min.js',
'/common/media-tag.js',
], function ($, Cryptpad, h, Marked, MediaTag) {
'use strict';
var UI = {};
var Messages = Cryptpad.Messages;
var m = function (md) {
var d = h('div.cp-app-contacts-content');
try {
d.innerHTML = Marked(md || '');
var $d = $(d);
// remove potentially malicious elements
$d.find('script, iframe, object, applet, video, audio').remove();
// activate media-tags
$d.find('media-tag').each(function (i, e) { MediaTag(e); });
} catch (e) {
console.error(md);
console.error(e);
}
return d;
};
var dataQuery = function (curvePublic) {
return '[data-key="' + curvePublic + '"]';
};
var initChannel = function (state, curvePublic, info) {
console.log('initializing channel for [%s]', curvePublic);
state.channels[curvePublic] = {
messages: [],
HEAD: info.lastKnownHash,
TAIL: null,
};
};
UI.create = function (messenger, $userlist, $messages, common) {
var origin = common.getMetadataMgr().getPrivateData().origin;
var state = window.state = {
active: '',
};
state.channels = {};
var displayNames = state.displayNames = {};
var avatars = state.avatars = {};
var setActive = function (curvePublic) {
state.active = curvePublic;
};
var isActive = function (curvePublic) {
return curvePublic === state.active;
};
var find = {};
find.inList = function (curvePublic) {
return $userlist.find(dataQuery(curvePublic));
};
var notify = function (curvePublic) {
find.inList(curvePublic).addClass('cp-app-contacts-notify');
};
var unnotify = function (curvePublic) {
find.inList(curvePublic).removeClass('cp-app-contacts-notify');
};
var markup = {};
markup.message = function (msg) {
var curvePublic = msg.author;
var name = displayNames[msg.author];
return h('div.cp-app-contacts-message', {
title: msg.time? new Date(msg.time).toLocaleString(): '?',
'data-key': curvePublic,
}, [
name? h('div.cp-app-contacts-sender', name): undefined,
m(msg.text),
]);
};
var getChat = function (curvePublic) {
return $messages.find(dataQuery(curvePublic));
};
var normalizeLabels = function ($messagebox) {
$messagebox.find('div.cp-app-contacts-message').toArray().reduce(function (a, b) {
var $b = $(b);
if ($(a).data('key') === $b.data('key')) {
$b.find('.cp-app-contacts-sender').hide();
return a;
}
return b;
}, []);
};
markup.chatbox = function (curvePublic, data) {
var moreHistory = h('span.cp-app-contacts-more-history.fa.fa-history', {
title: Messages.contacts_fetchHistory,
});
var displayName = data.displayName;
var fetching = false;
var $moreHistory = $(moreHistory).click(function () {
if (fetching) { return; }
// get oldest known message...
var channel = state.channels[curvePublic];
if (channel.exhausted) {
return void $moreHistory.addClass('cp-app-contacts-faded');
}
console.log('getting history');
var sig = channel.TAIL || channel.HEAD;
fetching = true;
var $messagebox = $(getChat(curvePublic)).find('.cp-app-contacts-messages');
messenger.getMoreHistory(curvePublic, sig, 10, function (e, history) {
fetching = false;
if (e) { return void console.error(e); }
if (history.length === 0) {
channel.exhausted = true;
return;
}
history.forEach(function (msg) {
if (channel.exhausted) { return; }
if (msg.sig) {
if (msg.sig === channel.TAIL) {
console.error('No more messages to fetch');
channel.exhausted = true;
console.log(channel);
return void $moreHistory.addClass('cp-app-contacts-faded');
} else {
channel.TAIL = msg.sig;
}
} else {
return void console.error('expected signature');
}
if (msg.type !== 'MSG') { return; }
// FIXME Schlameil the painter (performance does not scale well)
if (channel.messages.some(function (old) {
return msg.sig === old.sig;
})) { return; }
channel.messages.unshift(msg);
var el_message = markup.message(msg);
$messagebox.prepend(el_message);
});
normalizeLabels($messagebox);
});
});
var removeHistory = h('span.cp-app-contacts-remove-history.fa.fa-eraser', {
title: Messages.contacts_removeHistoryTitle
});
$(removeHistory).click(function () {
Cryptpad.confirm(Messages.contacts_confirmRemoveHistory, function (yes) {
if (!yes) { return; }
Cryptpad.clearOwnedChannel(data.channel, function (e) {
if (e) {
console.error(e);
Cryptpad.alert(Messages.contacts_removeHistoryServerError);
return;
}
});
});
});
var avatar = h('div.cp-avatar');
var header = h('div.cp-app-contacts-header', [
avatar,
moreHistory,
removeHistory,
]);
var messages = h('div.cp-app-contacts-messages');
var input = h('textarea', {
placeholder: Messages.contacts_typeHere
});
var sendButton = h('button.btn.btn-primary.fa.fa-paper-plane', {
title: Messages.contacts_send,
});
var rightCol = h('span.cp-app-contacts-right-col', [
h('span.cp-app-contacts-name', displayName),
]);
var $avatar = $(avatar);
if (data.avatar && avatars[data.avatar]) {
$avatar.append(avatars[data.avatar]).append(rightCol);
} else {
common.displayAvatar($avatar, data.avatar, data.displayName, function ($img) {
if (data.avatar && $img) {
avatars[data.avatar] = $img[0].outerHTML;
}
$(rightCol).insertAfter($avatar);
});
}
var sending = false;
var send = function (content) {
if (typeof(content) !== 'string' || !content.trim()) { return; }
if (sending) { return false; }
sending = true;
messenger.sendMessage(curvePublic, content, function (e) {
if (e) {
// failed to send
return void console.error('failed to send');
}
input.value = '';
sending = false;
console.log('sent successfully');
var $messagebox = $(messages);
var height = $messagebox[0].scrollHeight;
$messagebox.scrollTop(height);
});
};
var onKeyDown = function (e) {
// ignore anything that isn't 'enter'
if (e.keyCode !== 13) { return; }
// send unless they're holding a ctrl-key or shift
if (!e.ctrlKey && !e.shiftKey) {
send(this.value);
return false;
}
// insert a newline if they're holding either
var val = this.value;
var start = this.selectionState;
var end = this.selectionEnd;
if (![start,end].some(function (x) {
return typeof(x) !== 'number';
})) {
this.value = val.slice(0, start) + '\n' + val.slice(end);
this.selectionStart = this.selectionEnd = start + 1;
} else if (document.selection && document.selection.createRange) {
this.focus();
var range = document.selection.createRange();
range.text = '\r\n';
range.collapse(false);
range.select();
}
return false;
};
$(input).on('keydown', onKeyDown);
$(sendButton).click(function () { send(input.value); });
return h('div.cp-app-contacts-chat', {
'data-key': curvePublic,
}, [
header,
messages,
h('div.cp-app-contacts-input', [
input,
sendButton,
]),
]);
};
var hideInfo = function () {
$messages.find('.cp-app-contacts-info').hide();
};
var updateStatus = function (curvePublic) {
var $status = find.inList(curvePublic).find('.cp-app-contacts-status');
// FIXME this stopped working :(
messenger.getStatus(curvePublic, function (e, online) {
// if error maybe you shouldn't display this friend...
if (e) {
find.inList(curvePublic).hide();
getChat(curvePublic).hide();
return void console.error(curvePublic, e);
}
if (online) {
return void $status
.removeClass('cp-app-contacts-offline').addClass('cp-app-contacts-online');
}
$status.removeClass('cp-app-contacts-online').addClass('cp-app-contacts-offline');
});
};
var display = function (curvePublic) {
var channel = state.channels[curvePublic];
var lastMsg = channel.messages.slice(-1)[0];
if (lastMsg) {
channel.HEAD = lastMsg.sig;
messenger.setChannelHead(curvePublic, channel.HEAD, function (e) {
if (e) { console.error(e); }
});
}
setActive(curvePublic);
unnotify(curvePublic);
var $chat = getChat(curvePublic);
hideInfo();
$messages.find('div.cp-app-contacts-chat[data-key]').hide();
if ($chat.length) {
var $chat_messages = $chat.find('div.cp-app-contacts-message');
if (!$chat_messages.length) {
var $more = $chat.find('.cp-app-contacts-more-history');
$more.click();
}
return void $chat.show();
}
messenger.getFriendInfo(curvePublic, function (e, info) {
if (e) { return void console.error(e); } // FIXME
var chatbox = markup.chatbox(curvePublic, info);
$messages.append(chatbox);
});
};
var removeFriend = function (curvePublic) {
messenger.removeFriend(curvePublic, function (e /*, removed */) {
if (e) { return void console.error(e); }
find.inList(curvePublic).remove();
//console.log(removed);
});
};
markup.friend = function (data) {
var curvePublic = data.curvePublic;
var friend = h('div.cp-app-contacts-friend.cp-avatar', {
'data-key': curvePublic,
});
var remove = h('span.cp-app-contacts-remove.fa.fa-user-times', {
title: Messages.contacts_remove
});
var status = h('span.cp-app-contacts-status');
var rightCol = h('span.cp-app-contacts-right-col', [
h('span.cp-app-contacts-name', [data.displayName]),
remove,
]);
var $friend = $(friend)
.click(function () {
display(curvePublic);
})
.dblclick(function () {
if (data.profile) { window.open(origin + '/profile/#' + data.profile); }
});
$(remove).click(function (e) {
e.stopPropagation();
Cryptpad.confirm(Messages._getKey('contacts_confirmRemove', [
Cryptpad.fixHTML(data.displayName)
]), function (yes) {
if (!yes) { return; }
removeFriend(curvePublic, function (e) {
if (e) { return void console.error(e); }
});
// TODO remove friend from userlist ui
// FIXME seems to trigger EJOINED from netflux-websocket (from server);
// (tried to join a channel in which you were already present)
}, undefined, true);
});
if (data.avatar && avatars[data.avatar]) {
$friend.append(avatars[data.avatar]);
$friend.append(rightCol);
} else {
common.displayAvatar($friend, data.avatar, data.displayName, function ($img) {
if (data.avatar && $img) {
avatars[data.avatar] = $img[0].outerHTML;
}
$friend.append(rightCol);
});
}
$friend.append(status);
return $friend;
};
var isBottomedOut = function ($elem) {
return ($elem[0].scrollHeight - $elem.scrollTop() === $elem.outerHeight());
};
var initializing = true;
messenger.on('message', function (message) {
if (!initializing) { Cryptpad.notify(); }
var curvePublic = message.curve;
var name = displayNames[curvePublic];
var chat = getChat(curvePublic, name);
console.log(message);
var el_message = markup.message(message);
state.channels[curvePublic].messages.push(message);
var $chat = $(chat);
if (!$chat.length) {
console.error("Got a message but the chat isn't open");
}
var $messagebox = $chat.find('.cp-app-contacts-messages');
var shouldScroll = isBottomedOut($messagebox);
$messagebox.append(el_message);
if (shouldScroll) {
$messagebox.scrollTop($messagebox.outerHeight());
}
normalizeLabels($messagebox);
var channel = state.channels[curvePublic];
if (!channel) {
console.error('expected channel [%s] to be open', curvePublic);
return;
}
if (isActive(curvePublic)) {
channel.HEAD = message.sig;
messenger.setChannelHead(curvePublic, message.sig, function (e) {
if (e) { return void console.error(e); }
});
return;
}
var lastMsg = channel.messages.slice(-1)[0];
if (lastMsg.sig !== channel.HEAD) {
return void notify(curvePublic);
}
unnotify(curvePublic);
});
messenger.on('join', function (curvePublic, channel) {
channel = channel;
updateStatus(curvePublic);
});
messenger.on('leave', function (curvePublic, channel) {
channel = channel;
updateStatus(curvePublic);
});
// change in your friend list
messenger.on('update', function (info, curvePublic) {
var name = displayNames[curvePublic] = info.displayName;
// update label in friend list
find.inList(curvePublic).find('.cp-app-contacts-name').text(name);
// update title bar and messages
$messages.find(dataQuery(curvePublic) + ' .cp-app-contacts-header ' +
'.cp-app-contacts-name, div.cp-app-contacts-message'+
dataQuery(curvePublic) + ' div.cp-app-contacts-sender').text(name).text(name);
});
var connectToFriend = function (curvePublic, cb) {
messenger.getFriendInfo(curvePublic, function (e, info) {
if (e) { return void console.error(e); }
var name = displayNames[curvePublic] = info.displayName;
initChannel(state, curvePublic, info);
var chatbox = markup.chatbox(curvePublic, info);
$(chatbox).hide();
$messages.append(chatbox);
var friend = markup.friend(info, name);
$userlist.append(friend);
messenger.openFriendChannel(curvePublic, function (e) {
if (e) { return void console.error(e); }
cb();
updateStatus(curvePublic);
// don't add friends that are already in your userlist
//if (friendExistsInUserList(k)) { return; }
});
});
};
messenger.on('friend', function (curvePublic) {
console.log('new friend: ', curvePublic);
//console.error("TODO redraw user list");
//console.error("TODO connect to new friend");
// FIXME this doesn't work right now because the friend hasn't been fully added?
connectToFriend(curvePublic, function () {
//console.error('connected');
});
});
messenger.on('unfriend', function (curvePublic) {
console.log('unfriend', curvePublic);
find.inList(curvePublic).remove();
console.error('TODO remove chatbox');
console.error('TODO show something if that chatbox was active');
});
Cryptpad.onDisplayNameChanged(function () {
//messenger.checkNewFriends();
messenger.updateMyData();
});
// FIXME dirty hack
messenger.getMyInfo(function (e, info) {
displayNames[info.curvePublic] = info.displayName;
});
messenger.getFriendList(function (e, keys) {
var count = keys.length + 1;
var ready = function () {
count--;
if (count === 0) {
initializing = false;
Cryptpad.removeLoadingScreen();
}
};
ready();
keys.forEach(function (curvePublic) {
connectToFriend(curvePublic, ready);
});
});
};
return UI;
});

@ -1,12 +1,14 @@
define([ define([
'jquery', 'jquery',
'json.sortify', 'json.sortify',
'/common/cryptpad-common.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/slide/slide.js', '/slide/slide.js',
'/common/sframe-app-framework.js', '/common/sframe-app-framework.js',
'/common/common-util.js', '/common/common-util.js',
'/common/common-hash.js',
'/common/common-interface.js',
'/customize/messages.js',
'cm/lib/codemirror', 'cm/lib/codemirror',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
@ -42,16 +44,17 @@ define([
], function ( ], function (
$, $,
JSONSortify, JSONSortify,
Cryptpad,
nThen, nThen,
SFCommon, SFCommon,
Slide, Slide,
Framework, Framework,
Util, Util,
Hash,
UI,
Messages,
CMeditor) CMeditor)
{ {
window.CodeMirror = CMeditor; window.CodeMirror = CMeditor;
var Messages = Cryptpad.Messages;
var SLIDE_BACKCOLOR_ID = "cp-app-slide-toolbar-backcolor"; var SLIDE_BACKCOLOR_ID = "cp-app-slide-toolbar-backcolor";
var SLIDE_COLOR_ID = "cp-app-slide-toolbar-color"; var SLIDE_COLOR_ID = "cp-app-slide-toolbar-color";
@ -104,7 +107,7 @@ define([
Slide.update(editor.getValue(), true); Slide.update(editor.getValue(), true);
$print.html($content.html()); $print.html($content.html());
// TODO use translation key // TODO use translation key
Cryptpad.confirm("Are you sure you want to print?", function (yes) { UI.confirm("Are you sure you want to print?", function (yes) {
if (yes) { if (yes) {
window.focus(); window.focus();
window.print(); window.print();
@ -224,7 +227,7 @@ define([
slideOptionsTmp.style = ''; slideOptionsTmp.style = '';
parseLess(mkLess(less), function (err, css) { parseLess(mkLess(less), function (err, css) {
if (err) { if (err) {
Cryptpad.alert( UI.alert(
'<strong>' + Messages.slide_invalidLess + '</strong>' + '<strong>' + Messages.slide_invalidLess + '</strong>' +
'<br>' + '<br>' +
'<pre class="cp-slide-css-error">' + Util.fixHTML( '<pre class="cp-slide-css-error">' + Util.fixHTML(
@ -245,14 +248,14 @@ define([
updateLocalOptions(slideOptionsTmp); updateLocalOptions(slideOptionsTmp);
} }
$container.remove(); $container.remove();
Cryptpad.stopListening(h); UI.stopListening(h);
}; };
var todoCancel = function () { var todoCancel = function () {
$container.remove(); $container.remove();
Cryptpad.stopListening(h); UI.stopListening(h);
}; };
h = Cryptpad.listenForKeys(todo, todoCancel); h = UI.listenForKeys(todo, todoCancel);
var $nav = $('<nav>').appendTo($div); var $nav = $('<nav>').appendTo($div);
$('<button>', {'class': 'cancel'}).text(Messages.cancelButton).appendTo($nav).click(todoCancel); $('<button>', {'class': 'cancel'}).text(Messages.cancelButton).appendTo($nav).click(todoCancel);
@ -436,8 +439,8 @@ define([
//var cursor = editor.getCursor(); //var cursor = editor.getCursor();
//var cleanName = data.name.replace(/[\[\]]/g, ''); //var cleanName = data.name.replace(/[\[\]]/g, '');
//var text = '!['+cleanName+']('+data.url+')'; //var text = '!['+cleanName+']('+data.url+')';
var parsed = Cryptpad.parsePadUrl(data.url); var parsed = Hash.parsePadUrl(data.url);
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel); var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>'; var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>';
editor.replaceSelection(mt); editor.replaceSelection(mt);

@ -3,10 +3,12 @@ define([
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/common/sframe-chainpad-listmap.js', '/common/sframe-chainpad-listmap.js',
'/common/toolbar3.js', '/common/toolbar3.js',
'/common/cryptpad-common.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/common/common-interface.js',
'/common/common-hash.js',
'/todo/todo.js', '/todo/todo.js',
'/customize/messages.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
@ -16,38 +18,30 @@ define([
Crypto, Crypto,
Listmap, Listmap,
Toolbar, Toolbar,
Cryptpad,
nThen, nThen,
SFCommon, SFCommon,
Todo UI,
Hash,
Todo,
Messages
) )
{ {
var Messages = Cryptpad.Messages;
var APP = window.APP = {}; var APP = window.APP = {};
var onConnectError = function () {
Cryptpad.errorLoadingScreen(Messages.websocketError);
};
var common; var common;
var sFrameChan; var sFrameChan;
nThen(function (waitFor) { nThen(function (waitFor) {
$(waitFor(Cryptpad.addLoadingScreen)); $(waitFor(UI.addLoadingScreen));
SFCommon.create(waitFor(function (c) { APP.common = common = c; })); SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
sFrameChan = common.getSframeChannel(); sFrameChan = common.getSframeChannel();
sFrameChan.onReady(waitFor()); sFrameChan.onReady(waitFor());
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
onConnectError();
}
});
var $body = $('body'); var $body = $('body');
var $list = $('#cp-app-todo-taskslist'); var $list = $('#cp-app-todo-taskslist');
var removeTips = function () { var removeTips = function () {
Cryptpad.clearTooltips(); UI.clearTooltips();
}; };
var onReady = function () { var onReady = function () {
@ -163,7 +157,7 @@ define([
"mtime": +new Date() "mtime": +new Date()
}; };
var id = Cryptpad.createChannelId(); var id = Hash.createChannelId();
todo.add(id, obj); todo.add(id, obj);
$input.val(""); $input.val("");
@ -188,11 +182,11 @@ define([
editTask = editTask; editTask = editTask;
display(); display();
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
}; };
var onInit = function () { var onInit = function () {
Cryptpad.addLoadingScreen(); UI.addLoadingScreen();
$body.on('dragover', function (e) { e.preventDefault(); }); $body.on('dragover', function (e) { e.preventDefault(); });
$body.on('drop', function (e) { e.preventDefault(); }); $body.on('drop', function (e) { e.preventDefault(); });
@ -202,7 +196,6 @@ define([
var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle']; var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle'];
var configTb = { var configTb = {
displayed: displayed, displayed: displayed,
common: Cryptpad,
sfCommon: common, sfCommon: common,
$container: $bar, $container: $bar,
pageTitle: Messages.todo_title, pageTitle: Messages.todo_title,

@ -36,10 +36,10 @@ define([
}; };
window.addEventListener('message', onMsg); window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var getSecrets = function (Cryptpad) { var getSecrets = function (Cryptpad, Utils) {
var proxy = Cryptpad.getProxy(); var proxy = Cryptpad.getProxy();
var hash = proxy.todo || Cryptpad.createRandomHash(); var hash = proxy.todo || Utils.Hash.createRandomHash();
return Cryptpad.getSecrets('todo', hash); return Utils.Hash.getSecrets('todo', hash);
}; };
SFCommonO.start({ SFCommonO.start({
getSecrets: getSecrets, getSecrets: getSecrets,

@ -26,23 +26,6 @@ define([
$(function () { $(function () {
var $main = $('#mainBlock'); var $main = $('#mainBlock');
// Language selector
var $sel = $('#language-selector');
Cryptpad.createLanguageSelector(undefined, $sel);
$sel.find('button').addClass('btn').addClass('btn-secondary');
$sel.show();
// User admin menu
var $userMenu = $('#user-menu');
var userMenuCfg = {
$initBlock: $userMenu
};
var $userAdmin = Cryptpad.createUserAdminMenu(userMenuCfg);
$userAdmin.find('button').addClass('btn').addClass('btn-secondary');
$(window).click(function () {
$('.cp-dropdown-content').hide();
});
// main block is hidden in case javascript is disabled // main block is hidden in case javascript is disabled
$main.removeClass('hidden'); $main.removeClass('hidden');

@ -5,16 +5,14 @@ define([
'/common/toolbar3.js', '/common/toolbar3.js',
'json.sortify', 'json.sortify',
'/bower_components/chainpad-json-validator/json-ot.js', '/bower_components/chainpad-json-validator/json-ot.js',
'/common/cryptpad-common.js',
'/common/common-util.js', '/common/common-util.js',
'/common/cryptget.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/common/sframe-common-interface.js', '/common/common-interface.js',
'/api/config', '/api/config',
'/common/common-realtime.js', '/common/common-realtime.js',
'/customize/pages.js', '/customize/pages.js',
'/customize/messages.js',
'/customize/application_config.js', '/customize/application_config.js',
'/common/common-thumbnail.js', '/common/common-thumbnail.js',
'/whiteboard/colors.js', '/whiteboard/colors.js',
@ -32,24 +30,21 @@ define([
Toolbar, Toolbar,
JSONSortify, JSONSortify,
JsonOT, JsonOT,
Cryptpad,
Util, Util,
Cryptget,
nThen, nThen,
SFCommon, SFCommon,
SFUI, UI,
ApiConfig, ApiConfig,
CommonRealtime, CommonRealtime,
Pages, Pages,
Messages,
AppConfig, AppConfig,
Thumb, Thumb,
Colors) Colors)
{ {
var saveAs = window.saveAs; var saveAs = window.saveAs;
var Messages = Cryptpad.Messages;
var APP = window.APP = { var APP = window.APP = {
Cryptpad: Cryptpad,
$: $ $: $
}; };
var Fabric = APP.Fabric = window.fabric; var Fabric = APP.Fabric = window.fabric;
@ -60,10 +55,6 @@ define([
var toolbar; var toolbar;
var onConnectError = function () {
Cryptpad.errorLoadingScreen(Messages.websocketError);
};
var andThen = function (common) { var andThen = function (common) {
var config = {}; var config = {};
/* Initialize Fabric */ /* Initialize Fabric */
@ -129,7 +120,7 @@ define([
var updateBrushWidth = function () { var updateBrushWidth = function () {
var val = $width.val(); var val = $width.val();
canvas.freeDrawingBrush.width = Number(val); canvas.freeDrawingBrush.width = Number(val);
$widthLabel.text(Cryptpad.Messages._getKey("canvas_widthLabel", [val])); $widthLabel.text(Messages._getKey("canvas_widthLabel", [val]));
$('#cp-app-whiteboard-width-val').text(val + 'px'); $('#cp-app-whiteboard-width-val').text(val + 'px');
createCursor(); createCursor();
}; };
@ -140,7 +131,7 @@ define([
var val = $opacity.val(); var val = $opacity.val();
brush.opacity = Number(val); brush.opacity = Number(val);
canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity); canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity);
$opacityLabel.text(Cryptpad.Messages._getKey("canvas_opacityLabel", [val])); $opacityLabel.text(Messages._getKey("canvas_opacityLabel", [val]));
$('#cp-app-whiteboard-opacity-val').text((Number(val) * 100) + '%'); $('#cp-app-whiteboard-opacity-val').text((Number(val) * 100) + '%');
createCursor(); createCursor();
}; };
@ -228,7 +219,7 @@ define([
var saveImage = APP.saveImage = function () { var saveImage = APP.saveImage = function () {
var defaultName = "pretty-picture.png"; var defaultName = "pretty-picture.png";
Cryptpad.prompt(Messages.exportPrompt, defaultName, function (filename) { UI.prompt(Messages.exportPrompt, defaultName, function (filename) {
if (!(typeof(filename) === 'string' && filename)) { return; } if (!(typeof(filename) === 'string' && filename)) { return; }
$canvas[0].toBlob(function (blob) { $canvas[0].toBlob(function (blob) {
saveAs(blob, filename); saveAs(blob, filename);
@ -409,7 +400,6 @@ define([
metadataMgr: metadataMgr, metadataMgr: metadataMgr,
readOnly: readOnly, readOnly: readOnly,
realtime: info.realtime, realtime: info.realtime,
common: Cryptpad,
sfCommon: common, sfCommon: common,
$container: $bar, $container: $bar,
$contentContainer: $('#cp-app-whiteboard-canvas-area') $contentContainer: $('#cp-app-whiteboard-canvas-area')
@ -436,7 +426,7 @@ define([
if (common.isLoggedIn()) { if (common.isLoggedIn()) {
common.createButton('savetodrive', true, {}, function () {}) common.createButton('savetodrive', true, {}, function () {})
.click(function () { .click(function () {
Cryptpad.prompt(Messages.exportPrompt, document.title + '.png', UI.prompt(Messages.exportPrompt, document.title + '.png',
function (name) { function (name) {
if (name === null || !name.trim()) { return; } if (name === null || !name.trim()) { return; }
APP.upload(name); APP.upload(name);
@ -537,12 +527,12 @@ define([
(hjson.metadata && typeof(hjson.metadata.type) !== 'undefined' && (hjson.metadata && typeof(hjson.metadata.type) !== 'undefined' &&
hjson.metadata.type !== 'whiteboard')) { hjson.metadata.type !== 'whiteboard')) {
var errorText = Messages.typeError; var errorText = Messages.typeError;
Cryptpad.errorLoadingScreen(errorText); UI.errorLoadingScreen(errorText);
throw new Error(errorText); throw new Error(errorText);
} }
newDoc = hjson.content; newDoc = hjson.content;
} else { } else {
Title.updateTitle(Cryptpad.initialName || Title.defaultTitle); Title.updateTitle(Title.defaultTitle);
} }
nThen(function (waitFor) { nThen(function (waitFor) {
@ -556,7 +546,7 @@ define([
setEditable(!readOnly); setEditable(!readOnly);
initializing = false; initializing = false;
config.onLocal(); config.onLocal();
Cryptpad.removeLoadingScreen(); UI.removeLoadingScreen();
initThumbnails(); initThumbnails();
@ -592,27 +582,25 @@ define([
// inform of network disconnect // inform of network disconnect
setEditable(false); setEditable(false);
toolbar.failed(); toolbar.failed();
Cryptpad.alert(Messages.common_connectionLost, undefined, true); UI.alert(Messages.common_connectionLost, undefined, true);
}; };
config.onConnectionChange = function (info) { config.onConnectionChange = function (info) {
setEditable(info.state); setEditable(info.state);
if (info.state) { if (info.state) {
initializing = true; initializing = true;
Cryptpad.findOKButton().click(); UI.findOKButton().click();
} else { } else {
Cryptpad.alert(Messages.common_connectionLost, undefined, true); UI.alert(Messages.common_connectionLost, undefined, true);
} }
}; };
config.onError = onConnectError;
cpNfInner = common.startRealtime(config); cpNfInner = common.startRealtime(config);
metadataMgr = cpNfInner.metadataMgr; metadataMgr = cpNfInner.metadataMgr;
cpNfInner.onInfiniteSpinner(function () { cpNfInner.onInfiniteSpinner(function () {
setEditable(false); setEditable(false);
Cryptpad.confirm(Messages.realtime_unrecoverableError, function (yes) { UI.confirm(Messages.realtime_unrecoverableError, function (yes) {
if (!yes) { return; } if (!yes) { return; }
common.gotoURL(); common.gotoURL();
}); });
@ -629,7 +617,7 @@ define([
saveImage(); saveImage();
}); });
Cryptpad.onLogout(function () { setEditable(false); }); common.onLogout(function () { setEditable(false); });
}; };
var main = function () { var main = function () {
@ -637,17 +625,12 @@ define([
nThen(function (waitFor) { nThen(function (waitFor) {
$(waitFor(function () { $(waitFor(function () {
Cryptpad.addLoadingScreen(); UI.addLoadingScreen();
var $div = $('<div>').append(Pages['/whiteboard/']()); var $div = $('<div>').append(Pages['/whiteboard/']());
$('body').append($div.html()); $('body').append($div.html());
})); }));
SFCommon.create(waitFor(function (c) { APP.common = common = c; })); SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
onConnectError();
}
});
andThen(common); andThen(common);
}); });
}; };

Loading…
Cancel
Save