diff --git a/customize.dist/BottomBar.html b/customize.dist/BottomBar.html index 9fc1bd9e1..0f97941c8 100644 --- a/customize.dist/BottomBar.html +++ b/customize.dist/BottomBar.html @@ -12,10 +12,6 @@

-

diff --git a/customize.dist/DecorateToolbar.js b/customize.dist/DecorateToolbar.js index d6f77ea30..6ec6eac89 100644 --- a/customize.dist/DecorateToolbar.js +++ b/customize.dist/DecorateToolbar.js @@ -10,6 +10,11 @@ define([ var main = function () { var url = window.location.pathname; var isHtml = /\.html/.test(url) || url === '/' || url === ''; + var isPoll = /\/poll\//.test(url); + if (!isHtml && !isPoll) { + Messages._applyTranslation(); + return; + } $.ajax({ url: isHtml ? '/customize/BottomBar.html' : '/customize/Header.html', success: function (ret) { diff --git a/customize.dist/Header.html b/customize.dist/Header.html index 6d7e2a370..555b004c4 100644 --- a/customize.dist/Header.html +++ b/customize.dist/Header.html @@ -17,6 +17,7 @@

+ XWiki SAS

diff --git a/customize.dist/index.html b/customize.dist/index.html index f66d4dbcf..6c03cb30b 100644 --- a/customize.dist/index.html +++ b/customize.dist/index.html @@ -40,7 +40,7 @@
-

Unity is Strength - Collaboration is Key

+

Unity is Strength - Collaboration is Key

diff --git a/customize.dist/languageSelector.js b/customize.dist/languageSelector.js index af949a813..f857680b3 100644 --- a/customize.dist/languageSelector.js +++ b/customize.dist/languageSelector.js @@ -20,8 +20,8 @@ define(['/bower_components/jquery/dist/jquery.min.js'], function() { return getStoredLanguage() || getBrowserLanguage(); }; - var main = out.main = function () { - var selector = $('#language-selector'); + var main = out.main = function ($select) { + var selector = $select || $('#language-selector'); if (!selector.length) { return; } // Select the current language in the list diff --git a/customize.dist/messages.js b/customize.dist/messages.js index 21cb58e6f..730af9629 100644 --- a/customize.dist/messages.js +++ b/customize.dist/messages.js @@ -24,6 +24,16 @@ define(['/customize/languageSelector.js', 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 () { var missing = []; Object.keys(map).forEach(function (code) { @@ -35,6 +45,10 @@ define(['/customize/languageSelector.js', missing.push(warning); } }); + if (typeof(translation._languageName) !== 'string') { + var warning = 'key [_languageName] is missing from translation [' + code + ']'; + missing.push(warning); + } }); return missing; }; diff --git a/customize.dist/src/toolbar.less b/customize.dist/src/toolbar.less index 17e25c232..d8f7b6a6c 100644 --- a/customize.dist/src/toolbar.less +++ b/customize.dist/src/toolbar.less @@ -14,8 +14,8 @@ .unselectable; - color: #666; - font-weight: bold; + font: normal normal normal 12px Arial,Helvetica,Tahoma,Verdana,Sans-Serif; + color: #000; display: inline-block; width: 100%; z-index: 9001; @@ -33,13 +33,13 @@ color: #000; } &.cryptpad-lag { - float: right; + float: left; line-height: 26px; - margin: 2px 0px 2px 4px; + margin: 2px 0px; } } - button, .rightside-element { + button, select, .rightside-element { height: 26px; padding-right: 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 { float: left; margin-bottom: -1px; + .cryptpad-user-list { + float: right; + } .cryptpad-dropdown-container { position: relative; display: inline-block; @@ -130,10 +249,7 @@ white-space: normal; &.cryptpad-dropdown-users { text-align:baseline; - .yourself { - font-style: italic; - } - .anonymous { + .yourself, .anonymous, .viewer { font-style: italic; } } @@ -160,7 +276,6 @@ } .cryptpad-toolbar-rightside { text-align: right; - margin-right: 30px; //float: right; } .cryptpad-spinner { @@ -172,7 +287,7 @@ font-size: 20px; } .cryptpad-readonly { - margin-right: 20px; + margin-right: 5px; font-weight: bold; text-transform: uppercase; } diff --git a/customize.dist/toolbar.css b/customize.dist/toolbar.css index 930a17eaf..cfde957a3 100644 --- a/customize.dist/toolbar.css +++ b/customize.dist/toolbar.css @@ -14,8 +14,8 @@ -moz-user-select: none; -ms-user-select: none; user-select: none; - color: #666; - font-weight: bold; + font: normal normal normal 12px Arial, Helvetica, Tahoma, Verdana, Sans-Serif; + color: #000; display: inline-block; width: 100%; z-index: 9001; @@ -33,11 +33,12 @@ color: #000; } .cryptpad-toolbar div.cryptpad-lag { - float: right; + float: left; line-height: 26px; - margin: 2px 0px 2px 4px; + margin: 2px 0px; } .cryptpad-toolbar button, +.cryptpad-toolbar select, .cryptpad-toolbar .rightside-element { height: 26px; padding-right: 5px; @@ -101,10 +102,137 @@ border: 1px solid #A6A6A6; 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 { float: left; margin-bottom: -1px; } +.cryptpad-toolbar-leftside .cryptpad-user-list { + float: right; +} .cryptpad-toolbar-leftside .cryptpad-dropdown-container { position: relative; display: inline-block; @@ -131,10 +259,9 @@ .cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p.cryptpad-dropdown-users { text-align: baseline; } -.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 .yourself, +.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 { font-style: italic; } .cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p h2 { @@ -156,7 +283,6 @@ } .cryptpad-toolbar-rightside { text-align: right; - margin-right: 30px; } .cryptpad-spinner { float: left; @@ -167,7 +293,7 @@ font-size: 20px; } .cryptpad-readonly { - margin-right: 20px; + margin-right: 5px; font-weight: bold; text-transform: uppercase; } diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 3d4f85196..0ce5f0c03 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -1,7 +1,10 @@ define(function () { var out = {}; + out._languageName = "Français"; + 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.pad = 'Pad'; @@ -26,8 +29,14 @@ define(function () { out.yourself = "Vous-même"; out.anonymousUsers = "utilisateurs anonymes"; out.anonymousUser = "utilisateur anonyme"; - out.share = "Partage"; + out.shareView = "URL de lecture seule"; + out.shareEdit = "URL d'édition"; out.users = "Utilisateurs"; + out.and = "Et"; + out.viewer = "lecteur"; + out.viewers = "lecteurs"; + out.editor = "éditeur"; + out.editors = "éditeurs"; out.greenLight = "Tout fonctionne bien"; 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.renamePrompt = 'Quel titre souhaitez-vous utiliser pour ce document ?'; out.renameConflict = 'Un autre document existe déjà avec le même titre'; + out.clickToEdit = 'Cliquer pour modifier'; out.forgetButton = 'OUBLIER'; 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.shareButton = 'PARTAGER'; + out.shareButton = 'Partager'; out.shareButtonTitle = "Copier l'URL 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"; @@ -79,15 +89,17 @@ define(function () { out.readonlyUrl = 'Document en lecture seule'; out.copyReadOnly = "Copier l'URL dans le presse-papiers"; out.openReadOnly = "Ouvrir dans un nouvel onglet"; - out.editing = "éditeur(s)"; - out.viewing = "lecteur(s)"; - out.editShare = "Partager l'URL"; + out.editShare = "Partager l'URL d'édition"; out.editShareTitle = "Copier l'URL d'édition dans le presse-papiers"; out.viewShare = "Partager l'URL de lecture"; out.viewShareTitle = "Copier l'URL d'accès en lecture seule dans le presse-papiers"; out.viewOpen = "Voir 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.tryIt = 'Essayez-le !'; @@ -147,7 +159,7 @@ define(function () { // index.html - out.main_p1 = 'CryptPad est l\'éditeur collaboratif en temps réel zero knowledge. 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\'identifieur de fragment 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 zero knowledge. 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\'identifieur de fragment 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) CKEditor, l\'éditeur de code source CodeMirror, et le moteur temps-réel ChainPad.'; out.main_howitworks = 'Comment ça fonctionne'; out.main_howitworks_p1 = 'CryptPad utilise une variante de l\'algorithme d\'Operational transformation qui est capable de trouver un consensus distribué en utilisant une chaîne de bloc Nakamoto, un outil popularisé par le Bitcoin. 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.'; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 2b2a7a0cb..d96c78b39 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -1,7 +1,10 @@ define(function () { var out = {}; + out._languageName = 'English'; + out.main_title = "Cryptpad: Zero Knowledge, Collaborative Real Time Editing"; + out.main_slogan = "Unity is Strength - Collaboration is Key"; out.type = {}; out.type.pad = 'Pad'; @@ -26,8 +29,14 @@ define(function () { out.yourself = "Yourself"; out.anonymousUsers = "anonymous users"; out.anonymousUser = "anonymous user"; - out.share = "Share"; + out.shareView = "Read-only URL"; + out.shareEdit = "Edit URL"; 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.orangeLight = "Your slow connection may impact your experience"; @@ -45,18 +54,19 @@ define(function () { out.userButton = 'USER'; out.userButtonTitle = 'Change your username'; - out.changeNamePrompt = 'Change your name: '; + out.changeNamePrompt = 'Change your name (leave empty to be anonymous): '; out.renameButton = 'RENAME'; 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.renameConflict = 'Another pad already has that title'; + out.clickToEdit = "Click to edit"; out.forgetButton = 'FORGET'; 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.shareButton = 'SHARE'; + out.shareButton = 'Share'; out.shareButtonTitle = "Copy URL to clipboard"; out.shareSuccess = 'Copied URL to clipboard'; out.shareFailed = "Failed to copy URL to clipboard"; @@ -79,15 +89,17 @@ define(function () { out.readonlyUrl = 'Read only document'; out.copyReadOnly = "Copy URL to clipboard"; out.openReadOnly = "Open in a new tab"; - out.editing = "editing"; - out.viewing = "viewing"; - out.editShare = "Share"; + out.editShare = "Share edit URL"; out.editShareTitle = "Copy the edit URL to clipboard"; out.viewShare = "Share view URL"; out.viewShareTitle = "Copy the read-only URL to clipboard"; out.viewOpen = "View in 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.tryIt = 'Try it out!'; @@ -204,7 +216,10 @@ define(function () { // Header.html out.header_france = 'With from France by XWiki SAS'; - out.header_xwiki = 'XWiki SAS'; + + + // TODO Hardcode cause YOLO + //out.header_xwiki = 'XWiki SAS'; out.header_support = ' OpenPaaS-ng'; out.header_logoTitle = 'Go to the main page'; diff --git a/www/code/index.html b/www/code/index.html index bdef6b6f4..9b7c92d88 100644 --- a/www/code/index.html +++ b/www/code/index.html @@ -21,7 +21,7 @@ } #iframe-container { position: fixed; - top: 2.6em; + top: 0px; bottom: 0px; right: 0px; left: 0px; diff --git a/www/code/main.js b/www/code/main.js index 1e4a11758..ee4fd2f0f 100644 --- a/www/code/main.js +++ b/www/code/main.js @@ -45,10 +45,13 @@ define([ var andThen = function (CMeditor) { var CodeMirror = module.CodeMirror = CMeditor; CodeMirror.modeURL = "/bower_components/codemirror/mode/%N/%N.js"; - var $pad = $('#pad-iframe'); 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], { lineNumbers: true, lineWrapping: true, @@ -112,12 +115,22 @@ define([ editor.setOption('readOnly', !bool); }; - var userList = {}; // 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 addToUserList = function(data) { - for (var attrname in data) { userList[attrname] = data[attrname]; } - if(toolbarList && typeof toolbarList.onChange === "function") { - toolbarList.onChange(userList); + var userData = module.userData = {}; // List of pretty name of all users (mapped with their server ID) + var userList; // List of users still connected to the channel (server IDs) + var addToUserData = function(data) { + var users = module.users; + for (var attrname in data) { userData[attrname] = data[attrname]; } + + if (users && users.length) { + for (var userKey in userData) { + if (users.indexOf(userKey) === -1) { + delete userData[userKey]; + } + } + } + + if(userList && typeof userList.onChange === "function") { + userList.onChange(userData); } }; @@ -152,27 +165,30 @@ define([ var initializing = true; + var stringifyInner = function (textValue) { + var obj = { + content: textValue, + metadata: { + users: userData, + defaultTitle: defaultName + } + }; + obj.metadata.title = document.title; + // set mode too... + obj.highlightMode = module.highlightMode; + + // stringify the json and send it into chainpad + return stringify(obj); + }; + var onLocal = config.onLocal = function () { if (initializing) { return; } if (readOnly) { return; } editor.save(); - var textValue = canonicalize($textarea.val()); - var obj = {content: textValue}; - - // append the userlist to the hyperjson structure - obj.metadata = { - users: userList - }; - if (!isDefaultTitle()) { - obj.metadata.title = document.title; - } - - // set mode too... - obj.highlightMode = module.highlightMode; - // stringify the json and send it into chainpad - var shjson = stringify(obj); + var textValue = canonicalize($textarea.val()); + var shjson = stringifyInner(textValue); module.patchText(shjson); @@ -191,13 +207,14 @@ define([ myData[myID] = { name: myUserName }; - addToUserList(myData); + addToUserData(myData); Cryptpad.setAttribute('username', myUserName, function (err, data) { if (err) { console.log("Couldn't set username"); console.error(err); return; } + module.userName.lastName = myUserName; onLocal(); }); }; @@ -233,10 +250,10 @@ define([ } // lines including a c-style comment are also valuable - var clike = /^\s*(\/\*|\/\/)(.*?)(\*\/)$/; + var clike = /^\s*(\/\*|\/\/)(.*)?(\*\/)*$/; if (clike.test(line)) { line.replace(clike, function (a, one, two) { - text = two; + text = two.replace(/\*\/\s*$/, '').trim(); }); return true; } @@ -246,13 +263,10 @@ define([ }; var suggestName = function () { - var parsed = Cryptpad.parsePadUrl(window.location.href); - var name = Cryptpad.getDefaultName(parsed, []); - - if (Cryptpad.isDefaultName(parsed, document.title)) { - return getHeadingText() || document.title; + if (document.title === defaultName) { + return getHeadingText() || ""; } else { - return document.title || getHeadingText() || name; + return document.title || getHeadingText() || defaultName; } }; @@ -298,16 +312,68 @@ define([ onLocal(); }; - var onInit = config.onInit = function (info) { - var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox'); - toolbarList = info.userList; + 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) { + userList = info.userList; var config = { - userData: userList, + userData: userData, readOnly: readOnly, - ifrw: ifrw + ifrw: ifrw, + title: { + onRename: renameCb, + defaultName: defaultName, + suggestName: suggestName + }, + common: Cryptpad }; 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 $userBlock = $bar.find('.' + Toolbar.constants.username); @@ -321,13 +387,13 @@ define([ 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 */ getLastName(function (err, lastName) { - var usernameCb = function (newName) { - setName (newName); - }; - var $username = Cryptpad.createButton('username', false, {lastName: lastName}, usernameCb); - $userBlock.append($username).hide(); + userNameButtonObject.lastName = lastName; + var $username = module.$userNameButton = Cryptpad.createButton('username', false, userNameButtonObject, setName).hide(); + $userBlock.append($username); }); /* add an export button */ @@ -340,13 +406,8 @@ define([ $rightside.append($import); /* add a rename button */ - var renameCb = function (err, title) { - if (err) { return; } - document.title = title; - onLocal(); - }; - var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb); - $rightside.append($setTitle); + //var $setTitle = Cryptpad.createButton('rename', true, {suggestName: suggestName}, renameCb); + //$rightside.append($setTitle); } /* add a forget button */ @@ -444,47 +505,10 @@ define([ console.error(err); return; } - document.title = title || info.channel.slice(0, 8); - Cryptpad.setPadTitle(title, function (err, data) { - if (err) { - console.log("Unable to set pad title"); - console.error(err); - return; - } - }); + updateTitle(title || defaultName); }); }; - 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 () { if (module.tabNotification && typeof(module.tabNotification.cancel) === 'function') { @@ -501,6 +525,7 @@ define([ var onReady = config.onReady = function (info) { var realtime = module.realtime = info.realtime; + module.users = info.userList.users; module.patchText = TextPatcher.create({ realtime: realtime, //logging: true @@ -549,14 +574,16 @@ define([ // Update the toolbar list: // Add the current user in the metadata if he has edit rights if (readOnly) { return; } - myData[myID] = { - name: "" - }; - addToUserList(myData); if (typeof(lastName) === 'string' && lastName.length) { setName(lastName); + } else { + myData[myID] = { + name: "" + }; + addToUserData(myData); + onLocal(); + module.$userNameButton.click(); } - onLocal(); }); }; @@ -628,18 +655,8 @@ define([ editor.scrollTo(scroll.left, scroll.top); if (!readOnly) { - var localDoc = canonicalize($textarea.val()); - var hjson2 = { - content: localDoc, - metadata: { - users: userList - }, - highlightMode: highlightMode, - }; - if (!isDefaultTitle()) { - hjson2.metadata.title = document.title; - } - var shjson2 = stringify(hjson2); + var textValue = canonicalize($textarea.val()); + var shjson2 = stringifyInner(textValue); if (shjson2 !== shjson) { console.error("shjson2 !== shjson"); TextPatcher.log(shjson, TextPatcher.diff(shjson, shjson2)); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 4fdde73e6..d28b1e5e5 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -277,15 +277,15 @@ define([ }; var parsePadUrl = common.parsePadUrl = function (href) { - var patt = /^https*:\/\/([^\/]*)\/(.*?)\/#(.*)$/i; + var patt = /^https*:\/\/([^\/]*)\/(.*?)\//i; var ret = {}; - href.replace(patt, function (a, domain, type, hash) { + var hash = href.replace(patt, function (a, domain, type, hash) { ret.domain = domain; ret.type = type; - ret.hash = hash; return ''; }); + ret.hash = hash.replace(/#/g, ''); return ret; }; @@ -303,13 +303,17 @@ define([ var type = parsed.type; var untitledIndex = 1; var name = (Messages.type)[type] + ' - ' + new Date().toString().split(' ').slice(0,4).join(' '); - if (isNameAvailable(name, parsed, recentPads)) { return name; } - while (!isNameAvailable(name + ' - ' + untitledIndex, parsed, recentPads)) { untitledIndex++; } - return name + ' - ' + untitledIndex; + 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; } + while (!isNameAvailable(name + ' - ' + untitledIndex, parsed, recentPads)) { untitledIndex++; } + return name + ' - ' + untitledIndex; + */ }; var isDefaultName = common.isDefaultName = function (parsed, title) { var name = getDefaultName(parsed, []); - return title.slice(0, name.length) === name; + return title === name; }; var makePad = function (href, title) { @@ -620,6 +624,47 @@ define([ /* * 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 button; var size = "17px"; @@ -658,33 +703,9 @@ define([ button.click(function() { var suggestion = suggestName(); - common.prompt(Messages.renamePrompt, - suggestion, function (title, ev) { - if (title === null) { return; } - - common.causesNamingConflict(title, function (err, conflicts) { - if (err) { - console.log("Unable to determine if name caused a conflict"); - console.error(err); - callback(err, title); - return; - } - - if (conflicts) { - common.alert(Messages.renameConflict); - return; - } - - common.setPadTitle(title, function (err, data) { - if (err) { - console.log("unable to set pad title"); - console.log(err); - return; - } - callback(null, title); - }); - }); - }); + common.prompt(Messages.renamePrompt, suggestion, function (title, ev) { + renamePad(title, callback); + }); }); } break; @@ -720,9 +741,8 @@ define([ title: Messages.userButton + '\n' + Messages.userButtonTitle }).html(''); if (data && typeof data.lastName !== "undefined" && callback) { - var lastName = data.lastName; button.click(function() { - common.prompt(Messages.changeNamePrompt, lastName, function (newName) { + common.prompt(Messages.changeNamePrompt, data.lastName, function (newName) { callback(newName); }); }); diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 943e27df5..2fc0c112d 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -17,6 +17,7 @@ define([ /** 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'; @@ -34,6 +35,8 @@ define([ var VIEWSHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-viewShare"; 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"; /** Key in the localStore which indicates realtime activity should be disallowed. */ // TODO remove? will never be used in cryptpad @@ -49,6 +52,7 @@ define([ var $style; var firstConnection = true; + var lagErrors = 0; var styleToolbar = function ($container, href) { href = href || '/customize/toolbar.css'; @@ -67,6 +71,7 @@ define([ 'class': TOOLBAR_CLS, id: uid(), }) + .append($('
', {'class': TOP_CLS})) .append($('
', {'class': LEFTSIDE_CLS})) .append($('
', {'class': RIGHTSIDE_CLS})); @@ -100,34 +105,6 @@ define([ 'class': USERBUTTONS_CONTAINER_CLS }).appendTo($userlistElement); - var $editIcon = $('