merge staging branch

pull/1/head
ansuz 7 years ago
commit 7944bc1a82

@ -249,6 +249,14 @@ module.exports = {
*/
pinPath: './pins',
/* Pads that are not 'pinned' by any registered user can be set to expire
* after a configurable number of days of inactivity (default 90 days).
* The value can be changed or set to false to remove expiration.
* Expired pads can then be removed using a cron job calling the
* `delete-inactive.js` script with node
*/
inactiveTime: 90, // days
/* CryptPad allows logged in users to upload encrypted files. Files/blobs
* are stored in a 'blob-store'. Set its location here.
*/

@ -191,8 +191,10 @@ define([
window.location.href = '/drive/';
};
var hashing;
Exports.loginOrRegisterUI = function (uname, passwd, isRegister, shouldImport, testing, test) {
var hashing = true;
if (hashing) { return void console.log("hashing is already in progress"); }
hashing = true;
var proceed = function (result) {
hashing = false;
@ -275,7 +277,7 @@ define([
proceed(result);
});
}, 0);
}, 500);
}, 200);
};

@ -72,7 +72,7 @@ define([
])
])
]),
h('div.cp-version-footer', "CryptPad v1.26.0 (undefined)")
h('div.cp-version-footer', "CryptPad v1.27.0 (null)")
]);
};
@ -98,9 +98,11 @@ define([
]),
h('a.navbar-brand', { href: '/index.html'}),
h('div.collapse.navbar-collapse.justify-content-end#menuCollapse', [
h('a.nav-item.nav-link', { href: '/what-is-cryptpad.html'}, Msg.topbar_whatIsCryptpad),
//h('a.nav-item.nav-link', { href: '/what-is-cryptpad.html'}, Msg.topbar_whatIsCryptpad), // Moved the FAQ
h('a.nav-item.nav-link', { href: '/faq.html'}, Msg.faq_link),
h('a.nav-item.nav-link', { href: 'https://blog.cryptpad.fr/'}, Msg.blog),
h('a.nav-item.nav-link', { href: '/features.html'}, Msg.features),
h('a.nav-item.nav-link', { href: '/privacy.html'}, Msg.privacy),
h('a.nav-item.nav-link', { href: '/contact.html'}, Msg.contact),
h('a.nav-item.nav-link', { href: '/about.html'}, Msg.about),
].concat(rightLinks))
@ -350,7 +352,7 @@ define([
h('div.container.cp-container', [
h('center', h('h1', Msg.policy_title)),
h('h2', Msg.policy_whatweknow),
h('p', Msg.policywhatweknow_p1),
setHTML(h('p'), Msg.policy_whatweknow_p1),
h('h2', Msg.policy_howweuse),
h('p', Msg.policy_howweuse_p1),
@ -403,6 +405,9 @@ define([
infopageTopbar(),
h('div.container.cp-container', [
h('center', h('h1', Msg.faq_title)),
h('p.cp-faq-header', h('a.nav-item.nav-link', {
href: '/what-is-cryptpad.html'
}, Msg.faq_whatis)),
h('div.cp-faq-container', categories)
]),
infopageFooter()
@ -756,10 +761,14 @@ define([
Pages['/whiteboard/'] = Pages['/whiteboard/index.html'] = function () {
return [
appToolbar(),
h('div#cp-app-whiteboard-canvas-area', h('canvas#cp-app-whiteboard-canvas', {
h('div#cp-app-whiteboard-canvas-area',
h('div#cp-app-whiteboard-container',
h('canvas#cp-app-whiteboard-canvas', {
width: 600,
height: 600
})),
})
)
),
h('div#cp-app-whiteboard-controls', {
style: {
display: 'block',
@ -819,12 +828,6 @@ define([
appToolbar(),
h('div#cp-app-poll-content', [
h('div#cp-app-poll-form', [
h('div#cp-app-poll-help', [
h('h1', 'CryptPoll'),
setHTML(h('h2'), Msg.poll_subtitle),
h('p', Msg.poll_p_save),
h('p', Msg.poll_p_encryption)
]),
h('div.cp-app-poll-realtime', [
h('br'),
h('div', [

@ -3,28 +3,9 @@
.cke_reset_all * {
color: inherit;
}
#cke_editor1 .cke_inner {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: flex;
flex-flow: column;
}
.cke_toolbox_main {
display: inline-block;
}
#cke_1_contents {
flex: 1;
margin-top: -1px;
display: flex;
overflow: visible;
iframe {
min-height: 100%;
width: 100%;
}
}
.cke_toolbox .cp-toolbar-history {
input.gotoInput { // TODO
padding: 3px 3px;

@ -44,11 +44,11 @@
@colortheme_pad-bg: #1c4fa0;
@colortheme_pad-color: #fff;
@colortheme_pad-toolbar-bg: #c1e7ff;
@colortheme_pad-warn: #F83A3A;
@colortheme_pad-warn: #ffae00;
@colortheme_slide-bg: #e57614;
@colortheme_slide-color: #fff;
@colortheme_slide-warn: #58D697;
@colortheme_slide-warn: #005868;
@colortheme_code-bg: #ffae00;
@colortheme_code-color: #000;
@ -59,7 +59,7 @@
@colortheme_poll-help-bg: #bbffbb;
@colortheme_poll-th-bg: #005bef;
@colortheme_poll-th-fg: #fff;
@colortheme_poll-warn: #ffae00;
@colortheme_poll-warn: #ffade3;
@colortheme_whiteboard-bg: #800080;
@colortheme_whiteboard-color: #fff;

@ -25,6 +25,7 @@
text-align: center;
font: @colortheme_app-font;
width: 100%;
outline: none;
& > div {
width: 60vw;
max-width: 100%;
@ -75,7 +76,7 @@
}
}
.cp-creation-create {
.cp-creation-create, .cp-creation-settings {
button {
.tools_unselectable();
padding: 15px;
@ -84,9 +85,14 @@
margin: 3px 10px;
border: none;
cursor: pointer;
outline: none;
&:hover {
background: darken(@colortheme_loading-bg, 5%);
}
&.cp-creation-button-selected {
color: darken(@colortheme_loading-bg, 10%);
background: @colortheme_loading-color;
}
}
}
@ -159,6 +165,9 @@
color: lighten(#0275d8, 10%);
}
}
&> span.fa {
margin-left: 15px;
}
}
.cp-creation-deleted {
background: #111;

@ -0,0 +1,38 @@
@import (once) "./colortheme-all.less";
.help_main (@color, @bg-color) {
.cp-help-container {
position: relative;
background-color: lighten(@bg-color, 15%);
&.cp-help-hidden {
display: none;
}
.cp-help-close {
position: absolute;
top: 5px;
right: 5px;
}
.cp-help-text {
color: @color;
margin: 0;
padding: 15px;
a {
color: darken(@colortheme_link-color, 30%);
@spin: spin(lighten(@bg-color, 15%), 180);
color: contrast(lighten(@bg-color, 15%), lighten(@spin, 10%), darken(@spin, 10%));
//color: darken(spin(lighten(@bg-color, 15%), 180), 10%);
}
h1 {
font-size: 20px;
}
h2 {
font-size: 18px;
}
h3 {
font-size: 16px;
}
ul, ol, p { margin: 0; }
}
}
}

@ -25,6 +25,10 @@
text-overflow: ellipsis;
word-wrap: break-word;
}
&.cp-icons-element-selected {
background-color: white;
color: #666;
}
.fa {
display: block;
font-size: 64px;

@ -0,0 +1,20 @@
@import (once) "./colortheme-all.less";
.markdownToolbar_main (@color, @bg-color) {
.cp-markdown-toolbar {
height: @toolbar_line-height;
background-color: lighten(@bg-color, 20%);
display: none;
button {
height: @toolbar_line-height !important;
outline: 0;
color: @color;
.toolbar_button;
font: normal normal normal 14px/1 FontAwesome;
&:hover {
background-color: lighten(@bg-color, 8%);
}
&.cp-markdown-help { float: right; }
}
}
}

@ -8,6 +8,8 @@
@import (once) "./tools.less";
@import (once) "./icons.less";
@import (once) "./modal.less";
@import (once) "./markdown-toolbar.less";
@import (once) "./help.less";
.toolbar_main (
@color: @colortheme_default-color, // Color of the text for the toolbar
@ -24,6 +26,8 @@
.ckeditor_fix();
.history_main();
.iconColors_main();
.markdownToolbar_main(@color, @bg-color);
.help_main(@color, @bg-color);
.cp-toolbar-container {
display: flex;
@ -208,6 +212,7 @@
width: auto;
margin: 0;
padding: 0;
outline: none;
}
label[for="cp-app-toolbar-creation-advanced"] {
margin: 0;
@ -238,23 +243,6 @@
}
}
// TODO(cjd) This ought to be in a less file for markdown-based editors
.cp-markdown-toolbar {
height: @toolbar_line-height;
background-color: lighten(@bg-color, 20%);
display: none;
button {
height: @toolbar_line-height !important;
outline: 0;
color: @color;
.toolbar_button;
font: normal normal normal 14px/1 FontAwesome;
&:hover {
background-color: lighten(@bg-color, 8%);
}
&.cp-markdown-help { float: right; }
}
}
.cp-toolbar-userlist-drawer {
background-color: @bg-color;
color: @color;
@ -356,11 +344,6 @@
margin: 0;
}*/
.cp-toolbar-rightside-button {
float: right;
cursor: pointer;
}
select {
margin-left: 5px;
margin-right: 5px;
@ -379,11 +362,104 @@
flex: 1;
}
}
&:not(.cp-toolbar-notitle) {
.cp-toolbar-top {
@media screen and (max-width: @browser_media-medium-screen) {
flex-wrap: wrap;
height: auto;
height: @toolbar_line-height;
.cp-pad-not-pinned {
line-height: 32px;
flex: unset;
padding: 0;
align-self: auto;
margin: 0 5px;
}
.cp-toolbar-top-filler {
height: 32px;
}
.cp-toolbar-title {
height: @toolbar_line-height;
line-height: initial;
margin: 0;
.cp-toolbar-title-hoverable {
width: 100%;
}
.cp-toolbar-title-value-page {
padding: 5px;
line-height: unset;
border: 0;
}
.cp-toolbar-title-editable, .cp-toolbar-title-value-page {
max-width: ~"calc(100vw - 26px)";
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
font-size: @colortheme_app-font-size;
height: @toolbar_line-height;
box-sizing: border-box;
line-height: 20px;
}
.cp-toolbar-title-edit, .cp-toolbar-title-save {
box-sizing: border-box;
height: @toolbar_line-height;
line-height: @colortheme_app-font-size;
display: inline-block;
.fa {
font-size: @colortheme_app-font-size;
}
}
input {
height: @toolbar_line-height;
font-size: @colortheme_app-font-size;
flex: 1;
max-width: none;
line-height: calc(@toolbar_line-height - 12px); // padding + border
}
}
.cp-toolbar-link {
height: @toolbar_line-height;
width: @toolbar_line-height;
.cp-toolbar-link-logo {
padding: 5px;
}
}
.cp-toolbar-user {
height: @toolbar_line-height;
.cp-toolbar-new {
height: @toolbar_line-height;
width: @toolbar_line-height;
margin-left: 0;
button {
height: @toolbar_line-height;
width: @toolbar_line-height;
font-size: 20px;
margin-top: -1px;
}
}
.cp-toolbar-user-dropdown {
height: @toolbar_line-height;
width: @toolbar_line-height;
&> button {
height: @toolbar_line-height;
width: @toolbar_line-height;
span { font-size: unset; }
}
&> button.cp-avatar.cp-avatar {
media-tag {
margin: 4px;
max-width: 24px;
min-width: 24px;
max-height: 24px;
min-height: 24px;
}
}
}
.cp-toolbar-limit {
line-height: 32px;
margin: 0;
}
}
/*
.cp-toolbar-top-filler {
flex: 1;
}
@ -422,9 +498,10 @@
font-size: @colortheme_app-font-size;
flex: 1;
max-width: none;
line-height: calc(@toolbar_line-height - 12px); // padding + border
}
}
}
*/
}
}
@ -442,13 +519,6 @@
background-color: @bg-color;
}
}
.cp-toolbar-rightside {
@media screen and (max-width: @barWidth) { // 450px
flex-wrap: wrap;
height: auto;
width: 100%;
}
}
.cp-toolbar-title-hoverable:hover {
.cp-toolbar-title-editable, .cp-toolbar-title-edit {
cursor: text;
@ -494,9 +564,9 @@
overflow: hidden;
white-space: nowrap;
align-self: center;
padding-left: 20px;
padding-right: 5px;
line-height: @toolbar_top-height;
padding: 0;
margin: 0 5px;
font-size: @colortheme_app-font-size;
color: @warn-color;
.cp-pnp-msg {
@ -505,6 +575,7 @@
font-size: @colortheme_app-font-size;
a {
font-size: @colortheme_app-font-size;
font-family: @colortheme_font;
font-weight: bold;
color: @warn-color;
&:hover {
@ -593,7 +664,7 @@
}
input {
max-width: ~"calc(100% - 40px)";
flex: 1;
//flex: 1;
vertical-align: middle;
box-sizing: border-box;
cursor: auto;
@ -601,6 +672,7 @@
font-size: 20px;
padding: 5px 5px;
height: 40px;
line-height: 28px; // padding + border
}
}
.cp-toolbar-link, .cp-toolbar-new {
@ -792,16 +864,41 @@
}
.cp-toolbar-share-button {
width: 50px;
text-align: center;
}
}
.cp-toolbar-rightside {
display: flex;
min-height: @toolbar_line-height;
overflow: hidden;
@media screen and (max-width: @barWidth) { // 450px
flex-wrap: wrap;
height: auto;
width: 100%;
}
&:empty {
min-height: 0;
height: 0;
}
text-align: right;
.cp-toolbar-rightside-button {
cursor: pointer;
// UI actions
&.cp-toolbar-icon-toggle { order: 1; }
&.cp-toolbar-icon-preview { order: 2; }
&.cp-toolbar-icon-present { order: 3; }
// Content actions
&.cp-toolbar-icon-mediatag { order: 10; }
order: 11;
// Storage actions
&.cp-toolbar-icon-hashtag { order: 20; }
&.cp-toolbar-icon-template { order: 21; }
&.cp-toolbar-icon-forget { order: 22; }
// Drawer
&.cp-toolbar-drawer-button { order: 30; }
}
.cp-toolbar-drawer-content:empty ~ .cp-toolbar-drawer-button {
display: none;
}
@ -820,6 +917,7 @@
font-size: 17px;
}
&> span {
order: 8;
box-sizing: border-box;
min-width: 150px;
height: @toolbar_line-height;
@ -834,15 +932,36 @@
border: 0;
width: 100%;
line-height: 1em;
&.cp-toolbar-button-active {
background-color: inherit;
}
.cp-toolbar-drawer-element {
margin-left: 10px;
display: inline;
vertical-align: top;
vertical-align: baseline;
}
&.fa-info-circle, &.fa-history, &.fa-cog {
.cp-toolbar-drawer-element {
margin-left: 11px;
}
}
&.fa-question {
.cp-toolbar-drawer-element {
margin-left: 16px;
}
}
&:hover {
background-color: @colortheme_dropdown-bg-hover !important;
color: @colortheme_dropdown-color;
}
order: 8;
&.fa-history { order: 1; }
&.fa-download { order: 2; }
&.fa-upload { order: 3; }
&.fa-print { order: 4; }
&.fa-cog { order: 5; }
&.fa-info-circle { order: 6; }
&.fa-help { order: 7; }
}
}
}

@ -4,6 +4,13 @@
.infopages_main();
.infopages_topbar();
.cp-faq-header {
padding: 0;
font-size: 1.2em;
a {
padding: 0;
}
}
.cp-faq-container {
.cp-faq-questions-q {
color: #3a84b6;

@ -714,13 +714,8 @@ define(function () {
'<p>',
'Αυτό είναι&nbsp;<strong>CryptPad</strong>, ο συνεργατικός επεξεργαστής πραγματικού χρόνου Zero Knowledge. Τα πάντα αποθηκεύονται καθώς πληκτρολογείτε.',
'<br>',
'Μοιραστείτε τον σύνδεσμο σε αυτό το pad για να το επεξεργαστείτε με φίλους ή χρησιμοποιήστε το κουμπί <span class="fa fa-share-alt" style="border: 1px solid black;color:#000;">&nbsp;Share&nbsp;</span> για να μοιραστείτε ένα κείμενο με δικαιώματα <em>read-only link</em>&nbsp;το οποίο επιτρέπει να το αναγνώσει κάποιος αλλά όχι να το επεξεργαστεί.',
'Μοιραστείτε τον σύνδεσμο σε αυτό το pad για να το επεξεργαστείτε με φίλους ή χρησιμοποιήστε το κουμπί <span class="fa fa-share-alt"></span> για να μοιραστείτε ένα κείμενο με δικαιώματα <em>read-only link</em>&nbsp;το οποίο επιτρέπει να το αναγνώσει κάποιος αλλά όχι να το επεξεργαστεί.',
'</p>',
'<p><em>',
'Εμπρός, απλά ξεκινήστε να πληκτρολογείτε...',
'</em></p>',
'<p>&nbsp;<br></p>'
].join('');
out.codeInitialState = [
@ -732,14 +727,6 @@ define(function () {
out.slideInitialState = [
'# CryptSlide\n',
'* Αυτός είναι ένας συνεργατικός επεξεργαστής πραγματικού χρόνου με τεχνολογία zero knowledge.\n',
'* Ό,τι πληκτρολογείτε εδώ είναι κρυπτογραφημένο έτσι ώστε μόνο οι άνθρωποι που έχουν τον σύνδεσμο να μπορούν να έχουν πρόσβαση.\n',
'* Ακόμη κι ο διακομιστής δεν μπορεί να δει τι πληκτρολογείτε.\n',
'* Ό,τι δείτε εδώ, ό,τι ακούσετε εδώ, όταν φύγετε από εδώ, θα παραμείνει εδώ.\n',
'\n',
'---',
'\n',
'# Πως να το χρησιμοποιήσετε\n',
'1. Γράψτε τα περιεχόμενα των slides σας χρησιμοποιώντας σύνταξη markdown\n',
' - Μάθετε περισσότερα για την σύνταξη markdown [εδώ](http://www.markdowntutorial.com/)\n',
'2. Διαχωρίστε τα slides σας με ---\n',

@ -294,17 +294,12 @@ define(function () {
'<p>',
'Esto es&nbsp;<strong>CryptPad</strong>, el editor colaborativo en tiempo real Zero Knowledge. Todo está guardado cuando escribes.',
'<br>',
'Comparte el enlace a este pad para editar con amigos o utiliza el botón <span class="fa fa-share-alt" style="border: 1px solid black;color:#000;">&nbsp;Compartir&nbsp;</span> para obtener un <em>enlace sólo lectura</em>&nbsp;que permite leer pero no escribir.',
'Comparte el enlace a este pad para editar con amigos o utiliza el botón <span class="fa fa-share-alt"></span> para obtener un <em>enlace sólo lectura</em>&nbsp;que permite leer pero no escribir.',
'</p>',
'<p><em>',
'Vamos, empieza a escribir...',
'</em></p>',
'<p>&nbsp;<br></p>'
].join('');
out.codeInitialState = "/*\n Esto es CryptPad, el editor colaborativo en tiempo real zero knowledge.\n Lo que escribes aquí está cifrado de manera que sólo las personas con el enlace pueden acceder a ello.\n Incluso el servidor no puede ver lo que escribes.\n Lo que ves aquí, lo que escuchas aquí, cuando sales, se queda aquí\n*/";
out.slideInitialState = "# CryptSlide\n* Esto es CryptPad, el editor colaborativo en tiempo real zero knowledge.\n* Lo que escribes aquí está cifrado de manera que sólo las personas con el enlace pueden acceder a ello.\n* Incluso el servidor no puede ver lo que escribes.\n* Lo que ves aquí, lo que escuchas aquí, cuando sales, se queda aquí\n\n---\n# Cómo utilizarlo\n1. Escribe tu contenido en Markdown\n - Puedes aprender más sobre Markdown [aquí](http://www.markdowntutorial.com/)\n2. Separa tus diapositivas con ---\n3. Haz clic en \"Presentar\" para ver el resultado - Tus diapositivas se actualizan en tiempo real";
out.slideInitialState = "# CryptSlide\n1. Escribe tu contenido en Markdown\n - Puedes aprender más sobre Markdown [aquí](http://www.markdowntutorial.com/)\n2. Separa tus diapositivas con ---\n3. Haz clic en \"Presentar\" para ver el resultado - Tus diapositivas se actualizan en tiempo real";
out.driveReadmeTitle = "¿Qué es CryptPad?";
out.readme_welcome = "¡Bienvenido a CryptPad!";
out.readme_p1 = "Bienvenido a CryptPad, aquí podrás anotar cosas solo o con otra gente.";

@ -35,11 +35,12 @@ define(function () {
out.typeError = "Ce pad n'est pas compatible avec l'application sélectionnée";
out.onLogout = 'Vous êtes déconnecté de votre compte utilisateur, <a href="/" target="_blank">cliquez ici</a> pour vous authentifier<br>ou appuyez sur <em>Échap</em> pour accéder au pad en mode lecture seule.';
out.wrongApp = "Impossible d'afficher le contenu de ce document temps-réel dans votre navigateur. Vous pouvez essayer de recharger la page.";
out.padNotPinned = 'Ce pad va expirer dans 3 mois, {0}connectez-vous{1} ou {2}enregistrez-vous{3} pour le préserver.';
out.padNotPinned = 'Ce pad va expirer après 3 mois d\'inactivité, {0}connectez-vous{1} ou {2}enregistrez-vous{3} pour le préserver.';
out.anonymousStoreDisabled = "L'administrateur de cette instance de CryptPad a désactivé le drive pour les utilisateurs non enregistrés. Vous devez vous connecter pour pouvoir utiliser CryptDrive.";
out.expiredError = "Ce pad a atteint sa date d'expiration est n'est donc plus disponible.";
out.expiredErrorCopy = ' Vous pouvez toujours copier son contenu ailleurs en appuyant sur <em>Échap</em>.<br> Dés que vous aurez quitté la page, il sera impossible de le récupérer.';
out.deletedError = 'Ce pad a été supprimé par son propriétaire et n\'est donc plus disponible.';
out.inactiveError = 'Ce pad a été supprimé en raison de son inactivité. Appuyez sur Échap pour créer un nouveau pad.';
out.loading = "Chargement...";
out.error = "Erreur";
@ -52,7 +53,7 @@ define(function () {
out.disconnected = 'Déconnecté';
out.synchronizing = 'Synchronisation';
out.reconnecting = 'Reconnexion...';
out.reconnecting = 'Reconnexion';
out.typing = "Édition";
out.initializing = "Initialisation...";
out.forgotten = 'Déplacé vers la corbeille';
@ -223,6 +224,10 @@ define(function () {
out.cancelButton = 'Annuler (Échap)';
out.doNotAskAgain = "Ne plus demander (Échap)";
out.show_help_button = "Afficher l'aide";
out.hide_help_button = "Cacher l'aide";
out.help_button = "Aide";
out.historyText = "Historique";
out.historyButton = "Afficher l'historique du document";
out.history_next = "Voir la version suivante";
@ -283,11 +288,8 @@ define(function () {
out.poll_locked = "Verrouillé";
out.poll_unlocked = "Déverrouillé";
out.poll_show_help_button = "Afficher l'aide";
out.poll_hide_help_button = "Cacher l'aide";
out.poll_bookmark_col = "Marquer cette colonne comme favorite pour qu'elle soit toujours déverouillée et affichée en première position.";
out.poll_bookmarked_col = "Voici votre colonne favorite; elle sera toujours dévérouillée et affichée en première position.";
out.poll_bookmarked_col = "Voici votre colonne favorite ; elle sera toujours dévérouillée et affichée en première position.";
out.poll_total = 'TOTAL';
out.poll_comment_list = "Commentaires";
@ -365,7 +367,7 @@ define(function () {
out.fm_templateName = "Modèles";
out.fm_searchName = "Recherche";
out.fm_recentPadsName = "Pads récents";
out.fm_ownedPadsName = "Possédés";
out.fm_ownedPadsName = "Pads possédés";
out.fm_searchPlaceholder = "Rechercher...";
out.fm_newButton = "Nouveau";
out.fm_newButtonTitle = "Créer un nouveau pad ou un dossier, importer un fichier dans le dossier courant";
@ -676,7 +678,7 @@ define(function () {
out.footer_aboutUs = "À propos";
out.about = "À propos";
out.privacy = "Vie privée";
out.privacy = "Confidentialité";
out.contact = "Contact";
out.terms = "Conditions";
out.blog = "Blog";
@ -759,6 +761,180 @@ define(function () {
out.features_f_storage_anon = "Pads supprimés après 3 mois";
out.features_f_storage_registered = "Gratuit: 50Mo<br>Premium: 5Go/20Go/50Go";
// faq.html
out.faq_link = "FAQ";
out.faq_title = "Foire Aux Questions";
out.faq_whatis = "Qu'est-ce que CryptPad ?";
out.faq = {};
out.faq.keywords = {
title: 'Termes spéciaux',
pad: {
q: "Qu'est-ce qu'un pad ?",
a: '<em>Pad</em> est un terme popularisé par <a href="http://etherpad.org/" target="_blank">Etherpad</a> un éditeur collaboratif en temps-réel. ' +
'Il désigne un document que vous pouvez modifier dans votre navigateur et, en général, vous pouvez voir les modifications effectuées par les autres utilisateurs de manière quasiment instantanée.'
},
owned: {
q: "Qu'est-ce qu'un pad possédé ?",
a: "Un <em>pad possédé</em> est un pad créé avec un <em>propriétaire</em> explicite, identifié sur le serveur par sa <em>clé de signature publique</em>.<br>" +
"Le propriétaire d'un pad peut décider de supprimer ce pad du serveur de manière permanente, afin de le rendre inaccessible aux autres collaborateurs même s'ils possédent le lien dans leur CryptDrive."
},
expiring: {
q: "Qu'est-ce qu'un pad expirant ?",
a: "Un <em>pad expirant</em> est un pad créé avec une date définie à partir de laquelle il sera supprimé automatiquement du serveur. Les pads expirants peuvent être configurés pour avoir une durée de vie comprise entre une heure et cent mois. Le pad et tout son historique sera alors inaccessible, de manière permanente, même s'il est en cours d'édition à sa date d'expiration.<br>" +
"Si un pad possède une date d'expiration, vous pouvez la vérifier en regardant les <em>propriétés</em> du pad, soit avec un clic-droit sur le pad dans votre CryptDrive, ou soit en cliquant sur Propriétés dans le sous-menu de la barre d'outils de l'application."
},
tag: {
q: "Comment utiliser les mots-clés ?",
a: "Vous pouvez ajouter des <em>mots-clés</em> aux pads ou aux fichiers depuis votre CryptDrive et depuis le document en utilisant le bouton <span class=\"fa fa-hashtag\"></span> (<em>Mots-clés</em>) de la barre d'outils des éditeurs.<br>" +
"Il est ensuite possible de rechercher des pads et des fichiers dans votre CryptDrive en tapant un mot-clé, précédé de <em>#</em>, dans la barre de recherche (exemple: #crypto)."
},
template: {
q: "Qu'est-ce qu'un modèle ?",
a: "Un <em>modèle</em> est un pad qui peut être utilisé pour définir le contenu initial d'un nouveau pad du même type quand vous le créez.<br>" +
"Les pads existant dans votre CryptDrive peuvent être transformés en tant que modèle en les déplaçant dans la catégorie <em>Modèles</em> du CryptDrive.<br>" +
"Il est également possible de créer une copie d'un pad en tant que modèle en cliquant sur le bouton <span class=\"fa fa-bookmark\"></span> (<em>Sauver en tant que modèle</em>) dans la barre d'outils des éditeurs."
},
};
out.faq.privacy = {
title: 'Confidentialité',
different: {
q: "Comment Cryptpad est-il différent des autres services de pads ?",
a: "CryptPad chiffre les changements effectués dans vos pads avant de les envoyer au serveur pour qu'il soient stockés, nous ne pouvons donc pas lire le contenu que vous avez tapé."
},
me: {
q: "Quelles informations le serveur possède-t-il sur moi ?",
a: "Les administrateurs du serveur peuvent voir les <b>adresses IP</b> des utilisateurs de CryptPad.<br>" +
"Nous n'enregistrons pas les pads visités par chaque adresse IP mais nous le pouvons, bien que nous n'aurions pas accès au contenu déchiffré de ces pads.<br>" +
"Si vous avez des inquiétudes à ce sujet, il est préférable de considérer que nous collectons ces informations puisque nous n'avons aucun moyen de prouver que ce n'est pas le cas.<br><br>" +
"Nous collectons toutefois certaines <b>données de télémétrie</b> concernant la façon dont les gens utilisent CryptPad, par exemple la résolution de l'écran utilisé ou l'utilisation des boutons de la barre d'outils. Ces données nous aident à améliorer le produit, mais il est possible de désactiver l'envoi de telles informations au serveur en décochant la case <em>Activer l'envoi de retours d'expérience</em> dans vos <em>Préférences</em>.<br><br>" +
"Enfin, nous gardons une trace des pads stockés dans le CryptDrive des utilisateurs afin de pouvoir imposer les limites de stockage, mais nous n'avons, encore une fois, pas accès au contenu ou au type de ces pads. Ces limites sont toutefois associées à la clé publique des utilisateurs, nous ne pouvons donc pas les relier à un nom ou une adresse email.<br><br>" +
"Nous avons écrit un <a href=\"https://blog.cryptpad.fr/2017/07/07/cryptpad-analytics-what-we-cant-know-what-we-must-know-what-we-want-to-know/\" target=\"_blank\">article de blog</a> (en anglais) à ce sujet si vous souhaitez en apprendre davantage."
},
register: {
q: "Qu'est-ce que le serveur apprend à mon sujet si je m'inscrit ?",
a: "Nous ne demandons pas aux utilisateurs d'entrer une adresse email pour s'enregistrer, et le serveur ne connaît pas votre nom d'utilisateur ni votre mot de passe.<br>" +
"Les formulaires d'inscription et de connexion génèrent à la place un ensemble de clés uniques, créées à partir de vos identifiants, et le serveur ne connaît donc que votre signature cryptographique.<br>" +
"Nous utilisons cette information principalement pour mesurer combien de données vous avez stocké sur nos serveurs, afin de pouvoir limiter chaque utilisateur à son quota.<br><br>" +
"Nous utilisons également notre fonctionnalité de <em>retour d'expérience</em> pour indiquer au serveur que quelqu'un avec votre adresse IP a créé un compte utilisateur, bien que nous ne sachions pas lequel. Cela nous permet de mesurer le nombre d'inscriptions sur CryptPad mais aussi de voir dans quelles régions du monde se trouvent les utilisateurs, afin de déterminer les langues dans lesquelles traduire CryptPad.<br><br>" +
"Enfin, les clés générées à l'inscription permettent d'indiquer au serveur que les pads dans votre CryptDrive ne doivent pas être supprimés, même s'ils sont inactifs. Ce système a l'inconvénient de nous fournir davantage d'informations sur la façon dont vous utilisez CryptPad, mais il est nécessaire pour que nous puissions supprimer du serveur les pads inactifs dont personne n'a besoin."
},
other: {
q: "Que peuvent apprendre les autres collaborateurs à mon sujet ?",
a: "Quand vous éditez un pad avec quelqu'un d'autre, vous communiquez en passant par notre serveur, nous sommes donc les seuls à connaître votre adresse IP.<br>" +
"Les autres utilisateurs ont accès à votre pseudonyme, votre avatar, le lien vers votre profil (si vous en avez un) et votre <em>clé publique</em> (qui est utilisée pour le chiffrement des communications entre utilisateurs)."
},
anonymous: {
q: "CryptPad me rend-il anonyme ?",
a: "Bien que CryptPad soit conçu pour en savoir le moins possible à votre sujet, il ne fournit pas un anonymat complet.<br>" +
"Nos serveurs ont accès à votre adresse IP, mais vous pouvez la cacher en utilisant, par exemple, Tor pour accéder à CryptPad.<br>" +
"Utiliser Tor sans changer votre comportement ne garantira toutefois pas votre anonymat, puisque notre serveur est en mesure d'identifier des utilisateurs avec leur identifiant cryptoraphique unique. Si vous utilisez le même compte utilisateur avec et sans Tor, il serait donc possible de désanonymiser votre session.<br><br>" +
"Pour les utilisateurs qui n'ont pas besoin d'un niveau de confidentialité aussi élevé, Tor n'est pas nécessaire puisque CryptPad ne nécessite pas la saisie d'un nom réel, d'un numéro de téléphone ou même d'une adresse email comme de nombreux autres services."
},
policy: {
q: "Avez-vous une politique de confidentialité des données ?",
a: 'Oui ! Elle est disponible <a href="/privacy.html" target="_blank">ici</a>.'
},
};
out.faq.security = {
title: 'Sécurité',
proof: {
q: "Comment utilisez-vous les preuves à divulgation nulle de connaissance (Zero Knowledge proofs) ?",
a: "Quand nous utilisons le terme <em>Zero Knowledge</em>, ce n'est pas une référence aux <em>Zero Knowledge proofs</em>, mais aux <em>Services Web Zero Knowledge</em>.<br>" +
"Les <em>Services Web Zero Knowledge</em> chiffrent les données des utilisateurs dans le navigateur, de manière à ce que le serveur n'aie pas accès au contenu déchiffré ni aux clés de chiffrement.<br><br>" +
"Nous avons établi une courte liste de Services Zero Knowledge <a href=\"https://blog.cryptpad.fr/2017/02/20/Time-to-Encrypt-the-Cloud/#Other-Zero-Knowledge-Services\" target=\"_blank\">sur notre blog</a>."
},
why: {
q: "Pourquoi devrais-je utiliser CryptPad ?",
a: "Notre position est que les services cloud ne devraient pas nécessiter l'accès à vos données afin que vous puissiez les partager avec vos amis ou vos collègues. Si vous utilisez un autre service pour le travail collaboratif et qu'il n'indique pas clairement que le serveur n'a pas accès aux informations, il est très probable que vos données soient utilisées pour faire du profit."
},
compromised: {
q: "CryptPad me protège-t-il si mon ordinateur est compromis ?",
a: "Dans le cas où votre ordinateur ou téléphone serait volé, CryptPad vous permet de déclencher une déconnexion à distance de votre compte CryptPad sur tous les appareils, excepté celui sur lequel vous vous trouvez. Pour ce faire, vous pouvez cliquer sur <b>Se déconnecter partout</b> dans votre page de <b>Préférences</b>.<br>" +
"Tous les appareils qui sont actuellement connectés sur CryptPad à votre compte seront déconnectés. Tous les appareils qui se sont connectés au compte et ne vous demandent plus vos identifiants vous forceront à vous identifier de nouveau lorsque vous visiterez CryptPad.<br>" +
"Actuellement, la <em>déconnexion à distance</em> est implémentée dans votre navigateur et non avec le serveur. Cela signifie que cette fonction devrait être suffisante pour protéger vos données si vous oubliez de vous déconnecter après l'utilisation sur un ordinateur partagé, mais elle ne vous protègera pas des agences gouvernementales."
},
crypto: {
q: "Quelle cryptographie utilisez-vous ?",
a: 'CryptPad est basé sur deux librairies open-source de cryptographie : <a href="https://github.com/dchest/tweetnacl-js" target="_blank">tweetnacl.js</a> et <a href="https://github.com/dchest/scrypt-async-js" target="_blank">scrypt-async.js</a>.<br>' +
'<b>Scrypt</b> est une <em>fonction de dérivation de clé</em> basée sur un mot de passe. Nous l\'utilisons pour transformer votre nom d\'utilisateur et votre mot de passe en un unique ensemble de clés qui sécurise l\'accès à votre CryptDrive afin que vous seul puissiez accéder à votre liste de pads.<br>' +
'Nous utilisons les outils de chiffrement <em>xsalsa20-poly1305</em> et <em>x25519-xsalsa20-poly1305</em> fournis par <b>tweetnacl</b> pour chiffrer vos pads et l\'historique du chat respectivement.'
}
};
out.faq.usability = {
title: 'Utilisation',
register: {
q: "Qu'est-ce que je gagne en créant un compte utilisateur ?",
a: 'Les utilisateurs enregistrés ont accès à un certain nombre de nouvelles fonctionnalités inaccessibles aux utilisateurs non connectés. Un tableau récapitulatif est disponible <a href="/features.html">ici</a>.'
},
share: {
q: "Comment partager des pads chiffrés avec mes amis ?",
a: "CryptPad stocke la clé secrète de chiffrement des pads après le symbole `#` dans l'URL. " +
"Tout ce qui se trouve après ce symbole n'est jamais envoyé au serveur, ainsi nous n'avons pas accès à vos clés de chiffrement. " +
"Partager le lien d'un pad revient donc à permettre la lecture ou la modification du contenu."
},
remove: {
q: "J'ai supprimé un pad ou un fichier de mon CryptDrive, mais le contenu est encore disponible. Comment le supprimer ?",
a: "Seuls les <em>pads possédés</em> (introduits en février 2018) peuvent être supprimés du serveur. Ils ne peuvent d'ailleurs être supprimés du serveur que par leur <em>propriétaire</em> (l'utilisateur ayant créé le pad).<br>" +
"Si vous n'êtes pas le créateur du pad, vous devrez demander au propriétaire de le supprimer pour vous.<br>" +
"Pour les pads que vous possédez, vous pouvez effectuer un <b>clic-droit sur le pad dans votre CryptDrive</b>, et sélectionner <b>Supprimer du serveur</b>."
},
forget: {
q: "Que faire si j'oublie mon mot de passe ?",
a: "Malheureusement, si nous avions la possibilité de retrouver ou de modifier votre mot de passe, cela signifierait que nous avons accès à vos pads, ce n'est donc pas le cas.<br>" +
"Si vous n'avez pas noté votre nom d'utilisateur et votre mot de passe, et que vous ne vous en souvenez pas, il est peut-être possible de retrouver l'accès à certains pads grâce à l'historique de votre navigateur."
},
change: {
q: "Que faire si je souhaite changer de mot de passe ?",
a: "Il n'est actuellement pas possible de changer votre mot de passe sur CryptPad, mais nous comptons développer cette fonctionnalité très bientôt."
},
devices: {
q: "Je suis connecté sur deux appareils mais avec deux CryptDrive différents, comment est-ce possible ?",
a: "Il est possible que vous ayez enregistré le même nom d'utilisateur 2 fois, mais avec des mots de passe différents.<br>" +
"Puisque le serveur de CryptPad vous identifie avec une clé cryptographique et non avec votre nom d'utilisateur, il ne peut pas empêcher la création d'autres comptes avec le même nom. Ainsi, chaque utilisateur possède une combinaison nom d'utilisateur / mot de passe unique.<br>" +
"Les utilisateurs enregistrés peuvent voir leur nom de compte en haut de la page de préférences."
},
folder: {
q: "Puis-je partager des dossiers complets de mon CryptDrive ?",
a: "Nous travaillons sur l'ajout d'une fonctionnalité <em>workgroups</em> (ou groupes de travail), qui permettrait aux collaborateurs de partager une structure de type dossier, avec tous les pads contenus dans cette structure."
},
feature: {
q: "Pouvez-vous ajouter une fonctionnalité particulière dont j'ai besoin ?",
a: 'Beaucoup de fonctionnalités de CryptPad existent parce que des utilisateurs les ont demandées.<br>' +
'Notre <a href="https://cryptpad.fr/contact.html" target="_blank">page de contact</a> liste les différentes manières de nous joindre.<br><br>' +
'Malheureusement, nous ne pouvons pas garantir que nous allons implémenter toutes les fonctionnalités demandées.<br>' +
'Si une fonctionnalité particulière est nécessaire pour votre organisation, vous pouvez sponsoriser son développement pour s\'assurer de sa réalisation. Veuillez contacter <a href="mailto:sales@cryptpad.fr" target="_blank">sales@cryptpad.fr</a> pour plus d\'informations.<br><br>' +
"Si vous n'avez pas la possibilité de sponsoriser du développement, nous sommes toujours intéressés par de nouvelles idées et des retours d'expérience qui peuvent nous aider à améliorer CryptPad. N'hésitez pas à nous contacter, avec les méthodes données précédemment, à n'importe quel moment."
},
};
out.faq.other = {
title: "Autres questions",
pay: {
q: "Pourquoi payer alors que toutes les fonctionnalités sont gratuites ?",
a: "Un compte premium permet d'<b>augmenter la limite de stockage</b> dans le CryptDrive, ainsi que celle de ses amis (<a href=\"https://accounts.cryptpad.fr/#/faq\" target=\"_blank\">en savoir plus</a>).<br>" +
"En plus des ces avantages directs, l'abonnement premium permet aussi de <b>financer le développement</b> actif et de manière continue de CryptPad. Cela comprend la correction de bugs, l'ajout de nouvelles fonctionnalités et rendre plus facile l'hébergement de CryptPad par d'autres personnes.<br>" +
"Avec un abonnement, vous aidez aussi à prouver aux autres fournisseurs de services que les gens sont prêts à supporter les technologies améliorant le respect de leur vie privée. Nous espérons qu'un jour, les entreprises ayant pour revenu principal la revente de données des utilisateurs soient de l'histoire ancienne.<br>" +
"Enfin, nous offrons la plupart des fonctionnalités gratuitement parce que nous croyons que tout le monde mérite le respect de la vie privée. En souscrivant à un compte premium, vous nous aider à maintenir ces fonctionnalités basiques accessibles aux populations défavorisées."
},
goal: {
q: "Quel est votre objectif ?",
a: "En développant une technologie de collaboration qui respecte la vie privée, nous espérons augmenter les attentes des utilisateurs en ce qui concerne les plateformes de services \"cloud\" et leur politique de confidentialité. Nous souhaitons que notre travail conduise les autres fournisseurs de services, quel que soit leur domaine, à égaler voire dépasser nos efforts.<br>" +
"Malgré notre optimisme, nous savons que la plupart du Web est financé par les publicités ciblées. Il y a encore beaucoup de travail à effectuer que l'on peut faire de nous-mêmes, et nous apprécions le support, la promotion et les contributions de notre communauté envers cet objectif."
},
jobs: {
q: "Est-ce que vous embauchez ?",
a: 'Oui ! Vous pouvez envoyer un email à <a href="mailto:jobs@xwiki.com">jobs@xwiki.com</a>.'
},
host: {
q: "Pouvez-vous m'aider à installer ma propre instance de CryptPad ?",
a: 'Nous serions heureux de fournir du support pour l\'installation de CryptPad au sein de votre organisation. Veuillez contacter <a href="mailto:sales@cryptpad.fr">sales@cryptpad.fr</a> pour plus d\'informations.'
},
revenue: {
q: "Comment participer au système de partage des revenus ?",
a: "Si vous possédez votre propre instance de CryptPad et que vous souhaitez activer les comptes payant et partager les revenus avec les développeurs, votre serveur devra être configuré comme un service partenaire.<br>" +
'Dans votre répertoire CryptPad, le fichier <code>config.example.js</code> devrait contenir des explications concernant les étapes à suivre pour configurer votre serveur. Vous devrez aussi contacter <a href="mailto:sales@cryptpad.fr">sales@cryptpad.fr</a> pour vérifier que votre serveur est configuré correctement et pour discuter des méthodes de paiement.'
},
};
// terms.html
out.tos_title = "Conditions d'utilisation de CryptPad";
@ -781,17 +957,56 @@ define(function () {
// Initial states
out.help = {};
out.help.title = "Pour bien démarrer";
out.help.generic = {
more: 'Apprenez-en davantage sur le fonctionnement de CryptPad en lisant notre <a href="/faq.html" target="_blank">FAQ</a>',
share: 'Utilisez le menu partage (<span class="fa fa-share-alt"></span>) pour générer un lien d\'accès ou d\'édition pad',
stored: 'Chaque pad que vous visitez est stocké automatiquement dans votre <a href="/drive/" target="_blank">CryptDrive</a>'
};
out.help.text = {
formatting: 'Vous pouvez afficher ou cacher la barre d\'outils de texte en cliquant sur les boutons <span class="fa fa-caret-down"></span> ou <span class="fa fa-caret-up"></span>',
embed: 'Les utilisateurs enregistrés peuvent intégrer un fichier de leur CryptDrive en utilisant le bouton <span class="fa fa-image"></span>',
history: 'Vous pouvez utiliser l\'historique (<span class="fa fa-history"></span>) pour voir ou restaurer les versions précédentes du pad'
};
out.help.pad = {
export: 'Vous pouvez exporter le contenu en tant que PDF avec le bouton <span class="fa fa-print"></span> de la barre d\'outils de mise en forme du texte'
};
out.help.code = {
modes: 'Utilisez le sous-menu <span class="fa fa-ellipsis-h"></span> pour changer le mode de coloration syntaxique ou le thème de couleur'
};
out.help.slide = {
markdown: 'Rédigez vos slides en <a href="http://www.markdowntutorial.com/">Markdown</a> et séparez les avec une ligne contenant <code>---</code>',
present: 'Démarrez la présentation en utilisant le bouton <span class="fa fa-play-circle"></span>',
settings: 'Modifiez les préférences de la présentation (image de fond, transitions, numéro de pages, ...) avec le bouton <span class="fa fa-cog"></span> dans le sous-menu <span class="fa fa-ellipsis-h"></span>',
colors: 'Modifiez la couleur du texte ou du fond en utilisant les boutons <span class="fa fa-i-cursor"></span> et <span class="fa fa-square"></span>'
};
out.help.poll = {
decisions: 'Prenez des décisions en privé avec des personnes de confiance',
options: 'Proposez des options et exprimez vos préférences',
choices: 'Cliquez sur les cellules de votre colonne pour modifier leur valeur entre oui (<strong>✔</strong>), peut-être (<strong>~</strong>) ou non (<strong>✖</strong>)',
submit: 'Cliquez sur <b>Ajouter</b> pour rendre vos choix visibles aux autres'
};
out.help.whiteboard = {
colors: 'Double-cliquez sur les couleurs pour changer la palette',
mode: 'Vous pouvez désactiver le mode dessin pour déplacer, redimensionner, ou supprimer des éléments du dessin',
embed: 'Intégrez des images de votre disque <span class="fa fa-file-image-o"></span> ou de votre CryptDrive <span class="fa fa-image"></span> et exporter le contenu en tant que PNG sur votre disque <span class="fa fa-download"></span> ou votre CryptDrive <span class="fa fa-cloud-upload"></span>'
};
out.initialState = [
'<p>',
'Voici <strong>CryptPad</strong>, l\'éditeur collaboratif en temps-réel Zero Knowledge. Tout est sauvegardé dés que vous le tapez.',
'<br>',
'Partagez le lien vers ce pad avec des amis ou utilisez le bouton <span class="fa fa-share-alt" style="border: 1px solid black;color:#000;">&nbsp;Partager&nbsp;</span> pour obtenir le <em>lien de lecture-seule</em>, qui permet la lecture mais non la modification.',
'Partagez le lien vers ce pad avec des amis ou utilisez le bouton <span class="fa fa-share-alt"></span> pour obtenir le <em>lien de lecture-seule</em>, qui permet la lecture mais non la modification.',
'</p>',
'<p>',
'<em>',
'Lancez-vous, commencez à taper...',
'</em></p>',
'<p>&nbsp;<br></p>'
].join('');
out.codeInitialState = [
@ -803,14 +1018,6 @@ define(function () {
out.slideInitialState = [
'# CryptSlide\n',
'* Voici CryptPad, l\'éditeur collaboratif en temps-réel Zero Knowledge.\n',
'* Ce que vous tapez ici est chiffré de manière que seules les personnes avec le lien peuvent y accéder.\n',
'* Même le serveur est incapable de voir ce que vous tapez.\n',
'* Ce que vous voyez ici, ce que vous entendez, quand vous partez, ça reste ici.\n',
'\n',
'---',
'\n',
'# Comment l\'utiliser\n',
'1. Écrivez le contenu de votre présentation avec la syntaxe Markdown\n',
' - Apprenez à utiliser markdown en cliquant [ici](http://www.markdowntutorial.com/)\n',
'2. Séparez vos slides avec ---\n',
@ -875,6 +1082,7 @@ define(function () {
out.creation_createFromTemplate = "Depuis un modèle";
out.creation_createFromScratch = "Nouveau pad vide";
out.creation_settings = "Préférences des nouveaux pads";
out.creation_saveSettings = "Sauver les préférences";
// Properties about creation data
out.creation_owners = "Propriétaires";
out.creation_ownedByOther = "Possédé par un autre utilisateur";
@ -882,7 +1090,8 @@ define(function () {
out.creation_expiration = "Date d'expiration";
out.creation_propertiesTitle = "Disponibilité";
out.creation_appMenuName = "Mode avancé (Ctrl + E)";
out.creation_newPadModalDescription = "Cliquez sur un type de pad pour le créer. Vous pouvez cocher la case pour afficher l'écran de création de pads";
out.creation_newPadModalDescription = "Cliquez sur un type de pad pour le créer. Vous pouvez aussi appuyer sur <b>Tab</b> pour sélectionner un type et appuyer sur <b>Entrée</b> pour valider.";
out.creation_newPadModalDescriptionAdvanced = "Cochez la case si vous souhaitez voir l'écran de création de pads (pour les pads possédés ou à date d'expiration). Vous pouvez appuyer sur <b>Espace</b> pour changer sa valeur.";
out.creation_newPadModalAdvanced = "Afficher l'écran de création de pads";
// New share modal

@ -36,11 +36,12 @@ define(function () {
out.typeError = "This pad is not compatible with the selected application";
out.onLogout = 'You are logged out, <a href="/" target="_blank">click here</a> to log in<br>or press <em>Escape</em> to access your pad in read-only mode.';
out.wrongApp = "Unable to display the content of that realtime session in your browser. Please try to reload that page.";
out.padNotPinned = 'This pad will expire in 3 months, {0}login{1} or {2}register{3} to preserve it.';
out.padNotPinned = 'This pad will expire after 3 months of inactivity, {0}login{1} or {2}register{3} to preserve it.';
out.anonymousStoreDisabled = "The webmaster of this CryptPad instance has disabled the store for anonymous users. You have to log in to be able to use CryptDrive.";
out.expiredError = 'This pad has reached its expiration time and is no longer available.';
out.expiredErrorCopy = ' You can still copy the content to another location by pressing <em>Esc</em>.<br>Once you leave this page, it will disappear forever!';
out.deletedError = 'This pad has been deleted by its owner and is no longer available.';
out.inactiveError = 'This pad has been deleted due to inactivity. Press Esc to create a new pad.';
out.loading = "Loading...";
out.error = "Error";
@ -53,7 +54,7 @@ define(function () {
out.disconnected = 'Disconnected';
out.synchronizing = 'Synchronizing';
out.reconnecting = 'Reconnecting...';
out.reconnecting = 'Reconnecting';
out.typing = "Editing";
out.initializing = "Initializing...";
out.forgotten = 'Moved to the trash';
@ -225,6 +226,10 @@ define(function () {
out.cancelButton = 'Cancel (esc)';
out.doNotAskAgain = "Don't ask me again (Esc)";
out.show_help_button = "Show help";
out.hide_help_button = "Hide help";
out.help_button = "Help";
out.historyText = "History";
out.historyButton = "Display the document history";
out.history_next = "Go to the next version";
@ -285,9 +290,6 @@ define(function () {
out.poll_locked = "Locked";
out.poll_unlocked = "Unlocked";
out.poll_show_help_button = "Show help";
out.poll_hide_help_button = "Hide help";
out.poll_bookmark_col = 'Bookmark this column so that it is always unlocked and displayed at the beginning for you';
out.poll_bookmarked_col = 'This is your bookmarked column. It will always be unlocked and displayed at the beginning for you.';
out.poll_total = 'TOTAL';
@ -769,25 +771,208 @@ define(function () {
out.faq_link = "FAQ";
out.faq_title = "Frequently Asked Questions";
out.faq_whatis = "What is CryptPad?";
out.faq = {};
out.faq.cat1 = {
title: 'Category 1',
q1: {
q: 'What is a pad?',
a: 'A realtime collaborative document...'
out.faq.keywords = {
title: 'Keywords',
pad: {
q: "What is a pad?",
a: "<em>Pad</em> is a term popularized by <a href='http://etherpad.org/' target='_blank'>Etherpad</a>, a real-time collaborative editor.\n" +
"It refers to a document that you can edit in your browser, generally with other people's changes visible nearly instantly."
},
owned: {
q: "What is an owned Pad?",
a: "An <em>owned pad</em> is a pad created with an explicit <em>owner</em>, identified to the server by their <em>public signing key</em>." +
" A pad's owner may choose to delete their pads from the server, making it unavailable to other collaborators in the future, whether they had it in their CryptDrive or not."
},
expiring: {
q: "What is an expiring Pad?",
a: "An <em>expiring pad</em> is a pad created with a set time at which it will be automatically removed from the server." +
" Expiring pads can be configured to last anywhere from one hour to one hundred months." +
" The pad and all of its history will become permanently unavailable even if it is being edited at the time that it expires.<br><br>" +
"If a pad is set to expire, you can check its expiration time by viewing its <em>properties</em>, either by right-clicking the pad in your CryptDrive, or by using the <em>properties</em> sub-menu from an application's toolbar."
},
tag: {
q: "How can I use tags?",
a: "You can tag pads and uploaded files via your CryptDrive, or using the <em>tag</em> button (<span class='fa fa-hashtag'></span>) in any editor's toolbar." +
" Search for pads and files in your CryptDrive using the search bar with a term beginning with hashtag, like <em>#crypto</em>."
},
template: {
q: "What is a template?",
a: "A template is a pad which can be used to define to initial content for another pad of the same type when you create it." +
" Any existing pad can be turned into a template by moving it into the <em>Templates</em> section in your CryptDrive." +
" You can also create a copy of a pad to be used as a template by clicking the template button (<span class='fa fa-bookmark'></span>) in the editor's toolbar."
},
};
out.faq.privacy = {
title: 'Privacy',
different: {
q: "How is CryptPad different from other Pad services?",
a: "CryptPad encrypts changes to your pads before sending that information to be stored on the server, so we can't read what you're typing."
},
me: {
q: "What information does the server know about me?",
a: "Server administrators are able to see the IP addresses of people who visit the CryptPad." +
" We don't record which addresses visit which pads, but we could, even though we don't have access to the decrypted content of those pads." +
" If you are worried about us analyzing that information, it's safest to assume that we do collect it, since we can't prove that we don't.<br><br>" +
"We collect some basic telemetry about how people use CryptPad, such as the size of the screen on their device, and which buttons they click the most." +
"This helps us improve the software, but if you'd prefer not to send such information to the server, you can <strong>opt out by unchecking the <em>Enable user feedback</em> checkbox</strong>.<br><br>" +
"We do keep track of which pads are in a user's CryptDrive so that we can impose storage limits, but we don't know the content or type of those pads." +
" Storage quotas are associated with a user's public key, but we don't associate names or emails with those public keys.<br><br>" +
" For more information, you can read this <a href='https://blog.cryptpad.fr/2017/07/07/cryptpad-analytics-what-we-cant-know-what-we-must-know-what-we-want-to-know/' target='_blank'>blog post</a> which we wrote about the topic."
},
register: {
q: "Does the server know more about me if I register?",
a: "We don't require users to verify their email address, and the server does not even learn your username or password when you register." +
" Instead, the register and login forms generate a unique keyring from your input, and the server only learns your cryptographic signature." +
" We use this information to track details like how much data you are using, which allows us to restrict each user to a quota.<br><br>" +
"We use our <em>feedback</em> functionality to inform the server that someone with your IP has registered an account." +
" We use this to measure how many people register for CryptPad accounts, and to see what regions they are in so that we can guess which languages may need better support.<br><br>" +
"When you register, you generate a public key which is used to tell the server that the pads in your CryptDrive should not be deleted even if they are not actively being used." +
" This information does reveal more about how you are using CryptPad, but the system allows us to remove pads from the server once nobody cares enough to keep them."
},
other: {
q: "What can other collaborators learn about me?",
a: "When you edit a pad with someone else, you communicate through the server, so only we learn your IP address." +
" Other users can see your display name, avatar, the link to your profile (if you have one), and your <em>public key</em> (which is used for encrypting communications between each other)."
},
q2: {
q: 'Question 2?',
a: '42'
anonymous: {
q: "Does CryptPad make me anonymous?",
a: "Even though CryptPad is designed to know as little about you as possible, it does not provide strong anonymity." +
" Our servers have access to your IP address, however, you can hide this information by using Tor to access CryptPad." +
" Using Tor without changing your behaviour will not guarantee you anonymity, as the server is also able to identify users by their unique cryptographic identifier." +
" If you use the same account when you're not using Tor, it will be possible to deanonymize your session.<br><br>" +
"For users who require a lesser degree of privacy, CryptPad does not require users to identify themselves by name, phone number, or email address like many other services."
},
policy: {
q: "Do you have a data privacy policy?",
a: "Yes! It is available <a href='/privacy.html' target='_blank'>here</a>."
}
};
out.faq.cat2 = {
title: 'Category 2',
q1: {
q: 'A new question?',
a: 'The answer'
out.faq.security = {
title: 'Security',
proof: {
q: "How do you use Zero Knowledge Proofs?",
a: "When we use the term <em>Zero Knowledge</em>, we are not referring to <em>Zero Knowledge proofs</em>, but to <em>Zero Knowledge Web Services</em>." +
" <em>Zero Knowledge Web Services</em> encrypt user data in the user's browser, without the server ever having access to the unencrypted data, or the encryption keys.<br><br>" +
"We've compiled a short list of Zero Knowledge services <a href='https://blog.cryptpad.fr/2017/02/20/Time-to-Encrypt-the-Cloud/#Other-Zero-Knowledge-Services'>here</a>."
},
why: {
q: "Why should I use CryptPad?",
a: "Our position is that cloud services should not require access to your data in order for you to share it with your friends and colleagues." +
" If you are using another service to collaborate, and they do not explicitly say that they can't access your information, it is very likely that they are leveraging it for profit."
},
compromised: {
q: "Does CryptPad protect me if my device is compromised?",
a: "In the event that your device is stolen, CryptPad allows you to trigger a remote logout of all devices except the one you are currently using." +
" To do so, go to your <strong>settings page</strong> and click <strong>Log out everywhere</strong>." +
" All other devices which are currently connected to the account will log out." +
" Any previously connected devices which visit CryptPad will log out as soon as they load the page.<br><br>" +
"Currently, <em>remote logout</em> is implemented in the browser, not in conjunction with the server." +
" As such, it may not protect you from government agencies, but it should be sufficient if you forgot to log out after using CryptPad from a shared computer."
},
crypto: {
q: "What cryptography do you use?",
a: "CryptPad is based upon two open-source cryptography libraries: <a href='https://github.com/dchest/tweetnacl-js' target='_blank'>tweetnacl.js</a> and <a href='https://github.com/dchest/scrypt-async-js' target='_blank'>scrypt-async.js</a>.<br><br>" +
"Scrypt is a <em>password-based key derivation algorithm</em>. We use it to turn your username and password into a unique keyring which secures access to your CryptDrive such that only you can access your list of pads.<br><br>" +
"We use the <em>xsalsa20-poly1305</em> and <em>x25519-xsalsa20-poly1305</em> cyphers provided by tweetnacl to encrypt pads and chat history, respectively."
}
};
out.faq.usability = {
title: 'Usability',
register: {
q: "What do I get by registering?",
a: "Registered users have access to a number of features unavailable to unregistered users. There's a chart <a href='/features.html' target='_blank'>here</a>."
},
share: {
q: "How can I share encrypted pads with my friends?",
a: "CryptPad puts the secret encryption key to your pad after the <em>#</em> character in the URL." +
" Anything after this character is not sent to the server, so we never have access to your encryption keys." +
" By sharing the link to a pad, you share the ability to read and access it."
},
remove: {
q: "I removed a pad or file from my CryptDrive, but the content is still available. How can I remove it?",
a: "Only <em>owned pads</em> (introduced in February 2018) can be deleted. Additionally, these pads can only be deleted by their <em>owners</em> (the person that originally created the pad)." +
" If you are not the creator of the pad, you will have to ask its owner to delete it for you." +
" For pads you do own, you can <strong>right-click the pad in your CryptDrive</strong>, and choose <strong>Delete from the server</strong>."
},
forget: {
q: "What if I forget my password?",
a: "Unfortunately, if we could recover access to your encrypted pads for you, we'd be able to access them ourselves." +
" If you did not record your username and password anywhere, and cannot remember either, you may be able to recover your pads by filtering your browser's history."
},
change: {
q: "What if I want to change my password?",
a: "It is not currently possible to change your CryptPad password, though we are planning to develop this functionality very soon."
},
devices: {
q: "I am logged in on two devices, and see two different CryptDrives, how is this possible?",
a: "It's probable that you registered the same name twice, using different passwords." +
" Because the CryptPad server identifies you by your cryptographic signature and not your name, it cannot prevent others from registering with the same name." +
" As such, each user account has a unique username and password combination." +
" Logged in users can see their username at the top of the settings page."
},
folder: {
q: "Can I share entire folders from my CryptDrive?",
a: "We're working on adding support for <em>\"workgroups\"</em>, which would allow collaborators to share a folder structure, and all the pads contained within that structure."
},
feature: {
q: "Can you add a very special feature which I need?",
a: "Many of the features in CryptPad exist because users asked for them." +
" Our <a href='https://cryptpad.fr/contact.html' target='_blank'>contacts page</a> lists the ways that you can reach us.<br><br>" +
"Unfortunately, we cannot guarantee that we will implement everything that people ask for." +
" If a particular feature is critical for your organization, you can sponsor development time to ensure its completion." +
" Please contact <a href='mailto:sales@cryptpad.fr' target='_blank'>sales@cryptpad.fr</a> for more information.<br><br>" +
"Even if you cannot afford to sponsor development, we're interested in feedback that can help us improve CryptPad." +
" Feel free to contact us with via the above methods any time."
},
};
out.faq.other = {
title: "Other questions",
pay: {
q: "Why should I pay when so many features are free?",
a: "We give supporters additional storage and the ability to increase their friends' quotas (<a href='https://accounts.cryptpad.fr/#/faq' target='_blank'>learn more</a>).<br><br>" +
"Beyond these short term benefits, by subscribing with a premium account you help to fund continued, active development of CryptPad. That includes fixing bugs, adding new features, and making it easier for others to help host CryptPad themselves." +
" Additionally, you help to prove to other service providers that people are willing to support privacy enhancing technologies. It is our hope that eventually business models based on selling user data will become a thing of the past.<br><br>" +
"Finally, we offer most of CryptPad's functionality for free because we believe everyone deserves personal privacy, not just those with disposable income." +
" By supporting us, you help us continue to make it possible for underprivileged populations to access these basic features without a price tag attached."
},
goal: {
q: "What is your goal?",
a: "By developing privacy-respecting collaboration technology, we wish to raise users' expectations of privacy from cloud-computing platforms." +
" We hope that our work drives other service providers in all domains to match or exceed our efforts." +
" Despite our optimism, we know that much of the web is funded by revenue from targeted advertising." +
" There is much more work to be done than we can manage ourselves, and we appreciate the promotion, support, and contributions of our community towards this goal."
},
jobs: {
q: "Are you hiring?",
a: "Yes! Please introduce yourself with an email to <a href='mailto:jobs@xwiki.com' target='_blank'>jobs@xwiki.com</a>."
},
host: {
q: "Can you help me set up my own instance of CryptPad?",
a: "We are happy to provide support for your organization's internal CryptPad installation. Please contact <a href='mailto:sales@cryptpad.fr' target='_blank'>sales@cryptpad.fr</a> for more information."
},
revenue: {
q: "How can I participate in the revenue sharing?",
a: " If you are running your own instance of CryptPad, and would like to enable paid accounts and split the revenue with the developers, your server will need to be configured as a partner service.<br><br>" +
"In your CryptPad directory, <em>config.example.js</em> should contain an explanation of what you need to configure on your server." +
" You will need to contact <a href='mailto:sales@cryptpad.fr'>sales@cryptpad.fr</a> to verify that your server is configured to use HTTPS correctly, and discuss payment methods."
},
};
// terms.html
@ -817,17 +1002,56 @@ define(function () {
// Initial states
out.help = {};
out.help.title = "Getting started";
out.help.generic = {
more: 'Learn more about how CryptPad can work for you by reading our <a href="/faq.html" target="_blank">FAQ</a>',
share: 'Use the share menu (<span class="fa fa-share-alt"></span>) to generate a link so collaborators can view or edit this pad',
stored: 'Every pad you visit is automatically stored in your <a href="/drive/" target="_blank">CryptDrive</a>',
};
out.help.text = {
formatting: 'You can display or hide the text formatting toolbar by clicking the <span class="fa fa-caret-down"></span> or <span class="fa fa-caret-up"></span> buttons',
embed: 'Registered users can embed an image or a file stored in their CryptDrive using <span class="fa fa-image"></span>',
history: 'You can use <em>history</em> <span class="fa fa-history"></span> to view or restore previous versions',
};
out.help.pad = {
export: 'You can export the content as PDF using the <span class="fa fa-print"></span> button in the text formatting toolbar',
};
out.help.code = {
modes: 'Use the dropdown menus in the <span class="fa fa-ellipsis-h"></span> submenu to change syntax highlighting modes or color themes',
};
out.help.slide = {
markdown: 'Write slides in <a href="http://www.markdowntutorial.com/">Markdown</a> and separate them with a line containing <code>---</code>',
present: 'Start the presentation using the <span class="fa fa-play-circle"></span> button',
settings: 'Change the slide settings (background, transitions, page numbers, etc.) with the <span class="fa fa-cog"></span> button in the <span class="fa fa-ellipsis-h"></span> submenu',
colors: 'Change the text and background colors using the <span class="fa fa-i-cursor"></span> and <span class="fa fa-square"></span> buttons',
};
out.help.poll = {
decisions: 'Make decisions in private among trusted friends',
options: 'Propose options, and express your preferences',
choices: 'Click cells in your column to cycle through yes (<strong>✔</strong>), maybe (<strong>~</strong>), or no (<strong>✖</strong>)',
submit: 'Click <strong>submit</strong> to make your choices visible to others',
};
out.help.whiteboard = {
colors: 'Double-click colors to modify your palette',
mode: 'Disable draw mode to drag and stretch strokes',
embed: 'Embed images from your disk <span class="fa fa-file-image-o"></span> or your CryptDrive <span class="fa fa-image"></span> and export them as PNG to your disk <span class="fa fa-download"></span> or your CryptDrive <span class="fa fa-cloud-upload"></span>'
};
out.initialState = [
'<p>',
'This is&nbsp;<strong>CryptPad</strong>, the Zero Knowledge realtime collaborative editor. Everything is saved as you type.',
'<br>',
'Share the link to this pad to edit with friends or use the <span class="fa fa-share-alt" style="border: 1px solid black;color:#000;">&nbsp;Share&nbsp;</span> button to share a <em>read-only link</em>&nbsp;which allows viewing but not editing.',
'Share the link to this pad to edit with friends or use the <span class="fa fa-share-alt"></span> button to share a <em>read-only link</em>&nbsp;which allows viewing but not editing.',
'</p>',
'<p><em>',
'Go ahead, just start typing...',
'</em></p>',
'<p>&nbsp;<br></p>'
].join('');
out.codeInitialState = [
@ -839,14 +1063,6 @@ define(function () {
out.slideInitialState = [
'# CryptSlide\n',
'* This is a zero knowledge realtime collaborative editor.\n',
'* What you type here is encrypted so only people who have the link can access it.\n',
'* Even the server cannot see what you type.\n',
'* What you see here, what you hear here, when you leave here, let it stay here.\n',
'\n',
'---',
'\n',
'# How to use\n',
'1. Write your slides content using markdown syntax\n',
' - Learn more about markdown syntax [here](http://www.markdowntutorial.com/)\n',
'2. Separate your slides with ---\n',
@ -913,6 +1129,7 @@ define(function () {
out.creation_createFromTemplate = "From template";
out.creation_createFromScratch = "From scratch";
out.creation_settings = "New Pad settings";
out.creation_saveSettings = "Save settings";
// Properties about creation data
out.creation_owners = "Owners";
out.creation_ownedByOther = "Owned by another user";
@ -920,7 +1137,8 @@ define(function () {
out.creation_expiration = "Expiration time";
out.creation_propertiesTitle = "Availability";
out.creation_appMenuName = "Advanced mode (Ctrl + E)";
out.creation_newPadModalDescription = "Click on a pad type to create it. You can check the box if you want to display the pad creation screen (for owned pads, expiring pads, etc.).";
out.creation_newPadModalDescription = "Click on a pad type to create it. You can also press <b>Tab</b> to select the type and press <b>Enter</b> to confirm.";
out.creation_newPadModalDescriptionAdvanced = "You can check the box (or press <b>Space</b> to change its value) if you want to display the pad creation screen (for owned pads, expiring pads, etc.).";
out.creation_newPadModalAdvanced = "Display the pad creation screen";
// New share modal

@ -486,13 +486,8 @@ define(function () {
'<p>',
'This is&nbsp;<strong>CryptPad</strong>, the Zero Knowledge realtime collaborative editor. Everything is saved as you type.',
'<br>',
'Share the link to this pad to edit with friends or use the <span style="background-color:#5cb85c;color:#ffffff;">&nbsp;Share&nbsp;</span> button to share a <em>read-only link</em>&nbsp;which allows viewing but not editing.',
'Share the link to this pad to edit with friends or use the <span class="fa fa-share-alt"></span> button to share a <em>read-only link</em>&nbsp;which allows viewing but not editing.',
'</p>',
'<p><span style="color:#808080;"><em>',
'Go ahead, just start typing...',
'</em></span></p>',
'<p>&nbsp;<br></p>'
].join('');
out.codeInitialState = [
@ -504,14 +499,6 @@ define(function () {
out.slideInitialState = [
'# CryptSlide\n',
'* This is a zero knowledge realtime collaborative editor.\n',
'* What you type here is encrypted so only people who have the link can access it.\n',
'* Even the server cannot see what you type.\n',
'* What you see here, what you hear here, when you leave here, let it stay here.\n',
'\n',
'---',
'\n',
'# How to use\n',
'1. Write your slides content using markdown syntax\n',
' - Learn more about markdown syntax [here](http://www.markdowntutorial.com/)\n',
'2. Separate your slides with ---\n',

@ -4,16 +4,6 @@ define(function () {
out.main_title = "CryptPad: Zero Knowledge, Colaborare în timp real";
out.main_slogan = "Puterea stă în cooperare - Colaborarea este cheia";
out.type = {};
out.pad = "Rich text";
out.code = "Code";
out.poll = "Poll";
out.slide = "Presentation";
out.drive = "Drive";
out.whiteboard = "Whiteboard";
out.file = "File";
out.media = "Media";
out.button_newpad = "Filă Text Nouă";
out.button_newcode = "Filă Cod Nouă";
out.button_newpoll = "Sondaj Nou";
@ -330,9 +320,9 @@ define(function () {
out.header_france = "<a href=\"http://www.xwiki.com/\" target=\"_blank\" rel=\"noopener noreferrer\">With <img class=\"bottom-bar-heart\" src=\"/customize/heart.png\" alt=\"love\" /> from <img class=\"bottom-bar-fr\" src=\"/customize/fr.png\" title=\"Franța\" alt=\"Franța\"/> by <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_logoTitle = "Mergi la pagina principală";
out.initialState = "<p>Acesta este&nbsp;<strong>CryptPad</strong>, editorul colaborativ bazat pe tehnologia Zero Knowledge în timp real. Totul este salvat pe măsură ce scrii.<br>Partajează link-ul către acest pad pentru a edita cu prieteni sau folosește <span class=\"fa fa-share-alt\" style=\"border:1px solid black;color:#000;\">&nbsp;Share&nbsp;</span> butonul pentru a partaja <em>read-only link</em>&nbsp;permițând vizualizarea dar nu și editarea.</p><p><em>Îndrăznește, începe să scrii...</em></p><p>&nbsp;<br></p>";
out.initialState = "<p>Acesta este&nbsp;<strong>CryptPad</strong>, editorul colaborativ bazat pe tehnologia Zero Knowledge în timp real. Totul este salvat pe măsură ce scrii.<br>Partajează link-ul către acest pad pentru a edita cu prieteni sau folosește <span class=\"fa fa-share-alt\"></span> butonul pentru a partaja <em>read-only link</em>&nbsp;permițând vizualizarea dar nu și editarea.</p>";
out.codeInitialState = "/*\n Acesta este editorul colaborativ de cod bazat pe tehnologia Zero Knowledge CryptPad.\n Ce scrii aici este criptat, așa că doar oamenii care au link-ul pot să-l acceseze.\n Poți să alegi ce limbaj de programare pus n evidență și schema de culori UI n dreapta sus.\n*/";
out.slideInitialState = "# CryptSlide\n* Acesta este un editor colaborativ bazat pe tehnologia Zero Knowledge.\n* Ce scrii aici este criptat, așa că doar oamenii care au link-ul pot să-l acceseze.\n* Nici măcar serverele nu au acces la ce scrii tu.\n* Ce vezi aici, ce auzi aici, atunci când pleci, lași aici.\n\n-\n# Cum se folosește\n1. Scrie-ți conținutul slide-urilor folosind sintaxa markdown\n - Află mai multe despre sintaxa markdown [aici](http://www.markdowntutorial.com/)\n2. Separă-ți slide-urile cu -\n3. Click pe butonul \"Play\" pentru a vedea rezultatele - Slide-urile tale sunt actualizate în timp real.";
out.slideInitialState = "# CryptSlide\n1. Scrie-ți conținutul slide-urilor folosind sintaxa markdown\n - Află mai multe despre sintaxa markdown [aici](http://www.markdowntutorial.com/)\n2. Separă-ți slide-urile cu ---\n3. Click pe butonul \"Play\" pentru a vedea rezultatele - Slide-urile tale sunt actualizate în timp real.";
out.driveReadmeTitle = "Ce este CryptPad?";
out.readme_welcome = "Bine ai venit n CryptPad !";
out.readme_p1 = "Bine ai venit în CryptPad, acesta este locul unde îți poți lua notițe, singur sau cu prietenii.";

@ -469,13 +469,8 @@ define(function () {
'<p>',
'這是&nbsp;<strong>CryptPad</strong>, 零知識即時協作編輯平台,當你輸入時一切已即存好。',
'<br>',
'分享這個工作檔案的網址連結給友人或是使用、 <span class="fa fa-share-alt" style="border: 1px solid black;color:#000;">&nbsp;分享&nbsp;</span> 按鈕分享<em>唯讀的連結</em>&nbsp;其只能看不能編寫。',
'</p>',
'<p><em>',
'來吧, 開始打字輸入吧...',
'</em></p>',
'<p>&nbsp;<br></p>'
'分享這個工作檔案的網址連結給友人或是使用、 <span class="fa fa-share-alt"></span> 按鈕分享<em>唯讀的連結</em>&nbsp;其只能看不能編寫。',
'</p>'
].join('');
out.codeInitialState = [
@ -487,14 +482,6 @@ define(function () {
out.slideInitialState = [
'# CryptSlide\n',
'* 它是零知識即時協作編輯平台。\n',
'* 你所輸入的東西會予以加密,僅有知道此網頁連結者可以接取這份文件。\n',
'* 即便是本站伺服器也不知道你輸入了什麼內容。\n',
'* 你在這裏看到的、你在這裏聽到的、當你離開本站時,讓它就留在這裏吧。\n',
'\n',
'---',
'\n',
'# 如何使用\n',
'1. 使用 markdown 語法來寫下你的投影片內容\n',
' - 進一步學習 markdown 語法 [here](http://www.markdowntutorial.com/)\n',
'2. 利用 --- 來區隔不同的投影片\n',

@ -0,0 +1,40 @@
/* jshint esversion: 6, node: true */
const Fs = require("fs");
const nThen = require("nthen");
const Saferphore = require("saferphore");
const PinnedData = require('./pinneddata');
let config;
try {
config = require('./config');
} catch (e) {
config = require('./config.example');
}
if (!config.inactiveTime || typeof(config.inactiveTime) !== "number") { return; }
let inactiveTime = +new Date() - (config.inactiveTime * 24 * 3600 * 1000);
let inactiveConfig = {
unpinned: true,
olderthan: inactiveTime,
blobsolderthan: inactiveTime
};
let toDelete;
nThen(function (waitFor) {
PinnedData.load(inactiveConfig, waitFor(function (err, data) {
if (err) {
waitFor.abort();
throw new Error(err);
}
toDelete = data;
}));
}).nThen(function () {
var sem = Saferphore.create(10);
toDelete.forEach(function (f) {
sem.take(function (give) {
Fs.unlink(f.filename, give(function (err) {
if (err) { return void console.error(err + " " + f.filename); }
console.log(f.filename + " " + f.size + " " + (+f.atime) + " " + (+new Date()));
}));
});
});
});

@ -7,7 +7,6 @@ var config;
try {
config = require('./config');
} catch (e) {
console.log("You can customize the configuration by copying config.example.js to config.js");
config = require('./config.example');
}

@ -1,10 +1,11 @@
{
"name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server",
"version": "1.26.0",
"version": "1.27.0",
"license": "AGPL-3.0-or-later",
"dependencies": {
"chainpad-server": "^2.0.0",
"express": "~4.10.1",
"express": "~4.16.0",
"nthen": "~0.1.0",
"pull-stream": "^3.6.1",
"replify": "^1.2.0",

@ -47,12 +47,13 @@ const dsFileStats = {};
const out = [];
const pinned = {};
nThen((waitFor) => {
module.exports.load = function (config, cb) {
nThen((waitFor) => {
Fs.readdir('./datastore', waitFor((err, list) => {
if (err) { throw err; }
dirList = list;
}));
}).nThen((waitFor) => {
}).nThen((waitFor) => {
dirList.forEach((f) => {
sema.take((returnAfter) => {
Fs.readdir('./datastore/' + f, waitFor(returnAfter((err, list2) => {
@ -61,13 +62,13 @@ nThen((waitFor) => {
})));
});
});
}).nThen((waitFor) => {
}).nThen((waitFor) => {
Fs.readdir('./blob', waitFor((err, list) => {
if (err) { throw err; }
dirList = list;
}));
}).nThen((waitFor) => {
}).nThen((waitFor) => {
dirList.forEach((f) => {
sema.take((returnAfter) => {
Fs.readdir('./blob/' + f, waitFor(returnAfter((err, list2) => {
@ -76,7 +77,7 @@ nThen((waitFor) => {
})));
});
});
}).nThen((waitFor) => {
}).nThen((waitFor) => {
fileList.forEach((f) => {
sema.take((returnAfter) => {
Fs.stat(f, waitFor(returnAfter((err, st) => {
@ -86,12 +87,12 @@ nThen((waitFor) => {
})));
});
});
}).nThen((waitFor) => {
}).nThen((waitFor) => {
Fs.readdir('./pins', waitFor((err, list) => {
if (err) { throw err; }
dirList = list;
}));
}).nThen((waitFor) => {
}).nThen((waitFor) => {
fileList.splice(0, fileList.length);
dirList.forEach((f) => {
sema.take((returnAfter) => {
@ -101,14 +102,14 @@ nThen((waitFor) => {
})));
});
});
}).nThen((waitFor) => {
}).nThen((waitFor) => {
fileList.forEach((f) => {
sema.take((returnAfter) => {
Fs.readFile(f, waitFor(returnAfter((err, content) => {
if (err) { throw err; }
const hashes = hashesFromPinFile(content.toString('utf8'), f);
const size = sizeForHashes(hashes, dsFileStats);
if (process.argv.indexOf('--unpinned') > -1) {
if (config.unpinned) {
hashes.forEach((x) => { pinned[x] = 1; });
} else {
out.push([f, Math.floor(size / (1024 * 1024))]);
@ -116,34 +117,56 @@ nThen((waitFor) => {
})));
});
});
}).nThen(() => {
if (process.argv.indexOf('--unpinned') > -1) {
const ot = process.argv.indexOf('--olderthan');
}).nThen(() => {
if (config.unpinned) {
let before = Infinity;
if (ot > -1) {
before = new Date(process.argv[ot+1]);
if (config.olderthan) {
before = config.olderthan;
if (isNaN(before)) {
throw new Error('--olderthan error [' + process.argv[ot+1] + '] not a valid date');
return void cb('--olderthan error [' + config.olderthan + '] not a valid date');
}
}
const bot = process.argv.indexOf('--blobsolderthan');
let blobsbefore = before;
if (bot > -1) {
blobsbefore = new Date(process.argv[bot+1]);
if (config.blobsolderthan) {
blobsbefore = config.blobsolderthan;
if (isNaN(blobsbefore)) {
throw new Error('--blobsolderthan error [' + process.argv[bot+1] + '] not a valid date');
return void cb('--blobsolderthan error [' + config.blobsolderthan + '] not a valid date');
}
}
let files = [];
Object.keys(dsFileStats).forEach((f) => {
if (!(f in pinned)) {
const isBlob = dsFileStats[f].filename.indexOf('.ndjson') === -1;
if ((+dsFileStats[f].mtime) >= ((isBlob) ? blobsbefore : before)) { return; }
console.log(dsFileStats[f].filename + " " + dsFileStats[f].size + " " +
(+dsFileStats[f].mtime));
if ((+dsFileStats[f].atime) >= ((isBlob) ? blobsbefore : before)) { return; }
files.push({
filename: dsFileStats[f].filename,
size: dsFileStats[f].size,
atime: dsFileStats[f].atime
});
}
});
cb(null, files);
} else {
out.sort((a,b) => (a[1] - b[1]));
out.forEach((x) => { console.log(x[0] + ' ' + x[1] + ' MB'); });
cb(null, out.slice());
}
});
});
};
if (!module.parent) {
let config = {};
if (process.argv.indexOf('--unpinned') > -1) { config.unpinned = true; }
const ot = process.argv.indexOf('--olderthan');
config.olderthan = ot > -1 && new Date(process.argv[ot+1]);
const bot = process.argv.indexOf('--blobsolderthan');
config.blobsolderthan = bot > -1 && new Date(process.argv[bot+1]);
module.exports.load(config, function (err, data) {
if (err) { throw new Error(err); }
if (!Array.isArray(data)) { return; }
if (config.unpinned) {
data.forEach((f) => { console.log(f.filename + " " + f.size + " " + (+f.atime)); });
} else {
data.forEach((x) => { console.log(x[0] + ' ' + x[1] + ' MB'); });
}
});
}

@ -22,7 +22,7 @@ The most recent version and all past release notes can be found [here](https://g
## Setup using Docker
See [Cryptpad-Docker](docs/cryptpad-docker.md).
See [Cryptpad-Docker](docs/cryptpad-docker.md) and the community wiki's [Docker](https://github.com/xwiki-labs/cryptpad/wiki/Docker-(with-Nginx-and-Traefik)) page for details on how to get up-and-running with Cryptpad in Docker.
## Setup using Ansible

@ -74,6 +74,24 @@
}
}
.markdown_main();
.cp-app-code-preview-empty {
display: none;
}
&.cp-app-code-preview-isempty {
display: flex;
align-items: center;
justify-content: center;
#cp-app-code-preview-content {
display: none;
}
.cp-app-code-preview-empty {
//flex: 1 1 auto;
max-height: 100%;
max-width: 100%;
display: block;
opacity: 0.2;
}
}
}
#cp-app-code-preview-content {

@ -54,6 +54,7 @@ define([
var MEDIA_TAG_MODES = Object.freeze([
'markdown',
'gfm',
'html',
'htmlembedded',
'htmlmixed',
@ -63,6 +64,29 @@ define([
'xml',
]);
var mkMarkdownTb = function (editor, framework) {
var $codeMirrorContainer = $('#cp-app-code-container');
var markdownTb = framework._.sfCommon.createMarkdownToolbar(editor);
$codeMirrorContainer.prepend(markdownTb.toolbar);
framework._.toolbar.$rightside.append(markdownTb.button);
var modeChange = function (mode) {
if (['markdown', 'gfm'].indexOf(mode) !== -1) { return void markdownTb.setState(true); }
markdownTb.setState(false);
};
return {
modeChange: modeChange
};
};
var mkHelpMenu = function (framework) {
var $codeMirrorContainer = $('#cp-app-code-container');
var helpMenu = framework._.sfCommon.createHelpMenu(['text', 'code']);
$codeMirrorContainer.prepend(helpMenu.menu);
framework._.toolbar.$drawer.append(helpMenu.button);
};
var mkPreviewPane = function (editor, CodeMirror, framework, isPresentMode) {
var $previewContainer = $('#cp-app-code-preview');
var $preview = $('#cp-app-code-preview-content');
@ -70,12 +94,20 @@ define([
var $codeMirrorContainer = $('#cp-app-code-container');
var $codeMirror = $('.CodeMirror');
var markdownTb = framework._.sfCommon.createMarkdownToolbar(editor);
$codeMirrorContainer.prepend(markdownTb.toolbar);
$('<img>', {
src: '/customize/main-favicon.png',
alt: '',
class: 'cp-app-code-preview-empty'
}).appendTo($previewContainer);
var $previewButton = framework._.sfCommon.createButton(null, true);
var $previewButton = framework._.sfCommon.createButton('preview', true);
var forceDrawPreview = function () {
try {
if (editor.getValue() === '') {
$previewContainer.addClass('cp-app-code-preview-isempty');
return;
}
$previewContainer.removeClass('cp-app-code-preview-isempty');
DiffMd.apply(DiffMd.render(editor.getValue()), $preview);
} catch (e) { console.error(e); }
};
@ -85,12 +117,6 @@ define([
forceDrawPreview();
}, 150);
$previewButton.removeClass('fa-question').addClass('fa-eye');
window.setTimeout(function () {
// setTimeout needed for tippy (tooltip), otherwise we have the browser's default
// tooltips
$previewButton.attr('title', Messages.previewButtonTitle);
});
var previewTo;
$previewButton.click(function () {
clearTimeout(previewTo);
@ -118,7 +144,7 @@ define([
}
});
framework._.toolbar.$rightside.append($previewButton).append(markdownTb.button);
framework._.toolbar.$rightside.append($previewButton);
$preview.click(function (e) {
if (!e.target) { return; }
@ -145,7 +171,6 @@ define([
}
}
});
markdownTb.setState(true);
return;
}
$editorContainer.removeClass('cp-app-code-present');
@ -153,7 +178,6 @@ define([
$previewContainer.hide();
$previewButton.removeClass('active');
$codeMirrorContainer.addClass('cp-app-code-fullpage');
markdownTb.setState(false);
};
var isVisible = function () {
@ -252,8 +276,12 @@ define([
var common = framework._.sfCommon;
var previewPane = mkPreviewPane(editor, CodeMirror, framework, isPresentMode);
var markdownTb = mkMarkdownTb(editor, framework);
mkHelpMenu(framework);
var evModeChange = Util.mkEvent();
evModeChange.reg(previewPane.modeChange);
evModeChange.reg(markdownTb.modeChange);
mkIndentSettings(editor, framework._.cpNfInner.metadataMgr);
CodeMirror.init(framework.localChange, framework._.title, framework._.toolbar);
@ -292,6 +320,8 @@ define([
framework.setTitleRecommender(CodeMirror.getHeadingText);
framework.onReady(function (newPad) {
editor.focus();
if (newPad && !CodeMirror.highlightMode) {
CodeMirror.setMode('gfm', evModeChange.fire);
//console.log("%s => %s", CodeMirror.highlightMode, CodeMirror.$language.val());
@ -315,7 +345,7 @@ define([
});
framework.onDefaultContentNeeded(function () {
editor.setValue(Messages.codeInitialState);
editor.setValue(''); //Messages.codeInitialState);
});
framework.setFileExporter(CodeMirror.getContentExtension, CodeMirror.fileExporter);
@ -335,13 +365,9 @@ define([
var getThumbnailContainer = function () {
var $preview = $('#cp-app-code-preview-content');
var $codeMirror = $('.CodeMirror');
if ($preview.length && $preview.is(':visible')) {
return $preview[0];
}
if ($codeMirror.length) {
return $codeMirror[0];
}
};
var main = function () {

@ -87,8 +87,9 @@ define(function() {
oocell: 'fa-file-excel-o',
};
// EXPERIMENTAL: Enabling "displayCreationScreen" may cause UI issues and possible loss of data
config.displayCreationScreen = false;
// Ability to create owned pads and expiring pads through a new pad creation screen.
// The new screen can be disabled by the users in their settings page
config.displayCreationScreen = true;
// Prevent anonymous users from storing pads in their drive
config.disableAnonymousStore = false;

@ -555,8 +555,11 @@ define([
$loading = $('#' + LOADING); //.show();
$loading.css('display', '');
$loading.removeClass('cp-loading-hidden');
$('.cp-loading-spinner-container').show();
if (loadingText) {
$('#' + LOADING).find('p').text(loadingText);
} else {
$('#' + LOADING).find('p').text('');
}
$container = $loading.find('.cp-loading-container');
} else {
@ -612,7 +615,10 @@ define([
if (exitable) {
$(window).focus();
$(window).keydown(function (e) {
if (e.which === 27) { $('#' + LOADING).hide(); }
if (e.which === 27) {
$('#' + LOADING).hide();
if (typeof(exitable) === "function") { exitable(); }
}
});
}
};
@ -662,16 +668,21 @@ define([
position: 'bottom',
distance: 0,
performance: true,
dynamicTitle: true,
delay: [delay, 0],
sticky: true
});
};
// This is the robust solution to remove dangling tooltips
// The mutation observer does not always find removed nodes.
setInterval(UI.clearTooltips, delay);
//setInterval(UI.clearTooltips, delay);
var checkRemoved = function (x) {
var out = false;
var xId = $(x).attr('aria-describedby');
if (xId) {
if (xId.indexOf('tippy-tooltip-') === 0) {
return true;
}
}
$(x).find('[aria-describedby]').each(function (i, el) {
var id = el.getAttribute('aria-describedby');
if (id.indexOf('tippy-tooltip-') !== 0) { return; }
@ -685,6 +696,9 @@ define([
mutations.forEach(function(mutation) {
if (mutation.type === "childList") {
for (var i = 0; i < mutation.addedNodes.length; i++) {
if ($(mutation.addedNodes[i]).attr('title')) {
addTippy(0, mutation.addedNodes[i]);
}
$(mutation.addedNodes[i]).find('[title]').each(addTippy);
}
for (var j = 0; j < mutation.removedNodes.length; j++) {

@ -177,6 +177,7 @@ define([
window.html2canvas = undefined;
Thumb.fromDOM = function (opts, cb) {
var element = opts.getContainer();
if (!element) { return; }
var todo = function () {
if (opts.filter) { opts.filter(element, true); }
window.html2canvas(element, {
@ -202,8 +203,8 @@ define([
var mkThumbnail = function () {
var content = opts.getContent();
if (content === oldThumbnailState) { return; }
Thumb.fromDOM(opts, function (err, b64) {
oldThumbnailState = content;
Thumb.fromDOM(opts, function (err, b64) {
Thumb.setPadThumbnail(common, opts.href, b64);
});
};

@ -141,7 +141,7 @@ define([
};
var getPadProperties = function (common, data, cb) {
var $d = $('<div>');
if (!data || !data.href) { return void cb(void 0, $d); }
if (!data || (!data.href && !data.roHref)) { return void cb(void 0, $d); }
if (data.href) {
$('<label>', {'for': 'cp-app-prop-link'}).text(Messages.editShare).appendTo($d);
@ -283,10 +283,11 @@ define([
present: present
});
};
var getLinkValue = function () {
var edit = $(link).find('#cp-share-editable-true').is(':checked');
var embed = $(link).find('#cp-share-embed').is(':checked');
var present = $(link).find('#cp-share-present').is(':checked');
var getLinkValue = function (initValue) {
var val = initValue || {};
var edit = initValue ? val.edit : $(link).find('#cp-share-editable-true').is(':checked');
var embed = initValue ? val.embed : $(link).find('#cp-share-embed').is(':checked');
var present = initValue ? val.present : $(link).find('#cp-share-present').is(':checked');
var hash = (edit && hashes.editHash) ? hashes.editHash : hashes.viewHash;
var href = origin + pathname + '#' + hash;
@ -375,6 +376,11 @@ define([
}
if (val.embed) { $(link).find('#cp-share-embed').attr('checked', true); }
if (val.present) { $(link).find('#cp-share-present').attr('checked', true); }
$(link).find('#cp-share-link-preview').val(getLinkValue(val));
});
common.getMetadataMgr().onChange(function () {
hashes = common.getMetadataMgr().getPrivateData().availableHashes;
$(link).find('#cp-share-link-preview').val(getLinkValue());
});
return tabs;
};
@ -457,12 +463,12 @@ define([
UIElements.createButton = function (common, type, rightside, data, callback) {
var AppConfig = common.getAppConfig();
var button;
var size = "17px";
var sframeChan = common.getSframeChannel();
var appType = (common.getMetadataMgr().getMetadata().type || 'pad').toUpperCase();
switch (type) {
case 'export':
button = $('<button>', {
'class': 'fa fa-download',
'class': 'fa fa-download cp-toolbar-icon-export',
title: Messages.exportButtonTitle,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.exportButton));
@ -473,7 +479,7 @@ define([
break;
case 'import':
button = $('<button>', {
'class': 'fa fa-upload',
'class': 'fa fa-upload cp-toolbar-icon-import',
title: Messages.importButtonTitle,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.importButton));
if (callback) {
@ -519,7 +525,8 @@ define([
if (!common.isLoggedIn()) { return; }
button = $('<button>', {
title: Messages.saveTemplateButton,
}).append($('<span>', {'class':'fa fa-bookmark', style: 'font:'+size+' FontAwesome'}));
class: 'fa fa-bookmark cp-toolbar-icon-template'
});
if (data.rt) {
button
.click(function () {
@ -564,12 +571,10 @@ define([
break;
case 'forget':
button = $('<button>', {
id: 'cryptpad-forget',
title: Messages.forgetButtonTitle,
'class': "fa fa-trash cryptpad-forget",
style: 'font:'+size+' FontAwesome'
'class': "fa fa-trash cp-toolbar-icon-forget"
});
if (callback) {
callback = typeof callback === "function" ? callback : function () {};
button
.click(common.prepareFeedback(type))
.click(function() {
@ -584,17 +589,26 @@ define([
return;
});
});
});
}
break;
case 'present':
button = $('<button>', {
title: Messages.presentButtonTitle,
'class': "fa fa-play-circle cp-app-slide-present-button", // used in slide.js
style: 'font:'+size+' FontAwesome'
'class': "fa fa-play-circle cp-toolbar-icon-present", // used in slide.js
});
break;
case 'preview':
button = $('<button>', {
title: Messages.previewButtonTitle,
'class': "fa fa-eye cp-toolbar-icon-preview",
});
break;
case 'print':
button = $('<button>', {
title: Messages.printButtonTitle,
'class': "fa fa-print cp-toolbar-icon-print",
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.printText));
break;
case 'history':
if (!AppConfig.enableHistory) {
button = $('<span>');
@ -602,7 +616,7 @@ define([
}
button = $('<button>', {
title: Messages.historyButton,
'class': "fa fa-history history",
'class': "fa fa-history cp-toolbar-icon-history",
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.historyText));
if (data.histConfig) {
button
@ -616,19 +630,25 @@ define([
button = $('<button>', {
title: Messages.moreActions,
'class': "cp-toolbar-drawer-button fa fa-ellipsis-h",
style: 'font:'+size+' FontAwesome'
});
break;
case 'mediatag':
button = $('<button>', {
'class': 'fa fa-picture-o cp-toolbar-icon-mediatag',
title: Messages.filePickerButton,
})
.click(common.prepareFeedback(type));
break;
case 'savetodrive':
button = $('<button>', {
'class': 'fa fa-cloud-upload',
'class': 'fa fa-cloud-upload cp-toolbar-icon-savetodrive',
title: Messages.canvas_saveToDrive,
})
.click(common.prepareFeedback(type));
break;
case 'hashtag':
button = $('<button>', {
'class': 'fa fa-hashtag',
'class': 'fa fa-hashtag cp-toolbar-icon-hashtag',
title: Messages.tags_title,
})
.click(common.prepareFeedback(type))
@ -636,9 +656,8 @@ define([
break;
case 'toggle':
button = $('<button>', {
'class': 'fa fa-caret-down',
})
.click(common.prepareFeedback(type));
'class': 'fa fa-caret-down cp-toolbar-icon-toggle',
});
window.setTimeout(function () {
button.attr('title', data.title);
});
@ -647,17 +666,24 @@ define([
if (!isVisible) { button.addClass('fa-caret-down'); }
else { button.addClass('fa-caret-up'); }
};
button.click(function () {
button.click(function (e) {
data.element.toggle();
var isVisible = data.element.is(':visible');
if (callback) { callback(isVisible); }
if (isVisible) {
button.addClass('cp-toolbar-button-active');
if (e.originalEvent) { Feedback.send('TOGGLE_SHOW_' + appType); }
} else {
button.removeClass('cp-toolbar-button-active');
if (e.originalEvent) { Feedback.send('TOGGLE_HIDE_' + appType); }
}
updateIcon(isVisible);
});
updateIcon(data.element.is(':visible'));
break;
case 'properties':
button = $('<button>', {
'class': 'fa fa-info-circle',
'class': 'fa fa-info-circle cp-toolbar-icon-properties',
title: Messages.propertiesButtonTitle,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'})
.text(Messages.propertiesButton))
@ -682,14 +708,28 @@ define([
if (callback) { button.click(callback); }
break;
default:
data = data || {};
var icon = data.icon || "fa-question";
button = $('<button>', {
'class': "fa fa-question",
style: 'font:'+size+' FontAwesome'
'class': "fa " + icon,
})
.click(common.prepareFeedback(type));
.click(common.prepareFeedback(data.name || 'DEFAULT'));
//.click(common.prepareFeedback(type));
if (callback) {
button.click(callback);
}
if (data.title) { button.attr('title', data.title); }
if (data.style) { button.attr('style', data.style); }
if (data.id) { button.attr('id', data.id); }
if (data.hiddenReadOnly) { button.addClass('cp-hidden-if-readonly'); }
if (data.name) {
button.addClass('cp-toolbar-icon-'+data.name);
button.click(common.prepareFeedback(data.name));
}
if (data.text) {
$('<span>', {'class': 'cp-toolbar-drawer-element'}).text(data.text)
.appendTo(button);
}
}
if (rightside) {
button.addClass('cp-toolbar-rightside-button');
@ -825,6 +865,7 @@ define([
var tbState = true;
common.getAttribute(['general', 'markdown-help'], function (e, data) {
if (e) { return void console.error(e); }
if ($(window).height() < 800) { return; }
if (data === true && $toolbarButton.length && tbState) {
$toolbarButton.click();
}
@ -841,6 +882,7 @@ define([
}
common.getAttribute(['general', 'markdown-help'], function (e, data) {
if (e) { return void console.error(e); }
if ($(window).height() < 800) { return; }
if (data === true && $toolbarButton) {
// Show the toolbar using the button to make sure the icon in the button is
// correct (caret-down / caret-up)
@ -861,6 +903,91 @@ define([
};
};
UIElements.createHelpMenu = function (common, categories) {
var type = common.getMetadataMgr().getMetadata().type || 'pad';
var setHTML = function (e, html) {
e.innerHTML = html;
return e;
};
var elements = [];
if (Messages.help && Messages.help.generic) {
Object.keys(Messages.help.generic).forEach(function (el) {
elements.push(setHTML(h('li'), Messages.help.generic[el]));
});
}
if (categories) {
categories.forEach(function (cat) {
var msgs = Messages.help[cat];
if (msgs) {
Object.keys(msgs).forEach(function (el) {
elements.push(setHTML(h('li'), msgs[el]));
});
}
});
}
var text = h('p.cp-help-text', [
h('h1', Messages.help.title),
h('ul', elements)
]);
var origin = common.getMetadataMgr().getPrivateData().origin || '';
$(text).find('a').click(function (e) {
e.preventDefault();
e.stopPropagation();
var href = $(this).attr('href');
var absolute = /^https?:\/\//i;
if (!absolute.test(href)) {
if (href.slice(0,1) !== '/') { href = '/' + href; }
href = origin + href;
}
common.openUnsafeURL(href);
});
var closeButton = h('span.cp-help-close.fa.fa-window-close');
var $toolbarButton = common.createButton('', true, {
title: Messages.hide_help_button,
text: Messages.help_button,
name: 'help'
}).addClass('cp-toolbar-button-active');
var help = h('div.cp-help-container', [
closeButton,
text
]);
var toggleHelp = function (forceClose) {
if ($(help).hasClass('cp-help-hidden')) {
if (forceClose) { return; }
common.setAttribute(['hideHelp', type], false);
$toolbarButton.addClass('cp-toolbar-button-active');
$toolbarButton.attr('title', Messages.hide_help_button);
return void $(help).removeClass('cp-help-hidden');
}
$toolbarButton.removeClass('cp-toolbar-button-active');
$toolbarButton.attr('title', Messages.show_help_button);
$(help).addClass('cp-help-hidden');
common.setAttribute(['hideHelp', type], true);
};
$(closeButton).click(function () { toggleHelp(true); });
$toolbarButton.click(function () {
toggleHelp();
});
common.getAttribute(['hideHelp', type], function (err, val) {
if ($(window).height() < 800) { return void toggleHelp(true); }
if (val === true) { toggleHelp(true); }
});
return {
menu: help,
button: $toolbarButton,
text: text
};
};
// Avatars
// Enable mediatags
@ -1495,6 +1622,7 @@ define([
if (!$blockContainer.length) {
$blockContainer = $('<div>', {
'class': 'cp-modal-container',
tabindex: 1,
'id': cfg.id
});
}
@ -1526,14 +1654,16 @@ define([
$body: $('body')
});
var $title = $('<h3>').text(Messages.fm_newFile);
var $description = $('<p>').text(Messages.creation_newPadModalDescription);
var $description = $('<p>').html(Messages.creation_newPadModalDescription);
$modal.find('.cp-modal').append($title);
$modal.find('.cp-modal').append($description);
var $advanced;
var $advancedContainer = $('<div>');
if (common.isLoggedIn()) {
var priv = common.getMetadataMgr().getPrivateData();
var c = (priv.settings.general && priv.settings.general.creation) || {};
if (AppConfig.displayCreationScreen && common.isLoggedIn() && c.skip) {
$advanced = $('<input>', {
type: 'checkbox',
checked: 'checked',
@ -1542,9 +1672,12 @@ define([
$('<label>', {
for: 'cp-app-toolbar-creation-advanced'
}).text(Messages.creation_newPadModalAdvanced).appendTo($advancedContainer);
$description.append('<br>');
$description.append(Messages.creation_newPadModalDescriptionAdvanced);
}
var $container = $('<div>');
var i = 0;
AppConfig.availablePadTypes.forEach(function (p) {
if (p === 'drive') { return; }
if (p === 'contacts') { return; }
@ -1553,7 +1686,8 @@ define([
if (!common.isLoggedIn() && AppConfig.registeredOnlyTypes &&
AppConfig.registeredOnlyTypes.indexOf(p) !== -1) { return; }
var $element = $('<li>', {
'class': 'cp-icons-element'
'class': 'cp-icons-element',
'id': 'cp-newpad-icons-'+ (i++)
}).prepend(UI.getIcon(p)).appendTo($container);
$element.append($('<span>', {'class': 'cp-icons-name'})
.text(Messages.type[p]));
@ -1561,7 +1695,7 @@ define([
$element.click(function () {
$modal.hide();
if ($advanced && $advanced.is(':checked')) {
common.sessionStorage.put(Constants.displayPadCreationScreen, true, function () {
common.sessionStorage.put(Constants.displayPadCreationScreen, true, function (){
common.openURL('/' + p + '/');
});
return;
@ -1572,11 +1706,41 @@ define([
});
});
var selected = -1;
var next = function () {
selected = ++selected % 5;
$container.find('.cp-icons-element-selected').removeClass('cp-icons-element-selected');
$container.find('#cp-newpad-icons-'+selected).addClass('cp-icons-element-selected');
};
$modal.off('keydown');
$modal.keydown(function (e) {
if (e.which === 9) {
e.preventDefault();
e.stopPropagation();
next();
return;
}
if (e.which === 13) {
if ($container.find('.cp-icons-element-selected').length === 1) {
$container.find('.cp-icons-element-selected').click();
}
return;
}
if (e.which === 32 && $advanced) {
$advanced.prop('checked', !$advanced.prop('checked'));
$modal.focus();
e.stopPropagation();
e.preventDefault();
}
});
/*var $content = createNewPadIcons($modal, isInRoot);*/
$modal.find('.cp-modal').append($container).append($advancedContainer);
window.setTimeout(function () { $modal.show(); });
//addNewPadHandlers($modal, isInRoot);
window.setTimeout(function () {
$modal.show();
$modal.focus();
});
};
UIElements.initFilePicker = function (common, cfg) {
@ -1669,7 +1833,7 @@ define([
var $body = $('body');
var $creationContainer = $('<div>', { id: 'cp-creation-container' }).appendTo($body);
var $creation = $('<div>', { id: 'cp-creation' }).appendTo($creationContainer);
var $creation = $('<div>', { id: 'cp-creation', tabindex: 1 }).appendTo($creationContainer);
var setHTML = function (e, html) {
e.innerHTML = html;
@ -1692,6 +1856,7 @@ define([
};
// Owned pads
// Default is Owned pad
var owned = h('div.cp-creation-owned', [
h('h2', [
Messages.creation_ownedTitle,
@ -1717,7 +1882,7 @@ define([
]);
$creation.append(owned);
// If set to "open pad" or not set, check "open pad"
// If set to "open pad", check "open pad"
if (!cfg.owned && typeof cfg.owned !== "undefined") {
$creation.find('#cp-creation-owned-false').attr('checked', true);
}
@ -1765,11 +1930,21 @@ define([
])
]);
$creation.append(expire);
$creation.find('#cp-creation-expire-val').keydown(function (e) {
if (e.which === 9) {
e.stopPropagation();
}
});
$creation.find('#cp-creation-expire-unit').keydown(function (e) {
if (e.which === 9 && e.shiftKey) {
e.stopPropagation();
}
});
UIElements.setExpirationValue(cfg.expire, $creation);
// Create the pad
var create = function (template) {
var getFormValues = function (template) {
// Type of pad
var ownedVal = parseInt($('input[name="cp-creation-owned"]:checked').val());
// Life time
@ -1785,12 +1960,16 @@ define([
expireVal = ($('#cp-creation-expire-val').val() || 0) * unit;
}
common.createPad({
return {
owned: ownedVal,
expire: expireVal,
template: template
}, function () {
};
};
var create = function (template) {
$creationContainer.remove();
common.createPad(getFormValues(template), function () {
cb();
});
};
@ -1829,16 +2008,69 @@ define([
});
var $button = $('<button>').text(Messages.creation_createFromScratch).appendTo($create);
$button.addClass('cp-creation-button-selected');
$button.click(function () {
create();
});
// Settings button
var origin = common.getMetadataMgr().getPrivateData().origin;
$(h('div.cp-creation-settings', h('a', {
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide();
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide();
var okTo;
var $saveButton = $('<button>').text(Messages.creation_saveSettings).click(function () {
if (okTo) { clearTimeout(okTo); }
$ok.hide();
$spinner.show();
var val = getFormValues();
NThen(function (waitFor) {
common.setAttribute(['general', 'creation', 'owned'], val.owned, waitFor(function (e) {
if (e) { return void console.error(e); }
}));
common.setAttribute(['general', 'creation', 'expire'], val.expire, waitFor(function (e) {
if (e) { return void console.error(e); }
}));
}).nThen(function () {
$spinner.hide();
$ok.show();
okTo = setTimeout(function () {
$ok.hide();
}, 5000);
});
});
$(h('div.cp-creation-settings', [
$saveButton[0],
h('br'),
h('a', {
href: origin + '/settings/#creation',
target: '_blank'
}, Messages.creation_settings))).appendTo($creation);
}, Messages.creation_settings),
$ok[0],
$spinner[0]
])).appendTo($creation);
var selected = 0;
var next = function () {
selected = ++selected % $creation.find('button').length;
$creation.find('button').removeClass('cp-creation-button-selected');
$($creation.find('button').get(selected)).addClass('cp-creation-button-selected');
};
$creation.keydown(function (e) {
if (e.which === 9) {
e.preventDefault();
e.stopPropagation();
next();
return;
}
if (e.which === 13) {
if ($creation.find('.cp-creation-button-selected').length === 1) {
$creation.find('.cp-creation-button-selected').click();
}
return;
}
});
$creation.focus();
};
UIElements.onServerError = function (common, err, toolbar, cb) {

@ -116,7 +116,7 @@ define([
} else {
var errText = "[cursor.fixSelection] At least one of the " +
"cursor nodes did not exist, could not fix selection";
console.error(errText);
//console.error(errText);
return errText;
}
} catch (e) { console.error(e); }

@ -116,6 +116,7 @@ define([
/* remove listeners from the DOM */
var removeListeners = function (root) {
if (!root) { return; }
slice(root.attributes).map(function (attr) {
if (/^on/i.test(attr.name)) {
console.log('removing attribute', attr.name, root.attributes[attr.name]);
@ -171,7 +172,9 @@ define([
return mt + '</media-tag>';
});
var safe_newHtmlFixed = domFromHTML(unsafe_newHtmlFixed).body.outerHTML;
var newDomFixed = domFromHTML(unsafe_newHtmlFixed);
if (!newDomFixed || !newDomFixed.body) { return; }
var safe_newHtmlFixed = newDomFixed.body.outerHTML;
var $div = $('<div>', {id: id}).append(safe_newHtmlFixed);
var Dom = domFromHTML($('<div>').append($div).html());

@ -104,9 +104,13 @@ define([
fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) {
removeOwnedChannel(channelId, function (obj) {
if (obj && obj.error) {
console.error(obj.error);
// If the error is that the file is already removed, nothing to
// report, it's a normal behavior (pad expired probably)
if (obj.error.code === 'ENOENT') { return; }
// RPC may not be responding
// Send a report that can be handled manually
console.error(obj.error);
Feedback.send('ERROR_DELETING_OWNED_PAD=' + channelId, true);
}
});

@ -325,11 +325,11 @@ define([
var onConnectionChange = function (info) {
if (state === STATE.DELETED) { return; }
stateChange(info.state ? STATE.INITIALIZING : STATE.DISCONNECTED);
if (info.state) {
/*if (info.state) {
UI.findOKButton().click();
} else {
UI.alert(Messages.common_connectionLost, undefined, true);
}
}*/
};
var onError = function (err) {
@ -398,11 +398,7 @@ define([
'" data-crypto-key="cryptpad:' + data.key + '"></media-tag>'), data);
}
});
$embedButton = $('<button>', {
title: Messages.filePickerButton,
'class': 'cp-toolbar-rightside-button fa fa-picture-o',
style: 'font-size: 17px'
}).click(function () {
$embedButton = common.createButton('mediatag', true).click(function () {
common.openFilePicker({
types: ['file'],
where: ['root']
@ -426,15 +422,7 @@ define([
common.getSframeChannel().onReady(waitFor());
}).nThen(function (waitFor) {
Test.registerInner(common.getSframeChannel());
if (!AppConfig.displayCreationScreen) { return; }
var priv = common.getMetadataMgr().getPrivateData();
if (priv.isNewFile) {
var c = (priv.settings.general && priv.settings.general.creation) || {};
if (c.skip && !priv.forceCreationScreen) {
return void common.createPad(c, waitFor());
}
common.getPadCreationScreen(c, waitFor());
}
common.handleNewFile(waitFor);
}).nThen(function (waitFor) {
cpNfInner = common.startRealtime({
// really basic operational transform

@ -141,7 +141,6 @@ define([
mode: defaultMode || "javascript",
readOnly: true
});
//editor.setValue(Messages.codeInitialState);
editor.focus();
var setMode = exp.setMode = function (mode, cb) {

@ -187,7 +187,7 @@ define([
upgradeURL: Cryptpad.upgradeURL
},
isNewFile: isNewFile,
isDeleted: window.location.hash.length > 0,
isDeleted: isNewFile && window.location.hash.length > 0,
forceCreationScreen: forceCreationScreen
};
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
@ -331,17 +331,27 @@ define([
});
sframeChan.on('Q_GET_PAD_ATTRIBUTE', function (data, cb) {
var href;
if (readOnly && hashes.editHash) {
// If we have a stronger hash, use it for pad attributes
href = window.location.pathname + '#' + hashes.editHash;
}
Cryptpad.getPadAttribute(data.key, function (e, data) {
cb({
error: e,
data: data
});
});
}, href);
});
sframeChan.on('Q_SET_PAD_ATTRIBUTE', function (data, cb) {
var href;
if (readOnly && hashes.editHash) {
// If we have a stronger hash, use it for pad attributes
href = window.location.pathname + '#' + hashes.editHash;
}
Cryptpad.setPadAttribute(data.key, data.value, function (e) {
cb({error:e});
});
}, href);
});
sframeChan.on('Q_GET_ATTRIBUTE', function (data, cb) {
@ -625,6 +635,7 @@ define([
// Update metadata values and send new metadata inside
parsed = Utils.Hash.parsePadUrl(window.location.href);
defaultTitle = Utils.Hash.getDefaultName(parsed);
hashes = Utils.Hash.getHashes(secret.channel, secret);
readOnly = false;
updateMeta();
@ -655,8 +666,9 @@ define([
Utils.Feedback.reportAppUsage();
if (!realtime) { return; }
if (isNewFile && Utils.LocalStore.isLoggedIn()
&& AppConfig.displayCreationScreen && cfg.useCreationScreen) { return; }
if (isNewFile && cfg.useCreationScreen) { return; }
//if (isNewFile && Utils.LocalStore.isLoggedIn()
// && AppConfig.displayCreationScreen && cfg.useCreationScreen) { return; }
startRealtime();
});

@ -90,6 +90,7 @@ define([
funcs.updateTags = callWithCommon(UIElements.updateTags);
funcs.createLanguageSelector = callWithCommon(UIElements.createLanguageSelector);
funcs.createMarkdownToolbar = callWithCommon(UIElements.createMarkdownToolbar);
funcs.createHelpMenu = callWithCommon(UIElements.createHelpMenu);
funcs.getPadCreationScreen = callWithCommon(UIElements.getPadCreationScreen);
funcs.createNewPadModal = callWithCommon(UIElements.createNewPadModal);
funcs.onServerError = callWithCommon(UIElements.onServerError);
@ -167,6 +168,30 @@ define([
};
// Store
funcs.handleNewFile = function (waitFor) {
var priv = ctx.metadataMgr.getPrivateData();
if (priv.isNewFile) {
var c = (priv.settings.general && priv.settings.general.creation) || {};
var skip = !AppConfig.displayCreationScreen || (c.skip && !priv.forceCreationScreen);
// If this is a new file but we have a hash in the URL and pad creation screen is
// not displayed, then display an error...
if (priv.isDeleted && (!funcs.isLoggedIn() || skip)) {
UI.errorLoadingScreen(Messages.inactiveError, false, function () {
UI.addLoadingScreen();
return void funcs.createPad({}, waitFor());
});
return;
}
// Otherwise, if we don't display the screen, it means it is not a deleted pad
// so we can continue and start realtime...
if (!funcs.isLoggedIn() || skip) {
return void funcs.createPad(c, waitFor());
}
// If we display the pad creation screen, it will handle deleted pads directly
console.log('here');
funcs.getPadCreationScreen(c, waitFor());
}
};
funcs.createPad = function (cfg, cb) {
ctx.sframeChan.query("Q_CREATE_PAD", {
owned: cfg.owned,

@ -318,7 +318,7 @@ define([
$span.append($rightCol);
} else {
Common.displayAvatar($span, data.avatar, name, function ($img) {
if (data.avatar && $img.length) {
if (data.avatar && $img && $img.length) {
avatars[data.avatar] = $img[0].outerHTML;
}
$span.append($rightCol);
@ -377,38 +377,15 @@ define([
config.$contentContainer.prepend($content);
}
var $ck = config.$container.find('.cke_toolbox_main');
var mobile = $('body').width() <= 600;
var hide = function () {
$content.hide();
$button.removeClass('cp-toolbar-button-active');
$ck.css({
'padding-left': '',
});
};
var show = function () {
if (Bar.isEmbed) { $content.hide(); return; }
$content.show();
if (mobile) {
$ck.hide();
}
$button.addClass('cp-toolbar-button-active');
$ck.css({
'padding-left': '175px',
});
var h = $ck.is(':visible') ? -$ck.height() : 0;
$content.css('margin-top', h+'px');
};
$(window).on('cryptpad-ck-toolbar', function () {
if (mobile && $ck.is(':visible')) { return void hide(); }
if ($content.is(':visible')) { return void show(); }
hide();
});
$(window).on('resize', function () {
mobile = $('body').width() <= 600;
var h = $ck.is(':visible') ? -$ck.height() : 0;
$content.css('margin-top', h+'px');
});
$closeIcon.click(function () {
Common.setAttribute(['toolbar', 'userlist-drawer'], false);
hide();
@ -423,7 +400,7 @@ define([
});
show();
Common.getAttribute(['toolbar', 'userlist-drawer'], function (err, val) {
if (val === false || mobile) { return void hide(); }
if (val === false || $(window).height() < 800) { return void hide(); }
show();
});
@ -593,7 +570,7 @@ define([
};
var createUnpinnedWarning0 = function (toolbar, config) {
if (true) { return; } // stub this call since it won't make it into the next release
//if (true) { return; } // stub this call since it won't make it into the next release
if (Common.isLoggedIn()) { return; }
var pd = config.metadataMgr.getPrivateData();
var o = pd.origin;
@ -633,7 +610,7 @@ define([
});
});
$('.cp-toolbar-top').append($msg);
UI.addTooltips();
//UI.addTooltips();
});
};
@ -791,7 +768,6 @@ define([
content: $('<div>').append(UI.getIcon(p)).html() + Messages.type[p]
});
});
if (Config.displayCreationScreen) {
pads_options.push({
tag: 'a',
attributes: {
@ -801,11 +777,10 @@ define([
content: '<span class="fa fa-plus-circle"></span> ' + Messages.creation_appMenuName
});
$(window).keydown(function (e) {
if (e.which === 69 && e.ctrlKey) {
if (e.which === 69 && (e.ctrlKey || (navigator.platform === "MacIntel" && e.metaKey))) {
Common.createNewPadModal();
}
});
}
var dropdownConfig = {
text: '', // Button initial text
options: pads_options, // Entries displayed in the menu
@ -1073,6 +1048,13 @@ define([
toolbar.reconnecting = function (/*userId*/) {
toolbar.connected = false;
if (toolbar.spinner) {
var state = -1;
var interval = window.setInterval(function () {
if (toolbar.connected) { clearInterval(interval); }
var dots = Array(state+1).join('.');
toolbar.spinner.text(Messages.reconnecting + dots);
if (++state > 3) { state = 0; }
}, 500);
toolbar.spinner.text(Messages.reconnecting);
}
};

@ -724,7 +724,7 @@ span {
}
}
.history {
.cp-toolbar-icon-history {
float: right;
.cp-toolbar-drawer-element {
display: none;

@ -41,7 +41,8 @@ define([
{
var APP = window.APP = {
editable: false,
mobile: function () { return $('body').width() <= 600; } // Menu and content area are not inline-block anymore for mobiles
mobile: function () { return $('body').width() <= 600; }, // Menu and content area are not inline-block anymore for mobiles
isMac: navigator.platform === "MacIntel"
};
var stringify = function (obj) {
@ -236,7 +237,7 @@ define([
'tabindex': '-1',
'data-icon': faFolder,
}, Messages.fc_newfolder)),
h('li', h('a.cp-app-drive-context-hashtag.dropdown-item', {
h('li', h('a.cp-app-drive-context-hashtag.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faTags,
}, Messages.fc_hashtag)),
@ -548,6 +549,13 @@ define([
return;
}
// Ctrl+A select all
if (e.which === 65 && (e.ctrlKey || (e.metaKey && APP.isMac))) {
$content.find('.cp-app-drive-element:not(.cp-app-drive-element-selected)')
.addClass('cp-app-drive-element-selected');
return;
}
// [Left, Up, Right, Down]
if ([37, 38, 39, 40].indexOf(e.which) === -1) { return; }
e.preventDefault();
@ -2908,6 +2916,7 @@ define([
}
// else move to trash
moveElements(paths, [TRASH], false, refresh);
return;
}
});
var isCharacterKey = function (e) {

@ -30,6 +30,8 @@ flex-flow: column;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column;
min-height: 0;
}
#cp-app-file-content.ready {
@ -134,3 +136,25 @@ media-tag {
z-index: 10000;
display: block;
}
#cp-app-file-download-view {
flex: 1;
display: flex;
min-height: 0;
align-items: center;
justify-content: center;
flex-flow: column;
media-tag {
flex: 1;
min-height: 0;
max-width: 100vw;
display: flex;
align-items: center;
justify-content: center;
&> * {
max-height: 100%;
max-width: 100%;
}
}
}

@ -100,6 +100,11 @@ define([
var title = document.title = metadata.name;
Title.updateTitle(title || Title.defaultTitle);
toolbar.addElement(['pageTitle'], {pageTitle: title});
toolbar.$rightside.append(common.createButton('forget', true));
if (common.isLoggedIn()) {
toolbar.$rightside.append(common.createButton('hashtag', true));
}
common.setPadAttribute('fileType', metadata.type);
@ -118,7 +123,6 @@ define([
$mt.attr('data-crypto-key', 'cryptpad:'+cryptKey);
var rightsideDisplayed = false;
$(window.document).on('decryption', function (e) {
var decrypted = e.originalEvent;
if (decrypted.callback) {
@ -142,13 +146,7 @@ define([
toolbar.$rightside
.append(common.createButton('export', true, {}, function () {
saveAs(decrypted.blob, decrypted.metadata.name);
}))
.append(common.createButton('forget', true, {}, function () {
// not sure what to do here
}));
if (common.isLoggedIn()) {
toolbar.$rightside.append(common.createButton('hashtag', true));
}
rightsideDisplayed = true;
}

@ -53,12 +53,8 @@ define([
$('button.login').click();
});
var hashing = false;
var test;
$('button.login').click(function () {
if (hashing) { return void console.log("hashing is already in progress"); }
hashing = true;
var shouldImport = $checkImport[0].checked;
var uname = $uname.val();
var passwd = $passwd.val();

@ -13,21 +13,39 @@
#cke_1_top {
overflow: visible;
padding: 0px;
display: flex;
}
#cke_1_toolbox {
display: inline-flex;
width: 100%;
flex-flow: column;
.cke_toolbox_main {
background-color: @colortheme_pad-toolbar-bg;
}
#cke_1_toolbox .cke_toolbar {
.cke_toolbar {
height: 28px;
padding: 2px 0;
}
}
.cke_wysiwyg_frame {
min-width: 60%;
width: 100%;
}
#cke_editor1 {
display: flex;
flex-flow: column;
height: 100%;
border: 0;
> .cke_inner {
flex: 1;
position: unset;
display: flex;
margin-top: -1px;
#cke_1_contents {
flex: 1;
display: flex;
flex-flow: column;
height: auto !important;
iframe {
flex: 1;
}
}
}
}
}
.cke_wysiwyg_frame {

@ -136,6 +136,14 @@ define([
check();
};
var mkHelpMenu = function (framework) {
var $toolbarContainer = $('.cke_toolbox_main');
var helpMenu = framework._.sfCommon.createHelpMenu(['text', 'pad']);
$toolbarContainer.before(helpMenu.menu);
framework._.toolbar.$drawer.append(helpMenu.button);
};
var mkDiffOptions = function (cursor, readOnly) {
return {
preDiffApply: function (info) {
@ -269,12 +277,10 @@ define([
element: $bar.find('.cke_toolbox_main')
};
var onClick = function (visible) {
$(window).trigger('resize');
$(window).trigger('cryptpad-ck-toolbar');
framework._.sfCommon.setAttribute(['pad', 'showToolbar'], visible);
};
framework._.sfCommon.getAttribute(['pad', 'showToolbar'], function (err, data) {
if (typeof(data) === "undefined" || data) { $('.cke_toolbox_main').show(); }
if ($(window).height() >= 800 && (typeof(data) === "undefined" || data)) { $('.cke_toolbox_main').show(); }
else { $('.cke_toolbox_main').hide(); }
var $collapse = framework._.sfCommon.createButton('toggle', true, cfg, onClick);
framework._.toolbar.$rightside.append($collapse);
@ -324,12 +330,12 @@ define([
var andThen2 = function (editor, Ckeditor, framework) {
var mediaTagMap = {};
var $bar = $('#cke_1_toolbox');
var $contentContainer = $('#cke_1_contents');
var $html = $bar.closest('html');
var $faLink = $html.find('head link[href*="/bower_components/components-font-awesome/css/font-awesome.min.css"]');
if ($faLink.length) {
$html.find('iframe').contents().find('head').append($faLink.clone());
}
var ml = Ckeditor.instances.editor1.plugins.magicline.backdoor.that.line.$;
[ml, ml.parentElement].forEach(function (el) {
el.setAttribute('class', 'non-realtime');
@ -352,6 +358,8 @@ define([
}
};
mkHelpMenu(framework);
framework.onEditableChange(function (unlocked) {
if (!framework.isReadOnly()) {
$(inner).attr('contenteditable', '' + Boolean(unlocked));
@ -421,12 +429,14 @@ define([
$bar.find('#cke_1_toolbar_collapser').hide();
if (!framework.isReadOnly()) {
addToolbarHideBtn(framework, $bar);
addToolbarHideBtn(framework, $contentContainer);
} else {
$('.cke_toolbox_main').hide();
}
framework.onReady(function (newPad) {
editor.focus();
if (!module.isMaximized) {
module.isMaximized = true;
$('iframe.cke_wysiwyg_frame').css('width', '');
@ -434,7 +444,6 @@ define([
}
$('body').addClass('app-pad');
editor.focus();
if (newPad) {
cursor.setToEnd();
} else if (framework.isReadOnly()) {
@ -464,10 +473,17 @@ define([
$iframe.find('html').addClass('cke_body_width');
}
});
/*setTimeout(function () {
$('iframe.cke_wysiwyg_frame').focus();
editor.focus();
console.log(editor);
console.log(editor.focusManager);
$(window).trigger('resize');
});*/
});
framework.onDefaultContentNeeded(function () {
documentBody.innerHTML = Messages.initialState;
inner.innerHTML = '<p></p>';
});
var importMediaTags = function (dom, cb) {
@ -561,9 +577,9 @@ define([
nThen(function (waitFor) {
Framework.create({
toolbarContainer: '#cke_1_toolbox',
contentContainer: '#cke_1_contents',
contentContainer: '#cke_editor1 > .cke_inner',
patchTransformer: ChainPad.NaiveJSONTransformer,
thumbnail: {
/*thumbnail: {
getContainer: function () { return $('iframe').contents().find('html')[0]; },
filter: function (el, before) {
if (before) {
@ -584,7 +600,7 @@ define([
var range = module.cursor.makeRange();
module.cursor.fixSelection(sel, range);
}
}
}*/
}, waitFor(function (fw) { window.APP.framework = framework = fw; }));
nThen(function (waitFor) {
@ -608,9 +624,10 @@ define([
var backColor = AppConfig.appBackgroundColor;
var newCss = '.cke_body_width { background: '+ backColor +'; height: 100%; }' +
'.cke_body_width body {' +
'max-width: 50em; padding: 10px 30px; margin: 0 auto; min-height: 100%;'+
'box-sizing: border-box;'+
'}';
'max-width: 50em; padding: 20px 30px; margin: 0 auto; min-height: 100%;'+
'box-sizing: border-box; overflow: auto;'+
'}' +
'.cke_body_width body > *:first-child { margin-top: 0; }';
Ckeditor.addCss(newCss);
Ckeditor.plugins.addExternal('mediatag','/pad/', 'mediatag-plugin.js');
module.ckeditor = editor = Ckeditor.replace('editor1', {
@ -624,6 +641,15 @@ define([
height: Messages.pad_mediatagHeight
};
Links.addSupportForOpeningLinksInNewTab(Ckeditor)({editor: editor});
}).nThen(function () {
// Move ckeditor parts to have a structure like the other apps
var $toolbarContainer = $('#cke_1_top');
var $contentContainer = $('#cke_1_contents');
var $mainContainer = $('#cke_editor1');
$contentContainer.prepend($toolbarContainer.find('.cke_toolbox_main'));
$mainContainer.prepend($toolbarContainer);
$contentContainer.find('.cke_toolbox_main').addClass('cke_reset_all');
$toolbarContainer.removeClass('cke_reset_all');
}).nThen(waitFor());
}).nThen(function (/*waitFor*/) {

@ -14,6 +14,7 @@ define([
'/common/sframe-common-codemirror.js',
'/common/common-thumbnail.js',
'/common/common-interface.js',
'/common/hyperscript.js',
'/customize/messages.js',
'cm/lib/codemirror',
'/common/test.js',
@ -43,6 +44,7 @@ define([
SframeCM,
Thumb,
UI,
h,
Messages,
CMeditor,
Test)
@ -61,8 +63,6 @@ define([
var debug = $.noop; //console.log;
var HIDE_INTRODUCTION_TEXT = "hide-text";
var metadataMgr;
var Title;
var common;
@ -628,29 +628,6 @@ define([
APP.editor.refresh();
};
var updateHelpButton = function () {
if (!APP.$helpButton) { return; }
var help = $('#cp-app-poll-help').is(':visible');
var msg = (help ? Messages.poll_hide_help_button : Messages.poll_show_help_button);
APP.$helpButton.attr('title', msg);
if (help) {
APP.$helpButton.addClass('cp-toolbar-button-active');
return;
}
APP.$helpButton.removeClass('cp-toolbar-button-active');
};
var showHelp = function(help) {
if (typeof help === 'undefined') {
help = !$('#cp-app-poll-help').is(':visible');
}
var msg = (help ? Messages.poll_hide_help_button : Messages.poll_show_help_button);
$('#cp-app-poll-help').toggle(help);
$('#cp-app-poll-action-help').text(msg);
updateHelpButton();
};
var setEditable = function (editable) {
APP.locked = APP.readOnly || !editable;
@ -843,6 +820,7 @@ define([
var checkDeletedCells = function () {
// faster than forEach?
var c;
if (!APP.proxy || !APP.proxy.content) { return; }
for (var k in APP.proxy.content.cells) {
c = Render.getCoordinates(k);
if (APP.proxy.content.colsOrder.indexOf(c[0]) === -1 ||
@ -1087,7 +1065,7 @@ define([
setTimeout(waitFor());
}).nThen(function (waitFor) {
// Switch to non-admin mode
$('.cp-toolbar-rightside-button.fa-check').click();
$('.cp-toolbar-icon-publish').click();
setTimeout(waitFor());
}).nThen(function (waitFor) {
$('.cp-app-poll-comments-add-name').val("Mr.Me").keyup();
@ -1139,13 +1117,13 @@ define([
});
}
setEditable(false);
UI.alert(Messages.common_connectionLost, undefined, true);
//UI.alert(Messages.common_connectionLost, undefined, true);
};
var onReconnect = function () {
if (APP.unrecoverable) { return; }
setEditable(true);
UI.findOKButton().click();
//UI.findOKButton().click();
};
var getHeadingText = function () {
@ -1221,15 +1199,16 @@ define([
var $export = common.createButton('export', true, {}, exportFile);
$drawer.append($export);
var $help = common.createButton('', true).click(function () { showHelp(); })
.appendTo($rightside);
APP.$helpButton = $help;
updateHelpButton();
var helpMenu = common.createHelpMenu(['poll']);
$('#cp-app-poll-form').prepend(helpMenu.menu);
$drawer.append(helpMenu.button);
if (APP.readOnly) { publish(true); return; }
var $publish = common.createButton('', true)
.removeClass('fa-question').addClass('fa-check')
.click(function () { publish(!APP.proxy.published); }).appendTo($rightside);
var $publish = common.createButton('', true, {
name: 'publish',
icon: 'fa-check',
hiddenReadOnly: true
}).click(function () { publish(!APP.proxy.published); }).appendTo($rightside);
APP.$publishButton = $publish;
updatePublishButton();
@ -1244,11 +1223,7 @@ define([
}
};
common.initFilePicker(fileDialogCfg);
APP.$mediaTagButton = $('<button>', {
title: Messages.filePickerButton,
'class': 'cp-toolbar-rightside-button fa fa-picture-o',
style: 'font-size: 17px'
}).click(function () {
APP.$mediaTagButton = common.createButton('mediatag', true).click(function () {
var pickerCfg = {
types: ['file'],
where: ['root']
@ -1274,16 +1249,9 @@ define([
}).nThen(function (waitFor) {
common.getSframeChannel().onReady(waitFor());
}).nThen(function (waitFor) {
if (!AppConfig.displayCreationScreen) { return; }
var priv = common.getMetadataMgr().getPrivateData();
if (priv.isNewFile) {
var c = (priv.settings.general && priv.settings.general.creation) || {};
if (c.skip && !priv.forceCreationScreen) {
return void common.createPad(c, waitFor());
}
common.getPadCreationScreen(c, waitFor());
}
common.handleNewFile(waitFor);
}).nThen(function (/* waitFor */) {
console.log('here');
Test.registerInner(common.getSframeChannel());
var metadataMgr = common.getMetadataMgr();
APP.locked = APP.readOnly = metadataMgr.getPrivateData().readOnly;
@ -1351,18 +1319,6 @@ define([
})
.on('disconnect', onDisconnect)
.on('reconnect', onReconnect);
common.getAttribute(['poll', HIDE_INTRODUCTION_TEXT], function (e, value) {
if (e) { console.error(e); }
if (!value) {
common.setAttribute(['poll', HIDE_INTRODUCTION_TEXT], "1", function (e) {
if (e) { console.error(e); }
});
showHelp(true);
} else {
showHelp(false);
}
});
});
};
main();

@ -335,13 +335,13 @@ define([
type: 'radio',
name: 'cp-creation-owned',
value: 1,
checked: 'checked'
}),
h('label', { 'for': 'cp-creation-owned-true' }, Messages.creation_ownedTrue),
h('input#cp-creation-owned-false.cp-creation-owned-value', {
type: 'radio',
name: 'cp-creation-owned',
value: 0,
checked: 'checked'
}),
h('label', { 'for': 'cp-creation-owned-false' }, Messages.creation_ownedFalse),
h('span.fa.fa-check', {title: Messages.saved}),
@ -364,8 +364,8 @@ define([
});
});
common.getAttribute(['general', 'creation', 'owned'], function (e, val) {
if (val) {
$owned.find('#cp-creation-owned-true').attr('checked', true);
if (!val && typeof val !== "undefined") {
$owned.find('#cp-creation-owned-false').attr('checked', true);
}
});

@ -221,6 +221,16 @@ div#cp-app-slide-modal {
background-color: black;
color: white;
.cp-app-slide-isempty {
display: flex;
align-items: center;
justify-content: center;
img {
max-width: 100%;
max-height: 100%;
}
}
/* Navigation buttons */
.cp-app-slide-modal-button {
position: absolute;

@ -9,6 +9,7 @@ define([
'/common/common-util.js',
'/common/common-hash.js',
'/common/common-interface.js',
'/common/diffMarked.js',
'/customize/messages.js',
'cm/lib/codemirror',
@ -53,6 +54,7 @@ define([
Util,
Hash,
UI,
DiffMd,
Messages,
CMeditor)
{
@ -71,9 +73,7 @@ define([
};
var mkSlidePreviewPane = function (framework, $contentContainer) {
var $previewButton = framework._.sfCommon.createButton(null, true);
$previewButton.removeClass('fa-question').addClass('fa-eye');
$previewButton.attr('title', Messages.previewButtonTitle);
var $previewButton = framework._.sfCommon.createButton('preview', true);
$previewButton.click(function () {
var $c = $contentContainer;
if ($c.hasClass('cp-app-slide-preview')) {
@ -100,19 +100,16 @@ define([
});
};
var mkPrintButton = function (framework, editor, $content, $print, $toolbarDrawer) {
var $printButton = $('<button>', {
title: Messages.printButtonTitle,
'class': 'cp-toolbar-rightside-button fa fa-print',
style: 'font-size: 17px'
}).click(function () {
var mkPrintButton = function (framework, editor, $content, $print) {
var $printButton = framework._.sfCommon.createButton('print', true);
$printButton.click(function () {
Slide.update(editor.getValue(), true);
$print.html($content.html());
window.focus();
window.print();
framework.feedback('PRINT_SLIDES');
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.printText));
$toolbarDrawer.append($printButton);
});
framework._.toolbar.$drawer.append($printButton);
};
// Flag to check if a file from the filepicker is a mediatag for the slides or a background image
@ -120,7 +117,7 @@ define([
isBackground: false
};
var mkSlideOptionsButton = function (framework, slideOptions, $toolbarDrawer) {
var mkSlideOptionsButton = function (framework, slideOptions) {
var metadataMgr = framework._.cpNfInner.metadataMgr;
var updateSlideOptions = function (newOpt) {
if (JSONSortify(newOpt) !== JSONSortify(slideOptions)) {
@ -317,14 +314,17 @@ define([
return $container;
};
var $slideOptions = $('<button>', {
var $optionsButton = framework._.sfCommon.createButton(null, true, {
icon: 'fa-cog',
title: Messages.slideOptionsTitle,
'class': 'cp-toolbar-rightside-button fa fa-cog cp-hidden-if-readonly',
style: 'font-size: 17px'
}).click(function () {
hiddenReadOnly: true,
text: Messages.slideOptionsText,
name: 'options'
});
$optionsButton.click(function () {
$('body').append(createPrintDialog());
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.slideOptionsText));
$toolbarDrawer.append($slideOptions);
});
framework._.toolbar.$drawer.append($optionsButton);
metadataMgr.onChange(function () {
var md = metadataMgr.getMetadata();
@ -361,17 +361,21 @@ define([
framework.localChange();
};
var $back = $('<button>', {
id: SLIDE_BACKCOLOR_ID,
'class': 'fa fa-square cp-toolbar-rightside-button cp-hidden-if-readonly',
'style': 'font-family: FontAwesome; color: #000;',
title: Messages.backgroundButtonTitle
var $back = framework._.sfCommon.createButton(null, true, {
icon: 'fa-square',
title: Messages.backgroundButtonTitle,
hiddenReadOnly: true,
name: 'background',
style: 'color: #000;',
id: SLIDE_BACKCOLOR_ID
});
var $text = $('<button>', {
id: SLIDE_COLOR_ID,
'class': 'fa fa-i-cursor cp-toolbar-rightside-button cp-hidden-if-readonly',
'style': 'font-family: FontAwesome; font-weight: bold; color: #fff;',
title: Messages.colorButtonTitle
var $text = framework._.sfCommon.createButton(null, true, {
icon: 'fa-i-cursor',
title: Messages.colorButtonTitle,
hiddenReadOnly: true,
name: 'color',
style: 'font-weight: bold; color: #FFF;',
id: SLIDE_COLOR_ID
});
var $testColor = $('<input>', { type: 'color', value: '!' });
var $check = $("#cp-app-slide-colorpicker");
@ -395,7 +399,7 @@ define([
$foregroundPicker.click();
});
framework._.toolbar.$rightside.append($back).append($text);
framework._.toolbar.$rightside.append($text).append($back);
metadataMgr.onChange(function () {
var md = metadataMgr.getMetadata();
@ -426,6 +430,14 @@ define([
framework._.toolbar.$rightside.append(markdownTb.button);
};
var mkHelpMenu = function (framework) {
var $codeMirrorContainer = $('#cp-app-slide-editor-container');
var helpMenu = framework._.sfCommon.createHelpMenu(['text', 'slide']);
$codeMirrorContainer.prepend(helpMenu.menu);
framework._.toolbar.$drawer.append(helpMenu.button);
};
var activateLinks = function ($content, framework) {
$content.click(function (e) {
if (!e.target) { return; }
@ -459,12 +471,13 @@ define([
activateLinks($content, framework);
Slide.setModal(framework._.sfCommon, $modal, $content, slideOptions, Messages.slideInitialState);
mkPrintButton(framework, editor, $content, $print, $toolbarDrawer);
mkPrintButton(framework, editor, $content, $print);
mkSlideOptionsButton(framework, slideOptions, $toolbarDrawer);
mkColorConfiguration(framework, $modal);
mkFilePicker(framework, editor);
mkSlidePreviewPane(framework, $contentContainer);
mkMarkdownToolbar(framework, editor);
mkHelpMenu(framework);
CodeMirror.configureTheme(common);
@ -489,6 +502,8 @@ define([
});
framework.onReady(function (/*newPad*/) {
editor.focus();
CodeMirror.setMode('markdown', function () { });
Slide.onChange(function (o, n, l) {
var slideNumber = '';
@ -519,7 +534,7 @@ define([
});
framework.onDefaultContentNeeded(function () {
CodeMirror.contentUpdate({ content: Messages.slideInitialState });
CodeMirror.contentUpdate({ content: '' });
});
Slide.setTitle(framework._.title);
@ -539,14 +554,10 @@ define([
};
var getThumbnailContainer = function () {
var $codeMirror = $('.CodeMirror');
var $c = $('#cp-app-slide-editor');
if ($c.hasClass('cp-app-slide-preview')) {
return $('.cp-app-slide-frame').first()[0];
}
if ($codeMirror.length) {
return $codeMirror[0];
}
};
var main = function () {

@ -75,6 +75,20 @@ define([
if (typeof(Slide.content) !== 'string') { return; }
var c = Slide.content;
if (c === '') {
var $empty = $('<img>', {
src: '/customize/main-favicon.png',
alt: '',
class: 'cp-app-code-preview-empty'
});
$content.html('').append($empty);
$content.addClass('cp-app-slide-isempty');
return;
//c = $('<div>').append($empty).html();
}
$content.removeClass('cp-app-slide-isempty');
var mediatagBg = '';
if (options.background && options.background.mt) {
mediatagBg = options.background.mt;
@ -125,7 +139,6 @@ define([
change(null, Slide.index);
Common.setPresentUrl(true);
$('.cp-app-slide-present-button').hide();
updateFontSize();
return;
}
@ -133,8 +146,6 @@ define([
Common.setTabTitle(); // Remove the slide number from the title
Common.setPresentUrl(false);
change(Slide.index, null);
$('.cp-app-slide-present-button').show();
$('.cp-app-slide-source-button').hide();
$modal.removeClass('cp-app-slide-shown');
updateFontSize();
};

@ -147,6 +147,8 @@ define([
$span.text($input.val().trim());
$span.show();
}
}).on('click mousedown', function (e) {
e.stopPropagation();
}).appendTo($taskDiv);
$span.text(entry.task)

@ -31,10 +31,24 @@
display: none;
}
.cp-toolbar-rightside {
.cp-toolbar-icon-savetodrive { order: 13; }
.cp-toolbar-icon-embedImage { order: 12; }
.cp-toolbar-icon-mediatag { order: 11; }
.cp-toolbar-icon-color { order: 10; }
}
#cp-app-whiteboard-media-hidden {
display: none;
}
#cp-app-whiteboard-container {
flex: 1;
display: flex;
flex-flow: column;
overflow: auto;
}
// created in the html
#cp-app-whiteboard-canvas-area {
flex: 1;
@ -44,6 +58,8 @@
.cp-app-whiteboard-canvas-container {
margin: auto;
background: white;
flex: 1;
min-height: 0;
& > canvas {
border: 1px solid black;
}
@ -64,7 +80,7 @@
border-top: 1px solid black;
background: white;
padding: 1em;
padding: 10px;
& > * + * {
margin: 0;
@ -120,7 +136,7 @@
display: flex;
justify-content: space-between;
padding: 1em;
padding: 10px;
span.cp-app-whiteboard-palette-color {
height: 4vw;

@ -312,12 +312,13 @@ define([
return;
}
var $color = APP.$color = $('<button>', {
id: "cp-app-whiteboard-color-picker",
var $color = APP.$color = common.createButton(null, true, {
icon: 'fa-square',
title: Messages.canvas_chooseColor,
'class': "fa fa-square cp-toolbar-rightside-button",
})
.on('click', function () {
name: 'color',
id: 'cp-app-whiteboard-color-picker'
});
$color.on('click', function () {
pickColor($color.css('background-color'), function (color) {
setColor(color);
});
@ -453,6 +454,11 @@ define([
var $properties = common.createButton('properties', true);
toolbar.$drawer.append($properties);
var $appContainer = $('#cp-app-whiteboard-container');
var helpMenu = common.createHelpMenu(['whiteboard']);
$appContainer.prepend(helpMenu.menu);
toolbar.$drawer.append(helpMenu.button);
if (!readOnly) {
makeColorButton($rightside);
@ -469,10 +475,11 @@ define([
};
reader.readAsDataURL(file);
};
common.createButton('', true)
.attr('title', Messages.canvas_imageEmbed)
.removeClass('fa-question').addClass('fa-file-image-o')
.click(function () {
common.createButton('', true, {
title: Messages.canvas_imageEmbed,
icon: 'fa-file-image-o',
name: 'embedImage'
}).click(function () {
$('<input>', {type:'file'}).on('change', onUpload).click();
}).appendTo($rightside);
@ -493,11 +500,7 @@ define([
}
};
common.initFilePicker(fileDialogCfg);
APP.$mediaTagButton = $('<button>', {
title: Messages.filePickerButton,
'class': 'cp-toolbar-rightside-button fa fa-picture-o',
style: 'font-size: 17px'
}).click(function () {
APP.$mediaTagButton = common.createButton('mediatag', true).click(function () {
var pickerCfg = {
types: ['file'],
where: ['root'],
@ -610,9 +613,9 @@ define([
setEditable(info.state);
if (info.state) {
initializing = true;
UI.findOKButton().click();
//UI.findOKButton().click();
} else {
UI.alert(Messages.common_connectionLost, undefined, true);
//UI.alert(Messages.common_connectionLost, undefined, true);
}
};
@ -662,15 +665,7 @@ define([
}).nThen(function (waitFor) {
common.getSframeChannel().onReady(waitFor());
}).nThen(function (waitFor) {
if (!AppConfig.displayCreationScreen) { return; }
var priv = common.getMetadataMgr().getPrivateData();
if (priv.isNewFile) {
var c = (priv.settings.general && priv.settings.general.creation) || {};
if (c.skip && !priv.forceCreationScreen) {
return void common.createPad(c, waitFor());
}
common.getPadCreationScreen(c, waitFor());
}
common.handleNewFile(waitFor);
}).nThen(function (/*waitFor*/) {
andThen(common);
});

Loading…
Cancel
Save