merge changes from soon

pull/1/head
ansuz 8 years ago
commit 8064b1d196

@ -12,10 +12,6 @@
<p data-localization="bottom_france"> <p data-localization="bottom_france">
</p> </p>
</div> </div>
<!--
<div class="bottom-bar-center">
<p><a href="https://github.com/xwiki-labs/cryptpad">Fork me on GitHub</a></p>
</div> -->
<div class="bottom-bar-right"> <div class="bottom-bar-right">
<p data-localization="bottom_support"> <p data-localization="bottom_support">
</p> </p>

@ -10,6 +10,11 @@ define([
var main = function () { var main = function () {
var url = window.location.pathname; var url = window.location.pathname;
var isHtml = /\.html/.test(url) || url === '/' || url === ''; var isHtml = /\.html/.test(url) || url === '/' || url === '';
var isPoll = /\/poll\//.test(url);
if (!isHtml && !isPoll) {
Messages._applyTranslation();
return;
}
$.ajax({ $.ajax({
url: isHtml ? '/customize/BottomBar.html' : '/customize/Header.html', url: isHtml ? '/customize/BottomBar.html' : '/customize/Header.html',
success: function (ret) { success: function (ret) {

@ -17,6 +17,7 @@
</div> </div>
<div class="bottom-bar-right"> <div class="bottom-bar-right">
<p data-localization="header_xwiki" class="small"> <p data-localization="header_xwiki" class="small">
<a href="http://www.xwiki.com/" target="_blank" rel="noopener noreferrer"><img src="/customize/logo-xwiki.png" alt="XWiki SAS" class="bottom-bar-xwiki"/></a>
<p data-localization="header_support" class="big"> <p data-localization="header_support" class="big">
</p> </p>
</div> </div>

@ -40,7 +40,7 @@
<div id="main"> <div id="main">
<center> <center>
<img class="imgcenter cryptofist" src="/customize/cryptofist_small.png" /> <img class="imgcenter cryptofist" src="/customize/cryptofist_small.png" />
<h1>Unity is Strength - Collaboration is Key</h1> <h1 data-localization="main_slogan">Unity is Strength - Collaboration is Key</h1>
</center> </center>
<p data-localization="main_p1"><!-- Zero Knowledge collaborative realtime editor. Protected from the NSA. --></p> <p data-localization="main_p1"><!-- Zero Knowledge collaborative realtime editor. Protected from the NSA. --></p>

@ -20,8 +20,8 @@ define(['/bower_components/jquery/dist/jquery.min.js'], function() {
return getStoredLanguage() || getBrowserLanguage(); return getStoredLanguage() || getBrowserLanguage();
}; };
var main = out.main = function () { var main = out.main = function ($select) {
var selector = $('#language-selector'); var selector = $select || $('#language-selector');
if (!selector.length) { return; } if (!selector.length) { return; }
// Select the current language in the list // Select the current language in the list

@ -24,6 +24,16 @@ define(['/customize/languageSelector.js',
messages = $.extend(true, {}, Default, map[language]); messages = $.extend(true, {}, Default, map[language]);
} }
// messages_languages return the available translations and their name in an object :
// { "en": "English", "fr": "French", ... }
messages._languages = {
'en': Default._languageName
};
for (var l in map) {
messages._languages[l] = map[l]._languageName || l;
}
messages._initSelector = LS.main;
messages._checkTranslationState = function () { messages._checkTranslationState = function () {
var missing = []; var missing = [];
Object.keys(map).forEach(function (code) { Object.keys(map).forEach(function (code) {
@ -35,6 +45,10 @@ define(['/customize/languageSelector.js',
missing.push(warning); missing.push(warning);
} }
}); });
if (typeof(translation._languageName) !== 'string') {
var warning = 'key [_languageName] is missing from translation [' + code + ']';
missing.push(warning);
}
}); });
return missing; return missing;
}; };

@ -14,8 +14,8 @@
.unselectable; .unselectable;
color: #666; font: normal normal normal 12px Arial,Helvetica,Tahoma,Verdana,Sans-Serif;
font-weight: bold; color: #000;
display: inline-block; display: inline-block;
width: 100%; width: 100%;
z-index: 9001; z-index: 9001;
@ -33,13 +33,13 @@
color: #000; color: #000;
} }
&.cryptpad-lag { &.cryptpad-lag {
float: right; float: left;
line-height: 26px; line-height: 26px;
margin: 2px 0px 2px 4px; margin: 2px 0px;
} }
} }
button, .rightside-element { button, select, .rightside-element {
height: 26px; height: 26px;
padding-right: 5px; padding-right: 5px;
padding-left: 5px; padding-left: 5px;
@ -105,9 +105,128 @@
} }
} }
.cryptpad-toolbar-top {
display: block;
text-align: center;
height: 32px;
position: relative;
@media screen and (max-width: 400px) {
height: 67px;
}
.cryptpad-title {
.title, .pencilIcon {
font-size: 1.5em;
vertical-align: middle;
line-height: 32px;
}
.pencilIcon {
display: none;
&:hover {
color: #999;
}
span {
cursor: pointer;
}
}
&:not(input):hover {
.editable {
border: 1px solid #888;
border-radius: 2px 0px 0px 2px;
background: white;
padding: 5px;
border-collapse: collapse;
}
.pencilIcon {
cursor: pointer;
border: 1px solid #888;
border-radius: 0px 2px 2px 0px;
background: white;
padding: 5px;
display: inline;
margin-left: -1px;
border-collapse: collapse;
}
}
input {
font-size: 1.5em;
vertical-align: middle;
height: 100%;
box-sizing: border-box;
border: 1px solid black;
background: #fff;
cursor: auto;
width: 300px;
padding: 0px 5px;
}
}
.cryptpad-link {
position: absolute;
left: 0px;
height: 32px;
@media screen and (max-width: 400px) {
top: 35px;
}
@media screen and (min-width: 401px) {
top: 0px;
}
a.cryptpad-logo {
cursor: pointer;
height: 32px;
padding: 0px 5px;
text-decoration: none;
color: inherit;
&:hover {
span {
text-decoration: underline;
}
}
img {
vertical-align: middle;
height: 32px;
cursor: pointer;
}
span {
font-size: 1.5em;
margin-left: 5px;
vertical-align: middle;
cursor: pointer;
}
}
.big {
@media screen and (max-width: 400px) {
display: none;
}
@media screen and (min-width: 401px) {
display: inline-block;
}
}
.small {
@media screen and (max-width: 400px) {
display: inline-block;
}
@media screen and (min-width: 401px) {
display: none;
}
}
}
.cryptpad-user {
position: absolute;
right: 0;
@media screen and (max-width: 400px) {
top: 3em;
}
@media screen and (min-width: 401px) {
top: 0px;
}
}
}
.cryptpad-toolbar-leftside { .cryptpad-toolbar-leftside {
float: left; float: left;
margin-bottom: -1px; margin-bottom: -1px;
.cryptpad-user-list {
float: right;
}
.cryptpad-dropdown-container { .cryptpad-dropdown-container {
position: relative; position: relative;
display: inline-block; display: inline-block;
@ -130,10 +249,7 @@
white-space: normal; white-space: normal;
&.cryptpad-dropdown-users { &.cryptpad-dropdown-users {
text-align:baseline; text-align:baseline;
.yourself { .yourself, .anonymous, .viewer {
font-style: italic;
}
.anonymous {
font-style: italic; font-style: italic;
} }
} }
@ -160,7 +276,6 @@
} }
.cryptpad-toolbar-rightside { .cryptpad-toolbar-rightside {
text-align: right; text-align: right;
margin-right: 30px;
//float: right; //float: right;
} }
.cryptpad-spinner { .cryptpad-spinner {
@ -172,7 +287,7 @@
font-size: 20px; font-size: 20px;
} }
.cryptpad-readonly { .cryptpad-readonly {
margin-right: 20px; margin-right: 5px;
font-weight: bold; font-weight: bold;
text-transform: uppercase; text-transform: uppercase;
} }

@ -14,8 +14,8 @@
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
color: #666; font: normal normal normal 12px Arial, Helvetica, Tahoma, Verdana, Sans-Serif;
font-weight: bold; color: #000;
display: inline-block; display: inline-block;
width: 100%; width: 100%;
z-index: 9001; z-index: 9001;
@ -33,11 +33,12 @@
color: #000; color: #000;
} }
.cryptpad-toolbar div.cryptpad-lag { .cryptpad-toolbar div.cryptpad-lag {
float: right; float: left;
line-height: 26px; line-height: 26px;
margin: 2px 0px 2px 4px; margin: 2px 0px;
} }
.cryptpad-toolbar button, .cryptpad-toolbar button,
.cryptpad-toolbar select,
.cryptpad-toolbar .rightside-element { .cryptpad-toolbar .rightside-element {
height: 26px; height: 26px;
padding-right: 5px; padding-right: 5px;
@ -101,10 +102,137 @@
border: 1px solid #A6A6A6; border: 1px solid #A6A6A6;
border-bottom-color: #979797; border-bottom-color: #979797;
} }
.cryptpad-toolbar-top {
display: block;
text-align: center;
height: 32px;
position: relative;
}
@media screen and (max-width: 400px) {
.cryptpad-toolbar-top {
height: 67px;
}
}
.cryptpad-toolbar-top .cryptpad-title .title,
.cryptpad-toolbar-top .cryptpad-title .pencilIcon {
font-size: 1.5em;
vertical-align: middle;
line-height: 32px;
}
.cryptpad-toolbar-top .cryptpad-title .pencilIcon {
display: none;
}
.cryptpad-toolbar-top .cryptpad-title .pencilIcon:hover {
color: #999;
}
.cryptpad-toolbar-top .cryptpad-title .pencilIcon span {
cursor: pointer;
}
.cryptpad-toolbar-top .cryptpad-title:not(input):hover .editable {
border: 1px solid #888;
border-radius: 2px 0px 0px 2px;
background: white;
padding: 5px;
border-collapse: collapse;
}
.cryptpad-toolbar-top .cryptpad-title:not(input):hover .pencilIcon {
cursor: pointer;
border: 1px solid #888;
border-radius: 0px 2px 2px 0px;
background: white;
padding: 5px;
display: inline;
margin-left: -1px;
border-collapse: collapse;
}
.cryptpad-toolbar-top .cryptpad-title input {
font-size: 1.5em;
vertical-align: middle;
height: 100%;
box-sizing: border-box;
border: 1px solid black;
background: #fff;
cursor: auto;
width: 300px;
padding: 0px 5px;
}
.cryptpad-toolbar-top .cryptpad-link {
position: absolute;
left: 0px;
height: 32px;
}
@media screen and (max-width: 400px) {
.cryptpad-toolbar-top .cryptpad-link {
top: 35px;
}
}
@media screen and (min-width: 401px) {
.cryptpad-toolbar-top .cryptpad-link {
top: 0px;
}
}
.cryptpad-toolbar-top .cryptpad-link a.cryptpad-logo {
cursor: pointer;
height: 32px;
padding: 0px 5px;
text-decoration: none;
color: inherit;
}
.cryptpad-toolbar-top .cryptpad-link a.cryptpad-logo:hover span {
text-decoration: underline;
}
.cryptpad-toolbar-top .cryptpad-link a.cryptpad-logo img {
vertical-align: middle;
height: 32px;
cursor: pointer;
}
.cryptpad-toolbar-top .cryptpad-link a.cryptpad-logo span {
font-size: 1.5em;
margin-left: 5px;
vertical-align: middle;
cursor: pointer;
}
@media screen and (max-width: 400px) {
.cryptpad-toolbar-top .cryptpad-link .big {
display: none;
}
}
@media screen and (min-width: 401px) {
.cryptpad-toolbar-top .cryptpad-link .big {
display: inline-block;
}
}
@media screen and (max-width: 400px) {
.cryptpad-toolbar-top .cryptpad-link .small {
display: inline-block;
}
}
@media screen and (min-width: 401px) {
.cryptpad-toolbar-top .cryptpad-link .small {
display: none;
}
}
.cryptpad-toolbar-top .cryptpad-user {
position: absolute;
right: 0;
}
@media screen and (max-width: 400px) {
.cryptpad-toolbar-top .cryptpad-user {
top: 3em;
}
}
@media screen and (min-width: 401px) {
.cryptpad-toolbar-top .cryptpad-user {
top: 0px;
}
}
.cryptpad-toolbar-leftside { .cryptpad-toolbar-leftside {
float: left; float: left;
margin-bottom: -1px; margin-bottom: -1px;
} }
.cryptpad-toolbar-leftside .cryptpad-user-list {
float: right;
}
.cryptpad-toolbar-leftside .cryptpad-dropdown-container { .cryptpad-toolbar-leftside .cryptpad-dropdown-container {
position: relative; position: relative;
display: inline-block; display: inline-block;
@ -131,10 +259,9 @@
.cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p.cryptpad-dropdown-users { .cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p.cryptpad-dropdown-users {
text-align: baseline; text-align: baseline;
} }
.cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p.cryptpad-dropdown-users .yourself { .cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p.cryptpad-dropdown-users .yourself,
font-style: italic; .cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p.cryptpad-dropdown-users .anonymous,
} .cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p.cryptpad-dropdown-users .viewer {
.cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p.cryptpad-dropdown-users .anonymous {
font-style: italic; font-style: italic;
} }
.cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p h2 { .cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p h2 {
@ -156,7 +283,6 @@
} }
.cryptpad-toolbar-rightside { .cryptpad-toolbar-rightside {
text-align: right; text-align: right;
margin-right: 30px;
} }
.cryptpad-spinner { .cryptpad-spinner {
float: left; float: left;
@ -167,7 +293,7 @@
font-size: 20px; font-size: 20px;
} }
.cryptpad-readonly { .cryptpad-readonly {
margin-right: 20px; margin-right: 5px;
font-weight: bold; font-weight: bold;
text-transform: uppercase; text-transform: uppercase;
} }

@ -1,7 +1,10 @@
define(function () { define(function () {
var out = {}; var out = {};
out._languageName = "Français";
out.main_title = "Cryptpad: Editeur collaboratif en temps réel, zero knowledge"; out.main_title = "Cryptpad: Editeur collaboratif en temps réel, zero knowledge";
out.main_slogan = "L'unité est la force, la collaboration est la clé";
out.type = {}; out.type = {};
out.type.pad = 'Pad'; out.type.pad = 'Pad';
@ -26,8 +29,14 @@ define(function () {
out.yourself = "Vous-même"; out.yourself = "Vous-même";
out.anonymousUsers = "utilisateurs anonymes"; out.anonymousUsers = "utilisateurs anonymes";
out.anonymousUser = "utilisateur anonyme"; out.anonymousUser = "utilisateur anonyme";
out.share = "Partage"; out.shareView = "URL de lecture seule";
out.shareEdit = "URL d'édition";
out.users = "Utilisateurs"; out.users = "Utilisateurs";
out.and = "Et";
out.viewer = "lecteur";
out.viewers = "lecteurs";
out.editor = "éditeur";
out.editors = "éditeurs";
out.greenLight = "Tout fonctionne bien"; out.greenLight = "Tout fonctionne bien";
out.orangeLight = "Votre connexion est lente, ce qui réduit la qualité de l'éditeur"; out.orangeLight = "Votre connexion est lente, ce qui réduit la qualité de l'éditeur";
@ -51,12 +60,13 @@ define(function () {
out.renameButtonTitle = 'Changer le titre utilisé par ce document dans la page d\'accueil de Cryptpad'; out.renameButtonTitle = 'Changer le titre utilisé par ce document dans la page d\'accueil de Cryptpad';
out.renamePrompt = 'Quel titre souhaitez-vous utiliser pour ce document ?'; out.renamePrompt = 'Quel titre souhaitez-vous utiliser pour ce document ?';
out.renameConflict = 'Un autre document existe déjà avec le même titre'; out.renameConflict = 'Un autre document existe déjà avec le même titre';
out.clickToEdit = 'Cliquer pour modifier';
out.forgetButton = 'OUBLIER'; out.forgetButton = 'OUBLIER';
out.forgetButtonTitle = 'Enlever ce document de la liste en page d\'accueil'; out.forgetButtonTitle = 'Enlever ce document de la liste en page d\'accueil';
out.forgetPrompt = 'Cliquer sur OK supprimera l\'URL de ce document de la mémoire de votre navigateur (localStorage), êtes-vous sûr ?'; out.forgetPrompt = 'Cliquer sur OK supprimera l\'URL de ce document de la mémoire de votre navigateur (localStorage), êtes-vous sûr ?';
out.shareButton = 'PARTAGER'; out.shareButton = 'Partager';
out.shareButtonTitle = "Copier l'URL dans le presse-papiers"; out.shareButtonTitle = "Copier l'URL dans le presse-papiers";
out.shareSuccess = 'URL copiée dans le presse-papiers'; out.shareSuccess = 'URL copiée dans le presse-papiers';
out.shareFailed = "Échec de la copie de l'URL dans le presse-papiers"; out.shareFailed = "Échec de la copie de l'URL dans le presse-papiers";
@ -79,15 +89,17 @@ define(function () {
out.readonlyUrl = 'Document en lecture seule'; out.readonlyUrl = 'Document en lecture seule';
out.copyReadOnly = "Copier l'URL dans le presse-papiers"; out.copyReadOnly = "Copier l'URL dans le presse-papiers";
out.openReadOnly = "Ouvrir dans un nouvel onglet"; out.openReadOnly = "Ouvrir dans un nouvel onglet";
out.editing = "éditeur(s)"; out.editShare = "Partager l'URL d'édition";
out.viewing = "lecteur(s)";
out.editShare = "Partager l'URL";
out.editShareTitle = "Copier l'URL d'édition dans le presse-papiers"; out.editShareTitle = "Copier l'URL d'édition dans le presse-papiers";
out.viewShare = "Partager l'URL de lecture"; out.viewShare = "Partager l'URL de lecture";
out.viewShareTitle = "Copier l'URL d'accès en lecture seule dans le presse-papiers"; out.viewShareTitle = "Copier l'URL d'accès en lecture seule dans le presse-papiers";
out.viewOpen = "Voir dans un nouvel onglet"; out.viewOpen = "Voir dans un nouvel onglet";
out.viewOpenTitle = "Ouvrir le document en lecture seule dans un nouvel onglet"; out.viewOpenTitle = "Ouvrir le document en lecture seule dans un nouvel onglet";
out.notifyJoined = "{0} a rejoint la session collaborative";
out.notifyRenamed = "{0} a changé son nom en {1}";
out.notifyLeft = "{0} a quitté la session collaborative";
out.disconnectAlert = 'Perte de la connexion au réseau !'; out.disconnectAlert = 'Perte de la connexion au réseau !';
out.tryIt = 'Essayez-le !'; out.tryIt = 'Essayez-le !';
@ -147,7 +159,7 @@ define(function () {
// index.html // index.html
out.main_p1 = 'CryptPad est l\'éditeur collaboratif en temps réel <strong>zero knowledge</strong>. Le chiffrement est effectué depuis votre navigateur, ce qui protège les données contre le serveur, le cloud, et la NSA. La clé de chiffrement est stockée dans l\'<a href="https://fr.wikipedia.org/wiki/Identificateur_de_fragment">identifieur de fragment</a> de l\'URL qui n\'est jamais envoyée au serveur mais est accessible depuis javascript, de sorte qu\'en partageant l\'URL, vous donner l\'accès au pad à ceux qui souhaitent participer.'; out.main_p1 = 'CryptPad est l\'éditeur collaboratif en temps réel <strong>zero knowledge</strong>. Le chiffrement est effectué depuis votre navigateur, ce qui protège les données contre le serveur, le cloud, et la NSA. La clé de chiffrement est stockée dans l\'<a href="https://fr.wikipedia.org/wiki/Identificateur_de_fragment">identifieur de fragment</a> de l\'URL qui n\'est jamais envoyée au serveur mais est accessible depuis javascript, de sorte qu\'en partageant l\'URL, vous donnez l\'accès au pad à ceux qui souhaitent participer.';
out.main_p2 = 'Ce projet utilise l\'éditeur visuel (WYSIWYG) <a href="http://ckeditor.com/">CKEditor</a>, l\'éditeur de code source <a href="https://codemirror.net/">CodeMirror</a>, et le moteur temps-réel <a href="https://github.com/xwiki-contrib/chainpad">ChainPad</a>.'; out.main_p2 = 'Ce projet utilise l\'éditeur visuel (WYSIWYG) <a href="http://ckeditor.com/">CKEditor</a>, l\'éditeur de code source <a href="https://codemirror.net/">CodeMirror</a>, et le moteur temps-réel <a href="https://github.com/xwiki-contrib/chainpad">ChainPad</a>.';
out.main_howitworks = 'Comment ça fonctionne'; out.main_howitworks = 'Comment ça fonctionne';
out.main_howitworks_p1 = 'CryptPad utilise une variante de l\'algorithme d\'<a href="https://en.wikipedia.org/wiki/Operational_transformation">Operational transformation</a> qui est capable de trouver un consensus distribué en utilisant une chaîne de bloc Nakamoto, un outil popularisé par le <a href="https://fr.wikipedia.org/wiki/Bitcoin">Bitcoin</a>. De cette manière, l\'algorithme évite la nécessité d\'utiliser un serveur central pour résoudre les conflits d\'édition de l\'Operational Transformation, et sans ce besoin de résolution des conflits le serveur peut rester ignorant du contenu qui est édité dans le pad.'; out.main_howitworks_p1 = 'CryptPad utilise une variante de l\'algorithme d\'<a href="https://en.wikipedia.org/wiki/Operational_transformation">Operational transformation</a> qui est capable de trouver un consensus distribué en utilisant une chaîne de bloc Nakamoto, un outil popularisé par le <a href="https://fr.wikipedia.org/wiki/Bitcoin">Bitcoin</a>. De cette manière, l\'algorithme évite la nécessité d\'utiliser un serveur central pour résoudre les conflits d\'édition de l\'Operational Transformation, et sans ce besoin de résolution des conflits le serveur peut rester ignorant du contenu qui est édité dans le pad.';

@ -1,7 +1,10 @@
define(function () { define(function () {
var out = {}; var out = {};
out._languageName = 'English';
out.main_title = "Cryptpad: Zero Knowledge, Collaborative Real Time Editing"; out.main_title = "Cryptpad: Zero Knowledge, Collaborative Real Time Editing";
out.main_slogan = "Unity is Strength - Collaboration is Key";
out.type = {}; out.type = {};
out.type.pad = 'Pad'; out.type.pad = 'Pad';
@ -26,8 +29,14 @@ define(function () {
out.yourself = "Yourself"; out.yourself = "Yourself";
out.anonymousUsers = "anonymous users"; out.anonymousUsers = "anonymous users";
out.anonymousUser = "anonymous user"; out.anonymousUser = "anonymous user";
out.share = "Share"; out.shareView = "Read-only URL";
out.shareEdit = "Edit URL";
out.users = "Users"; out.users = "Users";
out.and = "And";
out.viewer = "viewer";
out.viewers = "viewers";
out.editor = "editor";
out.editors = "editors";
out.greenLight = "Everything is working fine"; out.greenLight = "Everything is working fine";
out.orangeLight = "Your slow connection may impact your experience"; out.orangeLight = "Your slow connection may impact your experience";
@ -45,18 +54,19 @@ define(function () {
out.userButton = 'USER'; out.userButton = 'USER';
out.userButtonTitle = 'Change your username'; out.userButtonTitle = 'Change your username';
out.changeNamePrompt = 'Change your name: '; out.changeNamePrompt = 'Change your name (leave empty to be anonymous): ';
out.renameButton = 'RENAME'; out.renameButton = 'RENAME';
out.renameButtonTitle = 'Change the title under which this document is listed on your home page'; out.renameButtonTitle = 'Change the title under which this document is listed on your home page';
out.renamePrompt = 'How would you like to title this pad?'; out.renamePrompt = 'How would you like to title this pad?';
out.renameConflict = 'Another pad already has that title'; out.renameConflict = 'Another pad already has that title';
out.clickToEdit = "Click to edit";
out.forgetButton = 'FORGET'; out.forgetButton = 'FORGET';
out.forgetButtonTitle = 'Remove this document from your home page listings'; out.forgetButtonTitle = 'Remove this document from your home page listings';
out.forgetPrompt = 'Clicking OK will remove the URL for this pad from localStorage, are you sure?'; out.forgetPrompt = 'Clicking OK will remove the URL for this pad from localStorage, are you sure?';
out.shareButton = 'SHARE'; out.shareButton = 'Share';
out.shareButtonTitle = "Copy URL to clipboard"; out.shareButtonTitle = "Copy URL to clipboard";
out.shareSuccess = 'Copied URL to clipboard'; out.shareSuccess = 'Copied URL to clipboard';
out.shareFailed = "Failed to copy URL to clipboard"; out.shareFailed = "Failed to copy URL to clipboard";
@ -79,15 +89,17 @@ define(function () {
out.readonlyUrl = 'Read only document'; out.readonlyUrl = 'Read only document';
out.copyReadOnly = "Copy URL to clipboard"; out.copyReadOnly = "Copy URL to clipboard";
out.openReadOnly = "Open in a new tab"; out.openReadOnly = "Open in a new tab";
out.editing = "editing"; out.editShare = "Share edit URL";
out.viewing = "viewing";
out.editShare = "Share";
out.editShareTitle = "Copy the edit URL to clipboard"; out.editShareTitle = "Copy the edit URL to clipboard";
out.viewShare = "Share view URL"; out.viewShare = "Share view URL";
out.viewShareTitle = "Copy the read-only URL to clipboard"; out.viewShareTitle = "Copy the read-only URL to clipboard";
out.viewOpen = "View in new tab"; out.viewOpen = "View in new tab";
out.viewOpenTitle = "Open the document in read-only mode in a new tab"; out.viewOpenTitle = "Open the document in read-only mode in a new tab";
out.notifyJoined = "{0} has joined the collaborative session";
out.notifyRenamed = "{0} is now known as {1}";
out.notifyLeft = "{0} has left the collaborative session";
out.disconnectAlert = 'Network connection lost!'; out.disconnectAlert = 'Network connection lost!';
out.tryIt = 'Try it out!'; out.tryIt = 'Try it out!';
@ -204,7 +216,10 @@ define(function () {
// Header.html // Header.html
out.header_france = '<a href="http://www.xwiki.com/" target="_blank" rel="noopener noreferrer">With <img class="bottom-bar-heart" src="/customize/heart.png" /> from <img class="bottom-bar-fr" src="/customize/fr.png" title="France" alt="France"/> by <img src="/customize/logo-xwiki.png" alt="XWiki SAS" class="bottom-bar-xwiki"/></a>'; out.header_france = '<a href="http://www.xwiki.com/" target="_blank" rel="noopener noreferrer">With <img class="bottom-bar-heart" src="/customize/heart.png" /> from <img class="bottom-bar-fr" src="/customize/fr.png" title="France" alt="France"/> by <img src="/customize/logo-xwiki.png" alt="XWiki SAS" class="bottom-bar-xwiki"/></a>';
out.header_xwiki = '<a href="http://www.xwiki.com/" target="_blank" rel="noopener noreferrer"><img src="/customize/logo-xwiki.png" alt="XWiki SAS" class="bottom-bar-xwiki"/></a>';
// TODO Hardcode cause YOLO
//out.header_xwiki = '<a href="http://www.xwiki.com/" target="_blank" rel="noopener noreferrer"><img src="/customize/logo-xwiki.png" alt="XWiki SAS" class="bottom-bar-xwiki"/></a>';
out.header_support = '<a href="http://ng.open-paas.org/" title="OpenPaaS::ng" target="_blank" rel="noopener noreferrer"> <img src="/customize/openpaasng.png" alt="OpenPaaS-ng" class="bottom-bar-openpaas" /></a>'; out.header_support = '<a href="http://ng.open-paas.org/" title="OpenPaaS::ng" target="_blank" rel="noopener noreferrer"> <img src="/customize/openpaasng.png" alt="OpenPaaS-ng" class="bottom-bar-openpaas" /></a>';
out.header_logoTitle = 'Go to the main page'; out.header_logoTitle = 'Go to the main page';

@ -21,7 +21,7 @@
} }
#iframe-container { #iframe-container {
position: fixed; position: fixed;
top: 2.6em; top: 0px;
bottom: 0px; bottom: 0px;
right: 0px; right: 0px;
left: 0px; left: 0px;

@ -45,10 +45,13 @@ define([
var andThen = function (CMeditor) { var andThen = function (CMeditor) {
var CodeMirror = module.CodeMirror = CMeditor; var CodeMirror = module.CodeMirror = CMeditor;
CodeMirror.modeURL = "/bower_components/codemirror/mode/%N/%N.js"; CodeMirror.modeURL = "/bower_components/codemirror/mode/%N/%N.js";
var $pad = $('#pad-iframe'); var $pad = $('#pad-iframe');
var $textarea = $pad.contents().find('#editor1'); var $textarea = $pad.contents().find('#editor1');
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
var parsedHash = Cryptpad.parsePadUrl(window.location.href);
var defaultName = Cryptpad.getDefaultName(parsedHash);
var editor = module.editor = CMeditor.fromTextArea($textarea[0], { var editor = module.editor = CMeditor.fromTextArea($textarea[0], {
lineNumbers: true, lineNumbers: true,
lineWrapping: true, lineWrapping: true,
@ -112,12 +115,22 @@ define([
editor.setOption('readOnly', !bool); editor.setOption('readOnly', !bool);
}; };
var userList = {}; // List of pretty name of all users (mapped with their server ID) var userData = module.userData = {}; // List of pretty name of all users (mapped with their server ID)
var toolbarList; // List of users still connected to the channel (server IDs) var userList; // List of users still connected to the channel (server IDs)
var addToUserList = function(data) { var addToUserData = function(data) {
for (var attrname in data) { userList[attrname] = data[attrname]; } var users = module.users;
if(toolbarList && typeof toolbarList.onChange === "function") { for (var attrname in data) { userData[attrname] = data[attrname]; }
toolbarList.onChange(userList);
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);
} }
}; };
@ -152,27 +165,30 @@ define([
var initializing = true; var initializing = true;
var onLocal = config.onLocal = function () { var stringifyInner = function (textValue) {
if (initializing) { return; } var obj = {
if (readOnly) { return; } content: textValue,
metadata: {
editor.save(); users: userData,
var textValue = canonicalize($textarea.val()); defaultTitle: defaultName
var obj = {content: textValue}; }
// append the userlist to the hyperjson structure
obj.metadata = {
users: userList
}; };
if (!isDefaultTitle()) {
obj.metadata.title = document.title; obj.metadata.title = document.title;
}
// set mode too... // set mode too...
obj.highlightMode = module.highlightMode; obj.highlightMode = module.highlightMode;
// stringify the json and send it into chainpad // stringify the json and send it into chainpad
var shjson = stringify(obj); return stringify(obj);
};
var onLocal = config.onLocal = function () {
if (initializing) { return; }
if (readOnly) { return; }
editor.save();
var textValue = canonicalize($textarea.val());
var shjson = stringifyInner(textValue);
module.patchText(shjson); module.patchText(shjson);
@ -191,13 +207,14 @@ define([
myData[myID] = { myData[myID] = {
name: myUserName name: myUserName
}; };
addToUserList(myData); addToUserData(myData);
Cryptpad.setAttribute('username', myUserName, function (err, data) { Cryptpad.setAttribute('username', myUserName, function (err, data) {
if (err) { if (err) {
console.log("Couldn't set username"); console.log("Couldn't set username");
console.error(err); console.error(err);
return; return;
} }
module.userName.lastName = myUserName;
onLocal(); onLocal();
}); });
}; };
@ -233,10 +250,10 @@ define([
} }
// lines including a c-style comment are also valuable // lines including a c-style comment are also valuable
var clike = /^\s*(\/\*|\/\/)(.*?)(\*\/)$/; var clike = /^\s*(\/\*|\/\/)(.*)?(\*\/)*$/;
if (clike.test(line)) { if (clike.test(line)) {
line.replace(clike, function (a, one, two) { line.replace(clike, function (a, one, two) {
text = two; text = two.replace(/\*\/\s*$/, '').trim();
}); });
return true; return true;
} }
@ -246,13 +263,10 @@ define([
}; };
var suggestName = function () { var suggestName = function () {
var parsed = Cryptpad.parsePadUrl(window.location.href); if (document.title === defaultName) {
var name = Cryptpad.getDefaultName(parsed, []); return getHeadingText() || "";
if (Cryptpad.isDefaultName(parsed, document.title)) {
return getHeadingText() || document.title;
} else { } else {
return document.title || getHeadingText() || name; return document.title || getHeadingText() || defaultName;
} }
}; };
@ -298,16 +312,68 @@ define([
onLocal(); onLocal();
}; };
var renameCb = function (err, title) {
if (err) { return; }
document.title = title;
onLocal();
};
var updateTitle = function (newTitle) {
if (newTitle === document.title) { return; }
// Change the title now, and set it back to the old value if there is an error
var oldTitle = document.title;
document.title = newTitle;
Cryptpad.renamePad(newTitle, function (err, data) {
if (err) {
console.log("Couldn't set pad title");
console.error(err);
document.title = oldTitle;
return;
}
document.title = data;
$bar.find('.' + Toolbar.constants.title).find('span.title').text(data);
$bar.find('.' + Toolbar.constants.title).find('input').val(data);
});
};
var updateDefaultTitle = function (defaultTitle) {
defaultName = defaultTitle;
$bar.find('.' + Toolbar.constants.title).find('input').attr("placeholder", defaultName);
};
var updateMetadata = function(shjson) {
// Extract the user list (metadata) from the hyperjson
var json = (shjson === "") ? "" : JSON.parse(shjson);
if (json && json.metadata) {
if (json.metadata.users) {
var userData = json.metadata.users;
// Update the local user data
addToUserData(userData);
}
if (json.metadata.defaultTitle) {
updateDefaultTitle(json.metadata.defaultTitle);
}
if (typeof json.metadata.title !== "undefined") {
updateTitle(json.metadata.title);
}
}
};
var onInit = config.onInit = function (info) { var onInit = config.onInit = function (info) {
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox'); userList = info.userList;
toolbarList = info.userList;
var config = { var config = {
userData: userList, userData: userData,
readOnly: readOnly, readOnly: readOnly,
ifrw: ifrw ifrw: ifrw,
title: {
onRename: renameCb,
defaultName: defaultName,
suggestName: suggestName
},
common: Cryptpad
}; };
if (readOnly) {delete config.changeNameID; } if (readOnly) {delete config.changeNameID; }
toolbar = module.toolbar = Toolbar.create($bar, info.myID, info.realtime, info.getLag, info.userList, config); toolbar = module.toolbar = Toolbar.create($bar, info.myID, info.realtime, info.getLag, userList, config);
var $rightside = $bar.find('.' + Toolbar.constants.rightside); var $rightside = $bar.find('.' + Toolbar.constants.rightside);
var $userBlock = $bar.find('.' + Toolbar.constants.username); var $userBlock = $bar.find('.' + Toolbar.constants.username);
@ -321,13 +387,13 @@ define([
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
} }
// Store the object sent for the "change username" button so that we can update the field value correctly
var userNameButtonObject = module.userName = {};
/* add a "change username" button */ /* add a "change username" button */
getLastName(function (err, lastName) { getLastName(function (err, lastName) {
var usernameCb = function (newName) { userNameButtonObject.lastName = lastName;
setName (newName); var $username = module.$userNameButton = Cryptpad.createButton('username', false, userNameButtonObject, setName).hide();
}; $userBlock.append($username);
var $username = Cryptpad.createButton('username', false, {lastName: lastName}, usernameCb);
$userBlock.append($username).hide();
}); });
/* add an export button */ /* add an export button */
@ -340,13 +406,8 @@ define([
$rightside.append($import); $rightside.append($import);
/* add a rename button */ /* add a rename button */
var renameCb = function (err, title) { //var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb);
if (err) { return; } //$rightside.append($setTitle);
document.title = title;
onLocal();
};
var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb);
$rightside.append($setTitle);
} }
/* add a forget button */ /* add a forget button */
@ -444,47 +505,10 @@ define([
console.error(err); console.error(err);
return; return;
} }
document.title = title || info.channel.slice(0, 8); updateTitle(title || defaultName);
Cryptpad.setPadTitle(title, function (err, data) {
if (err) {
console.log("Unable to set pad title");
console.error(err);
return;
}
});
}); });
}; };
var updateTitle = function (newTitle) {
if (newTitle === document.title) { return; }
// Change the title now, and set it back to the old value if there is an error
var oldTitle = document.title;
document.title = newTitle;
Cryptpad.setPadTitle(newTitle, function (err, data) {
if (err) {
console.log("Couldn't set pad title");
console.error(err);
document.title = oldTitle;
return;
}
});
};
var updateMetadata = function(shjson) {
// Extract the user list (metadata) from the hyperjson
var json = (shjson === "") ? "" : JSON.parse(shjson);
if (json && json.metadata) {
if (json.metadata.users) {
var userData = json.metadata.users;
// Update the local user data
addToUserList(userData);
}
if (json.metadata.title) {
updateTitle(json.metadata.title);
}
}
};
var unnotify = module.unnotify = function () { var unnotify = module.unnotify = function () {
if (module.tabNotification && if (module.tabNotification &&
typeof(module.tabNotification.cancel) === 'function') { typeof(module.tabNotification.cancel) === 'function') {
@ -501,6 +525,7 @@ define([
var onReady = config.onReady = function (info) { var onReady = config.onReady = function (info) {
var realtime = module.realtime = info.realtime; var realtime = module.realtime = info.realtime;
module.users = info.userList.users;
module.patchText = TextPatcher.create({ module.patchText = TextPatcher.create({
realtime: realtime, realtime: realtime,
//logging: true //logging: true
@ -549,14 +574,16 @@ define([
// Update the toolbar list: // Update the toolbar list:
// Add the current user in the metadata if he has edit rights // Add the current user in the metadata if he has edit rights
if (readOnly) { return; } if (readOnly) { return; }
if (typeof(lastName) === 'string' && lastName.length) {
setName(lastName);
} else {
myData[myID] = { myData[myID] = {
name: "" name: ""
}; };
addToUserList(myData); addToUserData(myData);
if (typeof(lastName) === 'string' && lastName.length) {
setName(lastName);
}
onLocal(); onLocal();
module.$userNameButton.click();
}
}); });
}; };
@ -628,18 +655,8 @@ define([
editor.scrollTo(scroll.left, scroll.top); editor.scrollTo(scroll.left, scroll.top);
if (!readOnly) { if (!readOnly) {
var localDoc = canonicalize($textarea.val()); var textValue = canonicalize($textarea.val());
var hjson2 = { var shjson2 = stringifyInner(textValue);
content: localDoc,
metadata: {
users: userList
},
highlightMode: highlightMode,
};
if (!isDefaultTitle()) {
hjson2.metadata.title = document.title;
}
var shjson2 = stringify(hjson2);
if (shjson2 !== shjson) { if (shjson2 !== shjson) {
console.error("shjson2 !== shjson"); console.error("shjson2 !== shjson");
TextPatcher.log(shjson, TextPatcher.diff(shjson, shjson2)); TextPatcher.log(shjson, TextPatcher.diff(shjson, shjson2));

@ -277,15 +277,15 @@ define([
}; };
var parsePadUrl = common.parsePadUrl = function (href) { var parsePadUrl = common.parsePadUrl = function (href) {
var patt = /^https*:\/\/([^\/]*)\/(.*?)\/#(.*)$/i; var patt = /^https*:\/\/([^\/]*)\/(.*?)\//i;
var ret = {}; var ret = {};
href.replace(patt, function (a, domain, type, hash) { var hash = href.replace(patt, function (a, domain, type, hash) {
ret.domain = domain; ret.domain = domain;
ret.type = type; ret.type = type;
ret.hash = hash;
return ''; return '';
}); });
ret.hash = hash.replace(/#/g, '');
return ret; return ret;
}; };
@ -303,13 +303,17 @@ define([
var type = parsed.type; var type = parsed.type;
var untitledIndex = 1; var untitledIndex = 1;
var name = (Messages.type)[type] + ' - ' + new Date().toString().split(' ').slice(0,4).join(' '); var name = (Messages.type)[type] + ' - ' + new Date().toString().split(' ').slice(0,4).join(' ');
return name;
/*
* Pad titles are shared in the document so it does not make sense anymore to avoid duplicates
if (isNameAvailable(name, parsed, recentPads)) { return name; } if (isNameAvailable(name, parsed, recentPads)) { return name; }
while (!isNameAvailable(name + ' - ' + untitledIndex, parsed, recentPads)) { untitledIndex++; } while (!isNameAvailable(name + ' - ' + untitledIndex, parsed, recentPads)) { untitledIndex++; }
return name + ' - ' + untitledIndex; return name + ' - ' + untitledIndex;
*/
}; };
var isDefaultName = common.isDefaultName = function (parsed, title) { var isDefaultName = common.isDefaultName = function (parsed, title) {
var name = getDefaultName(parsed, []); var name = getDefaultName(parsed, []);
return title.slice(0, name.length) === name; return title === name;
}; };
var makePad = function (href, title) { var makePad = function (href, title) {
@ -620,6 +624,47 @@ define([
/* /*
* Buttons * Buttons
*/ */
var renamePad = common.renamePad = function (title, callback) {
if (title === null) { return; }
if (title.trim() === "") {
var parsed = parsePadUrl(window.location.href);
title = getDefaultName(parsed);
}
common.setPadTitle(title, function (err, data) {
if (err) {
console.log("unable to set pad title");
console.log(err);
return;
}
callback(null, title);
});
/* Pad titles are shared in the document. We don't check for duplicates anymore.
common.causesNamingConflict(title, function (err, conflicts) {
if (err) {
console.log("Unable to determine if name caused a conflict");
console.error(err);
callback(err, title);
return;
}
if (conflicts) {
common.alert(Messages.renameConflict);
return;
}
common.setPadTitle(title, function (err, data) {
if (err) {
console.log("unable to set pad title");
console.log(err);
return;
}
callback(null, title);
});
});
*/
};
var createButton = common.createButton = function (type, rightside, data, callback) { var createButton = common.createButton = function (type, rightside, data, callback) {
var button; var button;
var size = "17px"; var size = "17px";
@ -658,32 +703,8 @@ define([
button.click(function() { button.click(function() {
var suggestion = suggestName(); var suggestion = suggestName();
common.prompt(Messages.renamePrompt, common.prompt(Messages.renamePrompt, suggestion, function (title, ev) {
suggestion, function (title, ev) { renamePad(title, callback);
if (title === null) { return; }
common.causesNamingConflict(title, function (err, conflicts) {
if (err) {
console.log("Unable to determine if name caused a conflict");
console.error(err);
callback(err, title);
return;
}
if (conflicts) {
common.alert(Messages.renameConflict);
return;
}
common.setPadTitle(title, function (err, data) {
if (err) {
console.log("unable to set pad title");
console.log(err);
return;
}
callback(null, title);
});
});
}); });
}); });
} }
@ -720,9 +741,8 @@ define([
title: Messages.userButton + '\n' + Messages.userButtonTitle title: Messages.userButton + '\n' + Messages.userButtonTitle
}).html('<span class="fa fa-user" style="font-family:FontAwesome;"></span>'); }).html('<span class="fa fa-user" style="font-family:FontAwesome;"></span>');
if (data && typeof data.lastName !== "undefined" && callback) { if (data && typeof data.lastName !== "undefined" && callback) {
var lastName = data.lastName;
button.click(function() { button.click(function() {
common.prompt(Messages.changeNamePrompt, lastName, function (newName) { common.prompt(Messages.changeNamePrompt, data.lastName, function (newName) {
callback(newName); callback(newName);
}); });
}); });

@ -17,6 +17,7 @@ define([
/** The toolbar class which contains the user list, debug link and lag. */ /** The toolbar class which contains the user list, debug link and lag. */
var TOOLBAR_CLS = Bar.constants.toolbar = 'cryptpad-toolbar'; var TOOLBAR_CLS = Bar.constants.toolbar = 'cryptpad-toolbar';
var TOP_CLS = Bar.constants.top = 'cryptpad-toolbar-top';
var LEFTSIDE_CLS = Bar.constants.leftside = 'cryptpad-toolbar-leftside'; var LEFTSIDE_CLS = Bar.constants.leftside = 'cryptpad-toolbar-leftside';
var RIGHTSIDE_CLS = Bar.constants.rightside = 'cryptpad-toolbar-rightside'; var RIGHTSIDE_CLS = Bar.constants.rightside = 'cryptpad-toolbar-rightside';
@ -34,6 +35,8 @@ define([
var VIEWSHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-viewShare"; var VIEWSHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-viewShare";
var DROPDOWN_CONTAINER_CLS = Bar.constants.dropdownContainer = "cryptpad-dropdown-container"; var DROPDOWN_CONTAINER_CLS = Bar.constants.dropdownContainer = "cryptpad-dropdown-container";
var DROPDOWN_CLS = Bar.constants.dropdown = "cryptpad-dropdown"; var DROPDOWN_CLS = Bar.constants.dropdown = "cryptpad-dropdown";
var TITLE_CLS = Bar.constants.title = "cryptpad-title";
var USER_CLS = Bar.constants.userAdmin = "cryptpad-user";
/** Key in the localStore which indicates realtime activity should be disallowed. */ /** Key in the localStore which indicates realtime activity should be disallowed. */
// TODO remove? will never be used in cryptpad // TODO remove? will never be used in cryptpad
@ -49,6 +52,7 @@ define([
var $style; var $style;
var firstConnection = true; var firstConnection = true;
var lagErrors = 0;
var styleToolbar = function ($container, href) { var styleToolbar = function ($container, href) {
href = href || '/customize/toolbar.css'; href = href || '/customize/toolbar.css';
@ -67,6 +71,7 @@ define([
'class': TOOLBAR_CLS, 'class': TOOLBAR_CLS,
id: uid(), id: uid(),
}) })
.append($('<div>', {'class': TOP_CLS}))
.append($('<div>', {'class': LEFTSIDE_CLS})) .append($('<div>', {'class': LEFTSIDE_CLS}))
.append($('<div>', {'class': RIGHTSIDE_CLS})); .append($('<div>', {'class': RIGHTSIDE_CLS}));
@ -100,34 +105,6 @@ define([
'class': USERBUTTONS_CONTAINER_CLS 'class': USERBUTTONS_CONTAINER_CLS
}).appendTo($userlistElement); }).appendTo($userlistElement);
var $editIcon = $('<button>', {
'class': 'userlist dropbtn edit',
});
var $editIconSmall = $editIcon.clone().addClass('small');
var $viewIcon = $('<button>', {
'class': 'userlist dropbtn view',
});
var $viewIconSmall = $viewIcon.clone().addClass('small');
var $shareTitle = $('<h2>').text(Messages.share);
var $dropdownEditUsers = $('<p>', {'class': USERLIST_CLS});
var $dropdownEditShare = $('<p>', {'class': EDITSHARE_CLS});
if (readOnly !== 1) {
$dropdownEditShare.append($shareTitle);
}
var $dropdownEditContainer = $('<div>', {'class': DROPDOWN_CONTAINER_CLS});
var $dropdownEdit = $('<div>', {
id: "cryptpad-dropdown-edit",
'class': DROPDOWN_CLS
}).append($dropdownEditUsers).append($dropdownEditShare);
var $dropdownViewShare = $('<p>', {'class': VIEWSHARE_CLS}).append($shareTitle.clone());
var $dropdownViewContainer = $('<div>', {'class': DROPDOWN_CONTAINER_CLS});
var $dropdownView = $('<div>', {
id: "cryptpad-dropdown-view",
'class': DROPDOWN_CLS
}).append($dropdownViewShare);
var createHandler = function ($elmt) { var createHandler = function ($elmt) {
return function () { return function () {
if ($elmt.is(':visible')) { if ($elmt.is(':visible')) {
@ -138,29 +115,59 @@ define([
$elmt.show(); $elmt.show();
}; };
}; };
var fa_caretdown = '<span class="fa fa-caret-down" style="font-family:FontAwesome;"></span>';
// User list button
var $editIcon = $('<button>', {
'class': 'userlist dropbtn edit',
});
var $editIconSmall = $editIcon.clone().addClass('small');
var $dropdownEditUsers = $('<p>', {'class': USERLIST_CLS});
var $dropdownEditContainer = $('<div>', {'class': DROPDOWN_CONTAINER_CLS});
var $dropdownEdit = $('<div>', {
id: "cryptpad-dropdown-edit",
'class': DROPDOWN_CLS
}).append($dropdownEditUsers); //.append($dropdownEditShare);
$editIcon.click(createHandler($dropdownEdit)); $editIcon.click(createHandler($dropdownEdit));
$editIconSmall.click(createHandler($dropdownEdit)); $editIconSmall.click(createHandler($dropdownEdit));
$viewIcon.click(createHandler($dropdownView));
$viewIconSmall.click(createHandler($dropdownView));
$dropdownEditContainer.append($editIcon).append($editIconSmall).append($dropdownEdit); $dropdownEditContainer.append($editIcon).append($editIconSmall).append($dropdownEdit);
$dropdownViewContainer.append($viewIcon).append($viewIconSmall).append($dropdownView);
$listElement.append($dropdownEditContainer); $listElement.append($dropdownEditContainer);
if (readOnly !== -1) {
$listElement.append($dropdownViewContainer);
}
// Share button
var fa_share = '<span class="fa fa-share-alt" style="font-family:FontAwesome;"></span>';
var fa_editusers = '<span class="fa fa-users" style="font-family:FontAwesome;"></span>';
var fa_viewusers = '<span class="fa fa-eye" style="font-family:FontAwesome;"></span>';
var $shareIcon = $('<button>', {
'class': 'userlist dropbtn share',
}).html(fa_share + ' ' + Messages.shareButton + ' ' + fa_caretdown);
var $shareIconSmall = $shareIcon.clone().addClass('small').html(fa_share + ' ' + fa_caretdown);
var $shareTitle = $('<h2>').html(fa_editusers + ' ' + Messages.shareEdit);
var $shareTitleView = $('<h2>').html(fa_viewusers + ' ' + Messages.shareView);
var $dropdownShareP = $('<p>', {'class': EDITSHARE_CLS}).append($shareTitle);
var $dropdownSharePView = $('<p>', {'class': VIEWSHARE_CLS}).append($shareTitleView);
var $dropdownShareContainer = $('<div>', {'class': DROPDOWN_CONTAINER_CLS});
var $dropdownShare = $('<div>', {
id: "cryptpad-dropdown-share",
'class': DROPDOWN_CLS
});
if (readOnly !== 1) {
// Hide the "Share edit URL" section when in read-only mode
$dropdownShare.append($dropdownShareP);
}
$dropdownShare.append($dropdownSharePView);
$shareIcon.click(createHandler($dropdownShare));
$shareIconSmall.click(createHandler($dropdownShare));
$dropdownShareContainer.append($shareIcon).append($shareIconSmall).append($dropdownShare);
$listElement.append($dropdownShareContainer);
}; };
var createUserList = function ($container, readOnly) { var createUserList = function ($container, readOnly) {
var $state = $('<span>', {'class': STATE_CLS}).text(Messages.synchronizing); var $state = $('<span>', {'class': STATE_CLS}).text(Messages.synchronizing);
var $usernameElement = $('<span>', {'class': USERNAME_CLS});
var $userlist = $('<div>', { var $userlist = $('<div>', {
'class': USER_LIST_CLS, 'class': USER_LIST_CLS,
id: uid(), id: uid(),
}).append($state).append($usernameElement); }).append($state);
createUserButtons($userlist, readOnly); createUserButtons($userlist, readOnly);
$container.append($userlist); $container.append($userlist);
return $userlist[0]; return $userlist[0];
@ -192,7 +199,7 @@ define([
if (n === 1) { return '; + ' + Messages.oneViewer; } if (n === 1) { return '; + ' + Messages.oneViewer; }
return '; + ' + Messages._getKey('viewers', [n]); return '; + ' + Messages._getKey('viewers', [n]);
}; };
var updateUserList = function (myUserName, userlistElement, userList, userData, readOnly, $stateElement) { var updateUserList = function (myUserName, userlistElement, userList, userData, readOnly, $stateElement, $userAdminElement) {
var meIdx = userList.indexOf(myUserName); var meIdx = userList.indexOf(myUserName);
if (meIdx === -1) { if (meIdx === -1) {
$stateElement.text(Messages.synchronizing); $stateElement.text(Messages.synchronizing);
@ -200,27 +207,26 @@ define([
} }
$stateElement.text(''); $stateElement.text('');
// Make sure the user block elements are displayed // Make sure the elements are displayed
var $userButtons = $(userlistElement).find("#userButtons"); var $userButtons = $(userlistElement).find("#userButtons");
$userButtons.show(); $userButtons.show();
var $userElement = $(userlistElement).find('.' + USERNAME_CLS);
$userElement.show();
var numberOfUsers = userList.length; var numberOfUsers = userList.length;
// If we are using old pads (readonly unavailable), only editing users are in userList. // 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 // 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 // the userData to have only the editing users. We can't use userData directly since it
// contains data about users that have already left the channel. // may contain data about users that have already left the channel.
userList = readOnly === -1 ? userList : arrayIntersect(userList, Object.keys(userData)); userList = readOnly === -1 ? userList : arrayIntersect(userList, Object.keys(userData));
var numberOfViewUsers = numberOfUsers - userList.length; var numberOfEditUsers = userList.length;
var numberOfViewUsers = numberOfUsers - numberOfEditUsers;
// Names of editing users // Names of editing users
var editUsersNames = getOtherUsers(myUserName, userList, userData); var editUsersNames = getOtherUsers(myUserName, userList, userData);
// Number of anonymous editing users // Number of anonymous editing users
var anonymous = numberOfUsers - editUsersNames.length; var anonymous = numberOfEditUsers - editUsersNames.length;
// Update the userlist // Update the userlist
var editUsersList = ''; var editUsersList = '';
@ -232,9 +238,20 @@ define([
var text = anonymous === 1 ? Messages.anonymousUser : Messages.anonymousUsers; var text = anonymous === 1 ? Messages.anonymousUser : Messages.anonymousUsers;
editUsersNames.push('<span class="anonymous">' + anonymous + ' ' + text + '</span>'); editUsersNames.push('<span class="anonymous">' + anonymous + ' ' + text + '</span>');
} }
if (numberOfViewUsers > 0) {
var viewText = '<span class="viewer">';
if (numberOfEditUsers > 0) {
editUsersNames.push('');
viewText += Messages.and + ' ';
}
var viewerText = numberOfViewUsers > 1 ? Messages.viewers : Messages.viewer;
viewText += numberOfViewUsers + ' ' + viewerText + '</span>';
editUsersNames.push(viewText);
}
if (editUsersNames.length > 0) { if (editUsersNames.length > 0) {
editUsersList += editUsersNames.join('<br>'); editUsersList += editUsersNames.join('<br>');
} }
var $usersTitle = $('<h2>').text(Messages.users); var $usersTitle = $('<h2>').text(Messages.users);
var $editUsers = $userButtons.find('.' + USERLIST_CLS); var $editUsers = $userButtons.find('.' + USERLIST_CLS);
$editUsers.html('').append($usersTitle).append(editUsersList); $editUsers.html('').append($usersTitle).append(editUsersList);
@ -243,13 +260,15 @@ define([
var fa_caretdown = '<span class="fa fa-caret-down" style="font-family:FontAwesome;"></span>'; var fa_caretdown = '<span class="fa fa-caret-down" style="font-family:FontAwesome;"></span>';
var fa_editusers = '<span class="fa fa-users" style="font-family:FontAwesome;"></span>'; var fa_editusers = '<span class="fa fa-users" style="font-family:FontAwesome;"></span>';
var fa_viewusers = '<span class="fa fa-eye" style="font-family:FontAwesome;"></span>'; var fa_viewusers = '<span class="fa fa-eye" style="font-family:FontAwesome;"></span>';
$userButtons.find('.userlist.edit').html(fa_editusers + ' ' + userList.length + ' ' + Messages.editing + ' ' + fa_caretdown); var viewersText = numberOfViewUsers > 1 ? Messages.viewers : Messages.viewer;
$userButtons.find('.userlist.edit.small').html(fa_editusers + ' ' + userList.length + ' ' + fa_caretdown); var editorsText = numberOfEditUsers > 1 ? Messages.editors : Messages.editor;
$userButtons.find('.userlist.view').html(fa_viewusers + ' ' + numberOfViewUsers + ' ' + Messages.viewing + ' ' + fa_caretdown); $userButtons.find('.userlist.edit').html(fa_editusers + ' ' + numberOfEditUsers + ' ' + editorsText + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers + ' ' + viewersText + ' ' + fa_caretdown);
$userButtons.find('.userlist.view.small').html(fa_viewusers + ' ' + numberOfViewUsers + ' ' + fa_caretdown); $userButtons.find('.userlist.edit.small').html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers + ' ' + fa_caretdown);
// Change username button
var $userElement = $userAdminElement.find('.' + USERNAME_CLS);
$userElement.show();
if (readOnly === 1) { if (readOnly === 1) {
// TODO
$userElement.html('<span class="' + READONLY_CLS + '">' + Messages.readonly + '</span>'); $userElement.html('<span class="' + READONLY_CLS + '">' + Messages.readonly + '</span>');
} }
else { else {
@ -258,6 +277,7 @@ define([
if (!name) { if (!name) {
name = Messages.anonymous; name = Messages.anonymous;
} }
$userElement.find("button").show();
$userElement.find("button").html(icon + ' ' + name); $userElement.find("button").html(icon + ' ' + name);
} }
}; };
@ -280,7 +300,8 @@ define([
'class': 'lag' 'class': 'lag'
}); });
var title; var title;
if(lag) { if (typeof lag !== "undefined") {
lagErrors = 0;
firstConnection = false; firstConnection = false;
title = Messages.lag + ' : ' + lag + ' ms\n'; title = Messages.lag + ' : ' + lag + ' ms\n';
if (lag.waiting || lag > 1000) { if (lag.waiting || lag > 1000) {
@ -292,21 +313,168 @@ define([
} }
} }
else if (!firstConnection) { else if (!firstConnection) {
lagErrors++;
// Display the red light at the 2nd failed attemp to get the lag
if (lagErrors > 1) {
lagLight.addClass('lag-red'); lagLight.addClass('lag-red');
title = Messages.redLight; title = Messages.redLight;
} }
}
if (title) {
lagLight.attr('title', title); lagLight.attr('title', title);
$(lagElement).html(''); $(lagElement).html('');
$(lagElement).append(lagLight); $(lagElement).append(lagLight);
}
};
var createLinkToMain = function ($topContainer) {
var $linkContainer = $('<span>', {
'class': "cryptpad-link"
}).appendTo($topContainer);
var $imgTag = $('<img>', {
src: "/customize/cryptofist_mini.png",
alt: "Cryptpad",
'class': "cryptofist"
});
// 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('big').append($span);
$aTagSmall.addClass('small');
var onClick = function (e) {
e.preventDefault();
window.location = "/";
};
$aTagBig.click(onClick);
$aTagSmall.click(onClick);
$linkContainer.append($aTagSmall).append($aTagBig);
};
var createUserAdmin = function ($topContainer) {
var $userContainer = $('<span>', {
'class': USER_CLS
}).appendTo($topContainer);
var $span = $('<span>' , {
'class': 'cryptpad-language'
});
var $select = $('<select>', {
'id': 'language-selector'
}).appendTo($userContainer);
var languages = Messages._languages;
for (var l in languages) {
$('<option>', {
value: l
}).text(languages[l]).appendTo($select);
}
Messages._initSelector($select);
$select.on('mousedown', function (e) {
e.stopPropagation();
return true;
});
var $usernameElement = $('<span>', {'class': USERNAME_CLS}).appendTo($userContainer);
return $userContainer;
};
var createTitle = function ($container, readOnly, config, Cryptpad) {
config = config || {};
var callback = config.onRename;
var placeholder = config.defaultName;
var suggestName = config.suggestName;
// Buttons
var $titleContainer = $('<span>', {
id: 'toolbarTitle',
'class': TITLE_CLS
}).appendTo($container);
var $text = $('<span>', {
'class': 'title'
}).appendTo($titleContainer);
var $pencilIcon = $('<span>', {
'class': 'pencilIcon'
});
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) {
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 () {
$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) { 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 readOnly = (typeof config.readOnly !== "undefined") ? (config.readOnly ? 1 : 0) : -1;
var Cryptpad = config.common;
var toolbar = createRealtimeToolbar($container); var toolbar = createRealtimeToolbar($container);
var userListElement = createUserList(toolbar.find('.' + LEFTSIDE_CLS), readOnly); var userListElement = createUserList(toolbar.find('.' + LEFTSIDE_CLS), readOnly);
var spinner = createSpinner(toolbar.find('.' + RIGHTSIDE_CLS)); var spinner = createSpinner(toolbar.find('.' + RIGHTSIDE_CLS));
var lagElement = createLagElement(toolbar.find('.' + RIGHTSIDE_CLS)); var lagElement = createLagElement($(userListElement));
var $titleElement = createTitle(toolbar.find('.' + TOP_CLS), readOnly, config.title, Cryptpad);
var $linkElement = createLinkToMain(toolbar.find('.' + TOP_CLS));
var $userAdminElement = createUserAdmin(toolbar.find('.' + TOP_CLS));
var userData = config.userData; var userData = config.userData;
// readOnly = 1 (readOnly enabled), 0 (disabled), -1 (old pad without readOnly mode) // readOnly = 1 (readOnly enabled), 0 (disabled), -1 (old pad without readOnly mode)
var saveElement; var saveElement;
@ -322,23 +490,89 @@ define([
} }
$container.find('.cryptpad-dropdown').hide(); $container.find('.cryptpad-dropdown').hide();
}; };
var cancelEditTitle = function (e) {
if ($(e.target).parents('.' + TITLE_CLS).length) {
return;
}
$titleElement.find('input').hide();
$titleElement.find('span.title').show();
$titleElement.find('span.pencilIcon').css('display', '');
};
$(config.ifrw).on('click', removeDropdowns); $(config.ifrw).on('click', removeDropdowns);
$(config.ifrw).on('click', cancelEditTitle);
if (config.ifrw.$('iframe').length) { if (config.ifrw.$('iframe').length) {
var innerIfrw = config.ifrw.$('iframe').each(function (i, el) { var innerIfrw = config.ifrw.$('iframe').each(function (i, el) {
$(el.contentWindow).on('click', removeDropdowns); $(el.contentWindow).on('click', removeDropdowns);
$(el.contentWindow).on('click', cancelEditTitle);
}); });
} }
} }
userList.onChange = function(newUserData) { // Update user list
userList.change.push(function (newUserData) {
var users = userList.users; var users = userList.users;
if (users.indexOf(myUserName) !== -1) { connected = true; } if (users.indexOf(myUserName) !== -1) { connected = true; }
if (!connected) { return; } if (!connected) { return; }
if(newUserData) { // Someone has changed his name/color /*if (newUserData) { // Someone has changed his name/color
userData = newUserData; userData = newUserData;
}*/
updateUserList(myUserName, userListElement, users, userData, readOnly, $stateElement, $userAdminElement);
});
// Display notifications when users are joining/leaving the session
var oldUserData;
if (typeof Cryptpad !== "undefined") {
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;
} }
updateUserList(myUserName, userListElement, users, userData, readOnly, $stateElement);
}; };
userList.change.push(function (newdata) {
// Notify for disconnected users
if (typeof oldUserData !== "undefined") {
for (var u in oldUserData) {
if (userList.users.indexOf(u) === -1) {
notify(-1, oldUserData[u].name);
delete oldUserData[u];
}
}
}
// 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") {
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 () { var ks = function () {
if (connected) { kickSpinner(spinner, false); } if (connected) { kickSpinner(spinner, false); }

@ -21,7 +21,7 @@
} }
#pad-iframe { #pad-iframe {
position:fixed; position:fixed;
top:2.5em; top:0px;
left:0px; left:0px;
bottom:0px; bottom:0px;
right:0px; right:0px;

@ -85,11 +85,14 @@ define([
}); });
editor.on('instanceReady', function (Ckeditor) { editor.on('instanceReady', function (Ckeditor) {
var $bar = $('#pad-iframe')[0].contentWindow.$('#cke_1_toolbox');
var parsedHash = Cryptpad.parsePadUrl(window.location.href);
var defaultName = Cryptpad.getDefaultName(parsedHash);
if (readOnly) { if (readOnly) {
$('#pad-iframe')[0].contentWindow.$('#cke_1_toolbox > .cke_toolbar').hide(); $('#pad-iframe')[0].contentWindow.$('#cke_1_toolbox > .cke_toolbar').hide();
} }
/* add a class to the magicline plugin so we can pick it out more easily */ /* add a class to the magicline plugin so we can pick it out more easily */
var ml = $('iframe')[0].contentWindow.CKEDITOR.instances.editor1.plugins.magicline var ml = $('iframe')[0].contentWindow.CKEDITOR.instances.editor1.plugins.magicline
@ -250,19 +253,20 @@ define([
}; };
var initializing = true; var initializing = true;
var userList = module.userList = {}; // List of pretty name of all users (mapped with their server ID) var userData = module.userData = {}; // List of pretty names for all users (mapped with their ID)
var toolbarList; // List of users still connected to the channel (server IDs) var userList; // List of users still connected to the channel (server IDs)
var addToUserList = function(data) { var addToUserData = function(data) {
var users = module.users; var users = module.users;
for (var attrname in data) { userData[attrname] = data[attrname]; }
if (users && users.length) { if (users && users.length) {
for (var userKey in userList) { for (var userKey in userData) {
if (users.indexOf(userKey) === -1) { delete userList[userKey]; } if (users.indexOf(userKey) === -1) { delete userData[userKey]; }
} }
} }
for (var attrname in data) { userList[attrname] = data[attrname]; } if(userList && typeof userList.onChange === "function") {
if(toolbarList && typeof toolbarList.onChange === "function") { userList.onChange(userData);
toolbarList.onChange(userList);
} }
}; };
@ -290,13 +294,14 @@ define([
myData[myID] = { myData[myID] = {
name: myUserName name: myUserName
}; };
addToUserList(myData); addToUserData(myData);
editor.fire('change');
Cryptpad.setAttribute('username', newName, function (err, data) { Cryptpad.setAttribute('username', newName, function (err, data) {
if (err) { if (err) {
console.error("Couldn't set username"); console.error("Couldn't set username");
return;
} }
module.userName.lastName = myUserName;
editor.fire('change');
}); });
}; };
@ -317,13 +322,10 @@ define([
}; };
var suggestName = function () { var suggestName = function () {
var parsed = Cryptpad.parsePadUrl(window.location.href); if (document.title === defaultName) {
var name = Cryptpad.getDefaultName(parsed, []); return getHeadingText() || "";
if (Cryptpad.isDefaultName(parsed, document.title)) {
return getHeadingText() || document.title;
} else { } else {
return document.title || getHeadingText() || name; return document.title || getHeadingText() || defaultName;
} }
}; };
@ -344,12 +346,11 @@ define([
var hjson = Hyperjson.fromDOM(dom, isNotMagicLine, brFilter); var hjson = Hyperjson.fromDOM(dom, isNotMagicLine, brFilter);
hjson[3] = { hjson[3] = {
metadata: { metadata: {
users: userList, users: userData,
defaultTitle: defaultName
} }
}; };
if (!isDefaultTitle()) {
hjson[3].metadata.title = document.title; hjson[3].metadata.title = document.title;
}
return stringify(hjson); return stringify(hjson);
}; };
@ -395,16 +396,24 @@ define([
// Change the title now, and set it back to the old value if there is an error // Change the title now, and set it back to the old value if there is an error
var oldTitle = document.title; var oldTitle = document.title;
document.title = newTitle; document.title = newTitle;
Cryptpad.setPadTitle(newTitle, function (err, data) { Cryptpad.renamePad(newTitle, function (err, data) {
if (err) { if (err) {
console.log("Couldn't set pad title"); console.log("Couldn't set pad title");
console.error(err); console.error(err);
document.title = oldTitle; document.title = oldTitle;
return; return;
} }
document.title = data;
$bar.find('.' + Toolbar.constants.title).find('span.title').text(data);
$bar.find('.' + Toolbar.constants.title).find('input').val(data);
}); });
}; };
var updateDefaultTitle = function (defaultTitle) {
defaultName = defaultTitle;
$bar.find('.' + Toolbar.constants.title).find('input').attr("placeholder", defaultName);
};
var updateMetadata = function(shjson) { var updateMetadata = function(shjson) {
// Extract the user list (metadata) from the hyperjson // Extract the user list (metadata) from the hyperjson
var hjson = JSON.parse(shjson); var hjson = JSON.parse(shjson);
@ -413,9 +422,12 @@ define([
if (peerMetadata.metadata.users) { if (peerMetadata.metadata.users) {
var userData = peerMetadata.metadata.users; var userData = peerMetadata.metadata.users;
// Update the local user data // Update the local user data
addToUserList(userData); addToUserData(userData);
} }
if (peerMetadata.metadata.title) { if (peerMetadata.metadata.defaultTitle) {
updateDefaultTitle(peerMetadata.metadata.defaultTitle);
}
if (typeof peerMetadata.metadata.title !== "undefined") {
updateTitle(peerMetadata.metadata.title); updateTitle(peerMetadata.metadata.title);
} }
} }
@ -512,16 +524,27 @@ define([
realtimeOptions.onLocal(); realtimeOptions.onLocal();
}; };
var renameCb = function (err, title) {
if (err) { return; }
document.title = title;
editor.fire('change');
};
var onInit = realtimeOptions.onInit = function (info) { var onInit = realtimeOptions.onInit = function (info) {
var $bar = $('#pad-iframe')[0].contentWindow.$('#cke_1_toolbox'); userList = info.userList;
toolbarList = info.userList;
var config = { var config = {
userData: userList, userData: userData,
readOnly: readOnly, readOnly: readOnly,
ifrw: ifrw ifrw: ifrw,
title: {
onRename: renameCb,
defaultName: defaultName,
suggestName: suggestName
},
common: Cryptpad
}; };
if (readOnly) {delete config.changeNameID; } if (readOnly) {delete config.changeNameID; }
toolbar = info.realtime.toolbar = Toolbar.create($bar, info.myID, info.realtime, info.getLag, info.userList, config); toolbar = info.realtime.toolbar = Toolbar.create($bar, info.myID, info.realtime, info.getLag, userList, config);
var $rightside = $bar.find('.' + Toolbar.constants.rightside); var $rightside = $bar.find('.' + Toolbar.constants.rightside);
var $userBlock = $bar.find('.' + Toolbar.constants.username); var $userBlock = $bar.find('.' + Toolbar.constants.username);
@ -535,13 +558,13 @@ define([
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
} }
// Store the object sent for the "change username" button so that we can update the field value correctly
var userNameButtonObject = module.userName = {};
/* add a "change username" button */ /* add a "change username" button */
getLastName(function (err, lastName) { getLastName(function (err, lastName) {
var usernameCb = function (newName) { userNameButtonObject.lastName = lastName;
setName (newName); var $username = module.$userNameButton = Cryptpad.createButton('username', false, userNameButtonObject, setName).hide();
}; $userBlock.append($username);
var $username = Cryptpad.createButton('username', false, {lastName: lastName}, usernameCb);
$userBlock.append($username).hide();
}); });
/* add an export button */ /* add an export button */
@ -554,13 +577,8 @@ define([
$rightside.append($import); $rightside.append($import);
/* add a rename button */ /* add a rename button */
var renameCb = function (err, title) { //var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb);
if (err) { return; } //$rightside.append($setTitle);
document.title = title;
editor.fire('change');
};
var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb);
$rightside.append($setTitle);
} }
/* add a forget button */ /* add a forget button */
@ -593,13 +611,7 @@ define([
console.log("Couldn't get pad title"); console.log("Couldn't get pad title");
return; return;
} }
document.title = title || info.channel.slice(0, 8); updateTitle(title || defaultName);
Cryptpad.setPadTitle(title, function (err, data) {
if (err) {
console.log("Couldn't remember pad");
console.error(err);
}
});
}); });
}; };
@ -632,14 +644,16 @@ define([
// Update the toolbar list: // Update the toolbar list:
// Add the current user in the metadata if he has edit rights // Add the current user in the metadata if he has edit rights
if (readOnly) { return; } if (readOnly) { return; }
if (typeof(lastName) === 'string' && lastName.length) {
setName(lastName);
} else {
myData[myID] = { myData[myID] = {
name: "" name: ""
}; };
addToUserList(myData); addToUserData(myData);
if (typeof(lastName) === 'string' && lastName.length) {
setName(lastName);
}
realtimeOptions.onLocal(); realtimeOptions.onLocal();
module.$userNameButton.click();
}
}); });
}; };

@ -475,6 +475,8 @@ define([
setEditable(false); setEditable(false);
var ready = function (info) { var ready = function (info) {
module.users = info.userList.users;
console.log("Your realtime object is ready"); console.log("Your realtime object is ready");
module.ready = true; module.ready = true;

@ -21,7 +21,7 @@
} }
#iframe-container { #iframe-container {
position: fixed; position: fixed;
top: 2.6em; top: 0px;
bottom: 0px; bottom: 0px;
right: 0px; right: 0px;
left: 0px; left: 0px;

@ -62,10 +62,13 @@ define([
var andThen = function (CMeditor) { var andThen = function (CMeditor) {
var CodeMirror = module.CodeMirror = CMeditor; var CodeMirror = module.CodeMirror = CMeditor;
CodeMirror.modeURL = "/bower_components/codemirror/mode/%N/%N.js"; CodeMirror.modeURL = "/bower_components/codemirror/mode/%N/%N.js";
var $pad = $('#pad-iframe'); var $pad = $('#pad-iframe');
var $textarea = $pad.contents().find('#editor1'); var $textarea = $pad.contents().find('#editor1');
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
var parsedHash = Cryptpad.parsePadUrl(window.location.href);
var defaultName = Cryptpad.getDefaultName(parsedHash);
var editor = module.editor = CMeditor.fromTextArea($textarea[0], { var editor = module.editor = CMeditor.fromTextArea($textarea[0], {
lineNumbers: true, lineNumbers: true,
lineWrapping: true, lineWrapping: true,
@ -149,12 +152,22 @@ define([
editor.setOption('readOnly', !bool); editor.setOption('readOnly', !bool);
}; };
var userList = {}; // List of pretty name of all users (mapped with their server ID) var userData = module.userData = {}; // List of pretty name of all users (mapped with their server ID)
var toolbarList; // List of users still connected to the channel (server IDs) var userList; // List of users still connected to the channel (server IDs)
var addToUserList = function(data) { var addToUserData = function(data) {
for (var attrname in data) { userList[attrname] = data[attrname]; } var users = module.users;
if(toolbarList && typeof toolbarList.onChange === "function") { for (var attrname in data) { userData[attrname] = data[attrname]; }
toolbarList.onChange(userList);
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);
} }
}; };
@ -196,12 +209,11 @@ define([
var obj = { var obj = {
content: textValue, content: textValue,
metadata: { metadata: {
users: userList users: userData,
defaultTitle: defaultName
} }
}; };
if (!isDefaultTitle()) {
obj.metadata.title = APP.title; obj.metadata.title = APP.title;
}
if (textColor) { if (textColor) {
obj.metadata.color = textColor; obj.metadata.color = textColor;
} }
@ -239,13 +251,14 @@ define([
myData[myID] = { myData[myID] = {
name: myUserName name: myUserName
}; };
addToUserList(myData); addToUserData(myData);
Cryptpad.setAttribute('username', myUserName, function (err, data) { Cryptpad.setAttribute('username', myUserName, function (err, data) {
if (err) { if (err) {
console.log("Couldn't set username"); console.log("Couldn't set username");
console.error(err); console.error(err);
return; return;
} }
module.userName.lastName = myUserName;
onLocal(); onLocal();
}); });
}; };
@ -276,13 +289,10 @@ define([
}; };
var suggestName = function () { var suggestName = function () {
var parsed = Cryptpad.parsePadUrl(window.location.href); if (APP.title === defaultName) {
var name = Cryptpad.getDefaultName(parsed, []); return getHeadingText() || "";
if (Cryptpad.isDefaultName(parsed, APP.title)) {
return getHeadingText() || APP.title;
} else { } else {
return APP.title || getHeadingText() || name; return APP.title || getHeadingText() || defaultName;
} }
}; };
@ -334,7 +344,7 @@ define([
var oldTitle = APP.title; var oldTitle = APP.title;
APP.title = newTitle; APP.title = newTitle;
setTabTitle(); setTabTitle();
Cryptpad.setPadTitle(newTitle, function (err, data) { Cryptpad.renamePad(newTitle, function (err, data) {
if (err) { if (err) {
console.log("Couldn't set pad title"); console.log("Couldn't set pad title");
console.error(err); console.error(err);
@ -342,6 +352,10 @@ define([
setTabTitle(); setTabTitle();
return; return;
} }
APP.title = data;
setTabTitle();
$bar.find('.' + Toolbar.constants.title).find('span.title').text(data);
$bar.find('.' + Toolbar.constants.title).find('input').val(data);
}); });
}; };
@ -359,6 +373,11 @@ define([
} }
}; };
var updateDefaultTitle = function (defaultTitle) {
defaultName = defaultTitle;
$bar.find('.' + Toolbar.constants.title).find('input').attr("placeholder", defaultName);
};
var updateMetadata = function(shjson) { var updateMetadata = function(shjson) {
// Extract the user list (metadata) from the hyperjson // Extract the user list (metadata) from the hyperjson
var json = (shjson === "") ? "" : JSON.parse(shjson); var json = (shjson === "") ? "" : JSON.parse(shjson);
@ -366,22 +385,37 @@ define([
if (json.metadata.users) { if (json.metadata.users) {
var userData = json.metadata.users; var userData = json.metadata.users;
// Update the local user data // Update the local user data
addToUserList(userData); addToUserData(userData);
} }
if (json.metadata.title) { if (json.metadata.defaultTitle) {
updateDefaultTitle(json.metadata.defaultTitle);
}
if (typeof json.metadata.title !== "undefined") {
updateTitle(json.metadata.title); updateTitle(json.metadata.title);
} }
updateColors(json.metadata.color, json.metadata.backColor); updateColors(json.metadata.color, json.metadata.backColor);
} }
}; };
var renameCb = function (err, title) {
if (err) { return; }
APP.title = title;
setTabTitle();
onLocal();
};
var onInit = config.onInit = function (info) { var onInit = config.onInit = function (info) {
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox'); userList = info.userList;
toolbarList = info.userList;
var config = { var config = {
userData: userList, userData: userData,
readOnly: readOnly, readOnly: readOnly,
ifrw: $('#pad-iframe')[0].contentWindow ifrw: ifrw,
title: {
onRename: renameCb,
defaultName: defaultName,
suggestName: suggestName
},
common: Cryptpad
}; };
if (readOnly) {delete config.changeNameID; } if (readOnly) {delete config.changeNameID; }
toolbar = module.toolbar = Toolbar.create($bar, info.myID, info.realtime, info.getLag, info.userList, config); toolbar = module.toolbar = Toolbar.create($bar, info.myID, info.realtime, info.getLag, info.userList, config);
@ -398,13 +432,13 @@ define([
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
} }
// Store the object sent for the "change username" button so that we can update the field value correctly
var userNameButtonObject = module.userName = {};
/* add a "change username" button */ /* add a "change username" button */
getLastName(function (err, lastName) { getLastName(function (err, lastName) {
var usernameCb = function (newName) { userNameButtonObject.lastName = lastName;
setName (newName); var $username = module.$userNameButton = Cryptpad.createButton('username', false, userNameButtonObject, setName).hide();
}; $userBlock.append($username);
var $username = Cryptpad.createButton('username', false, {lastName: lastName}, usernameCb);
$userBlock.append($username).hide();
}); });
/* add an export button */ /* add an export button */
@ -417,14 +451,8 @@ define([
$rightside.append($import); $rightside.append($import);
/* add a rename button */ /* add a rename button */
var renameCb = function (err, title) { //var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb);
if (err) { return; } //$rightside.append($setTitle);
APP.title = title;
setTabTitle();
onLocal();
};
var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb);
$rightside.append($setTitle);
} }
/* add a forget button */ /* add a forget button */
@ -521,6 +549,7 @@ define([
if ($testColor.attr('type') !== "color" || $testColor.val() === '!') { return; } // TODO if ($testColor.attr('type') !== "color" || $testColor.val() === '!') { return; } // TODO
$back.on('click', function() { $back.on('click', function() {
var $picker = $('<input>', { type: 'color', value: backColor }) var $picker = $('<input>', { type: 'color', value: backColor })
.css({ display: 'none', })
.on('change', function() { .on('change', function() {
updateColors(undefined, this.value); updateColors(undefined, this.value);
onLocal(); onLocal();
@ -532,6 +561,7 @@ define([
}); });
$text.on('click', function() { $text.on('click', function() {
var $picker = $('<input>', { type: 'color', value: textColor }) var $picker = $('<input>', { type: 'color', value: textColor })
.css({ display: 'none', })
.on('change', function() { .on('change', function() {
updateColors(this.value, undefined); updateColors(this.value, undefined);
onLocal(); onLocal();
@ -564,14 +594,7 @@ define([
console.error(err); console.error(err);
return; return;
} }
document.title = APP.title = title || info.channel.slice(0, 8); updateTitle(title || defaultName);
Cryptpad.setPadTitle(title, function (err, data) {
if (err) {
console.log("Unable to set pad title");
console.error(err);
return;
}
});
}); });
}; };
@ -591,6 +614,7 @@ define([
var onReady = config.onReady = function (info) { var onReady = config.onReady = function (info) {
var realtime = module.realtime = info.realtime; var realtime = module.realtime = info.realtime;
module.users = info.userList.users;
module.patchText = TextPatcher.create({ module.patchText = TextPatcher.create({
realtime: realtime, realtime: realtime,
//logging: true //logging: true
@ -649,14 +673,16 @@ define([
// Update the toolbar list: // Update the toolbar list:
// Add the current user in the metadata if he has edit rights // Add the current user in the metadata if he has edit rights
if (readOnly) { return; } if (readOnly) { return; }
if (typeof(lastName) === 'string' && lastName.length) {
setName(lastName);
} else {
myData[myID] = { myData[myID] = {
name: "" name: ""
}; };
addToUserList(myData); addToUserData(myData);
if (typeof(lastName) === 'string' && lastName.length) {
setName(lastName);
}
onLocal(); onLocal();
module.$userNameButton.click();
}
}); });
}; };

Loading…
Cancel
Save