From 299a2f4c080810faf0f6101deca5543e6c8f20a0 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Mon, 19 Feb 2018 12:09:57 +0100
Subject: [PATCH 01/21] FAQ placeholder

---
 config.example.js                       |  3 ++-
 customize.dist/pages.js                 | 36 +++++++++++++++++++++++++
 customize.dist/src/less2/main.less      |  1 +
 customize.dist/translations/messages.js | 24 +++++++++++++++++
 4 files changed, 63 insertions(+), 1 deletion(-)

diff --git a/config.example.js b/config.example.js
index 2c234aee4..7e728451a 100644
--- a/config.example.js
+++ b/config.example.js
@@ -126,7 +126,8 @@ module.exports = {
         'about',
         'contact',
         'what-is-cryptpad',
-        'features'
+        'features',
+        'faq'
     ],
 
     /*  Limits, Donations, Subscriptions and Contact
diff --git a/customize.dist/pages.js b/customize.dist/pages.js
index a9fbeabde..37bdc5b42 100644
--- a/customize.dist/pages.js
+++ b/customize.dist/pages.js
@@ -373,6 +373,42 @@ define([
         ]);
     };
 
+    Pages['/faq.html'] = function () {
+        var categories = [];
+        var faq = Msg.faq;
+        Object.keys(faq).forEach(function (c) {
+            var questions = [];
+            Object.keys(faq[c]).forEach(function (q) {
+                var item = faq[c][q];
+                if (typeof item !== "object") { return; }
+                var answer = h('p.cp-faq-questions-a');
+                var question = h('p.cp-faq-questions-q');
+                $(question).click(function () {
+                    if ($(answer).is(':visible')) {
+                        return void $(answer).slideUp();
+                    }
+                    $(answer).slideDown();
+                });
+                questions.push(h('div.cp-faq-questions-items', [
+                    setHTML(question, item.q),
+                    setHTML(answer, item.a)
+                ]));
+            });
+            categories.push(h('div.cp-faq-category', [
+                h('h3', faq[c].title),
+                h('div.cp-faq-category-questions', questions)
+            ]));
+        });
+        return h('div#cp-main', [
+            infopageTopbar(),
+            h('div.container.cp-container', [
+                h('center', h('h1', Msg.faq_title)),
+                h('div.cp-faq-container', categories)
+            ]),
+            infopageFooter()
+        ]);
+    };
+
     Pages['/terms.html'] = function () {
         return h('div#cp-main', [
             infopageTopbar(),
diff --git a/customize.dist/src/less2/main.less b/customize.dist/src/less2/main.less
index c1bb5bba4..dadbef539 100644
--- a/customize.dist/src/less2/main.less
+++ b/customize.dist/src/less2/main.less
@@ -10,6 +10,7 @@ body.cp-page-what-is-cryptpad { @import "./pages/page-what-is-cryptpad.less"; }
 body.cp-page-about { @import "./pages/page-about.less"; }
 body.cp-page-privacy { @import "./pages/page-privacy.less"; }
 body.cp-page-features { @import "./pages/page-features.less"; }
+body.cp-page-faq { @import "./pages/page-faq.less"; }
 body.cp-page-terms { @import "./pages/page-terms.less"; }
 
 // Set the HTML style for the apps which shouldn't have a body scrollbar
diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js
index 956aa2147..eaceee812 100644
--- a/customize.dist/translations/messages.js
+++ b/customize.dist/translations/messages.js
@@ -759,6 +759,30 @@ define(function () {
     out.features_f_storage_anon = "Pads deleted after 3 months";
     out.features_f_storage_registered = "Free: 50MB<br>Premium: 5GB/20GB/50GB";
 
+    // faq.html
+
+    out.faq_link = "FAQ";
+    out.faq_title = "Frequently Asked Questions";
+    out.faq = {};
+    out.faq.cat1 = {
+        title: 'Category 1',
+        q1: {
+            q: 'What is a pad?',
+            a: 'A realtime collaborative document...'
+        },
+        q2: {
+            q: 'Question 2?',
+            a: '42'
+        }
+    };
+    out.faq.cat2 = {
+        title: 'Category 2',
+        q1: {
+            q: 'A new question?',
+            a: 'The answer'
+        }
+    };
+
     // terms.html
 
     out.tos_title = "CryptPad Terms of Service";

From 5d584625e4ff1109d361ed080c6903493184e388 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Mon, 19 Feb 2018 12:11:45 +0100
Subject: [PATCH 02/21] FAQ pages

---
 customize.dist/faq.html                      | 17 +++++++++++
 customize.dist/src/less2/pages/page-faq.less | 30 ++++++++++++++++++++
 2 files changed, 47 insertions(+)
 create mode 100644 customize.dist/faq.html
 create mode 100644 customize.dist/src/less2/pages/page-faq.less

diff --git a/customize.dist/faq.html b/customize.dist/faq.html
new file mode 100644
index 000000000..d485505dd
--- /dev/null
+++ b/customize.dist/faq.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="cp">
+<!-- If this file is not called customize.dist/src/template.html, it is generated -->
+<head>
+    <title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
+    <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+    <link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
+    <script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
+</head>
+<body class="html">
+    <noscript>
+        <p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
+        <p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
+    </noscript>
+</html>
+
diff --git a/customize.dist/src/less2/pages/page-faq.less b/customize.dist/src/less2/pages/page-faq.less
new file mode 100644
index 000000000..e24dc767c
--- /dev/null
+++ b/customize.dist/src/less2/pages/page-faq.less
@@ -0,0 +1,30 @@
+@import (once) "../include/infopages.less";
+@import (once) "../include/colortheme-all.less";
+
+.infopages_main();
+.infopages_topbar();
+
+.cp-faq-container {
+    .cp-faq-questions-q {
+        color: #3a84b6;
+        padding: 0;
+        margin-bottom: 0;
+        margin-top: 5px;
+        cursor: pointer;
+        -webkit-touch-callout: none;
+        -webkit-user-select: none;
+        -khtml-user-select: none;
+        -moz-user-select: none;
+        -ms-user-select: none;
+        user-select: none;
+    }
+    .cp-faq-questions-q:hover {
+        color: #2e688f;
+        text-decoration: underline;
+    }
+    .cp-faq-questions-a {
+        display: none;
+        padding: 0;
+    }
+}
+

From 7ede2e1a07ed3d1dadb09487086ee235507c04f5 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Tue, 20 Feb 2018 18:43:14 +0100
Subject: [PATCH 03/21] Fix cursor position when editing a task in todo

---
 www/todo/inner.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/www/todo/inner.js b/www/todo/inner.js
index f34592568..136a908e9 100644
--- a/www/todo/inner.js
+++ b/www/todo/inner.js
@@ -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)

From a0ec51dde57319b95478ec4a4336893072498821 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Thu, 22 Feb 2018 12:43:06 +0100
Subject: [PATCH 04/21] Fix share modal with new pads

---
 www/common/common-ui-elements.js  | 4 ++++
 www/common/sframe-common-outer.js | 1 +
 2 files changed, 5 insertions(+)

diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 1d41d8308..c8b118694 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -378,6 +378,10 @@ define([
             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;
     };
     UIElements.createFileShareModal = function (config) {
diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js
index 57261372d..70d1f72ac 100644
--- a/www/common/sframe-common-outer.js
+++ b/www/common/sframe-common-outer.js
@@ -625,6 +625,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();
 

From 22f130d94814828a60cc5442ae4ec3de3c8f218a Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Mon, 26 Feb 2018 10:41:37 +0100
Subject: [PATCH 05/21] Remove lag when displaying loading screen in login and
 register

---
 customize.dist/login.js | 6 ++++--
 www/login/main.js       | 4 ----
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/customize.dist/login.js b/customize.dist/login.js
index 5511637e8..fde218d21 100644
--- a/customize.dist/login.js
+++ b/customize.dist/login.js
@@ -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);
     };
 
diff --git a/www/login/main.js b/www/login/main.js
index f0c6c7d22..b3f08a0cf 100644
--- a/www/login/main.js
+++ b/www/login/main.js
@@ -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();

From 329fd61bb1ce4efee93245d21ec13b6239d1214d Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Mon, 26 Feb 2018 10:49:35 +0100
Subject: [PATCH 06/21] Remove lag when creating a pad from the pad creation
 screen

---
 www/common/common-ui-elements.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 27c583e08..2a892cae6 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -1764,6 +1764,7 @@ define([
 
         // Create the pad
         var create = function (template) {
+            $creationContainer.remove();
             // Type of pad
             var ownedVal = parseInt($('input[name="cp-creation-owned"]:checked').val());
             // Life time
@@ -1784,7 +1785,6 @@ define([
                 expire: expireVal,
                 template: template
             }, function () {
-                $creationContainer.remove();
                 cb();
             });
         };

From 5c53868c3bbd6bca0541c1ea44ff767af56c6da1 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Mon, 26 Feb 2018 18:23:12 +0100
Subject: [PATCH 07/21] Delete pads after 3 months of inactivity

---
 config.example.js                             |   8 +
 .../src/less2/include/colortheme.less         |   6 +-
 customize.dist/src/less2/include/toolbar.less |   2 +
 customize.dist/translations/messages.fr.js    |   3 +-
 customize.dist/translations/messages.js       |   3 +-
 delete-inactive.js                            |  40 ++++
 expire-channels.js                            |   1 -
 pinneddata.js                                 | 207 ++++++++++--------
 www/common/common-interface.js                |   8 +-
 www/common/sframe-app-framework.js            |  10 +-
 www/common/sframe-common-outer.js             |   7 +-
 www/common/sframe-common.js                   |  24 ++
 www/common/toolbar3.js                        |   2 +-
 www/poll/inner.js                             |  11 +-
 www/whiteboard/inner.js                       |  10 +-
 15 files changed, 212 insertions(+), 130 deletions(-)
 create mode 100644 delete-inactive.js

diff --git a/config.example.js b/config.example.js
index 7e728451a..3aace0d4a 100644
--- a/config.example.js
+++ b/config.example.js
@@ -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.
      */
diff --git a/customize.dist/src/less2/include/colortheme.less b/customize.dist/src/less2/include/colortheme.less
index c8848d02b..1a2b143e5 100644
--- a/customize.dist/src/less2/include/colortheme.less
+++ b/customize.dist/src/less2/include/colortheme.less
@@ -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;
diff --git a/customize.dist/src/less2/include/toolbar.less b/customize.dist/src/less2/include/toolbar.less
index 435476090..f8cd43fd0 100644
--- a/customize.dist/src/less2/include/toolbar.less
+++ b/customize.dist/src/less2/include/toolbar.less
@@ -505,6 +505,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 {
@@ -792,6 +793,7 @@
         }
         .cp-toolbar-share-button {
             width: 50px;
+            text-align: center;
         }
     }
     .cp-toolbar-rightside {
diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js
index 2d21cf9a6..5e681e699 100644
--- a/customize.dist/translations/messages.fr.js
+++ b/customize.dist/translations/messages.fr.js
@@ -29,11 +29,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";
diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js
index eaceee812..c5f7bcd8c 100644
--- a/customize.dist/translations/messages.js
+++ b/customize.dist/translations/messages.js
@@ -30,11 +30,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";
diff --git a/delete-inactive.js b/delete-inactive.js
new file mode 100644
index 000000000..16f41da45
--- /dev/null
+++ b/delete-inactive.js
@@ -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()));
+            }));
+        });
+    });
+});
diff --git a/expire-channels.js b/expire-channels.js
index 4e22dfce4..a42a3af58 100644
--- a/expire-channels.js
+++ b/expire-channels.js
@@ -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');
 }
 
diff --git a/pinneddata.js b/pinneddata.js
index d7848c7df..378f34ad7 100644
--- a/pinneddata.js
+++ b/pinneddata.js
@@ -47,103 +47,126 @@ const dsFileStats = {};
 const out = [];
 const pinned = {};
 
-nThen((waitFor) => {
-    Fs.readdir('./datastore', waitFor((err, list) => {
-        if (err) { throw err; }
-        dirList = list;
-    }));
-}).nThen((waitFor) => {
-    dirList.forEach((f) => {
-        sema.take((returnAfter) => {
-            Fs.readdir('./datastore/' + f, waitFor(returnAfter((err, list2) => {
-                if (err) { throw err; }
-                list2.forEach((ff) => { fileList.push('./datastore/' + f + '/' + ff); });
-            })));
+module.exports.load = function (config, cb) {
+    nThen((waitFor) => {
+        Fs.readdir('./datastore', waitFor((err, list) => {
+            if (err) { throw err; }
+            dirList = list;
+        }));
+    }).nThen((waitFor) => {
+        dirList.forEach((f) => {
+            sema.take((returnAfter) => {
+                Fs.readdir('./datastore/' + f, waitFor(returnAfter((err, list2) => {
+                    if (err) { throw err; }
+                    list2.forEach((ff) => { fileList.push('./datastore/' + f + '/' + ff); });
+                })));
+            });
         });
-    });
-}).nThen((waitFor) => {
+    }).nThen((waitFor) => {
 
-    Fs.readdir('./blob', waitFor((err, list) => {
-        if (err) { throw err; }
-        dirList = list;
-    }));
-}).nThen((waitFor) => {
-    dirList.forEach((f) => {
-        sema.take((returnAfter) => {
-            Fs.readdir('./blob/' + f, waitFor(returnAfter((err, list2) => {
-                if (err) { throw err; }
-                list2.forEach((ff) => { fileList.push('./blob/' + f + '/' + ff); });
-            })));
+        Fs.readdir('./blob', waitFor((err, list) => {
+            if (err) { throw err; }
+            dirList = list;
+        }));
+    }).nThen((waitFor) => {
+        dirList.forEach((f) => {
+            sema.take((returnAfter) => {
+                Fs.readdir('./blob/' + f, waitFor(returnAfter((err, list2) => {
+                    if (err) { throw err; }
+                    list2.forEach((ff) => { fileList.push('./blob/' + f + '/' + ff); });
+                })));
+            });
         });
-    });
-}).nThen((waitFor) => {
-    fileList.forEach((f) => {
-        sema.take((returnAfter) => {
-            Fs.stat(f, waitFor(returnAfter((err, st) => {
-                if (err) { throw err; }
-                st.filename = f;
-                dsFileStats[f.replace(/^.*\/([^\/\.]*)(\.ndjson)?$/, (all, a) => (a))] = st;
-            })));
+    }).nThen((waitFor) => {
+        fileList.forEach((f) => {
+            sema.take((returnAfter) => {
+                Fs.stat(f, waitFor(returnAfter((err, st) => {
+                    if (err) { throw err; }
+                    st.filename = f;
+                    dsFileStats[f.replace(/^.*\/([^\/\.]*)(\.ndjson)?$/, (all, a) => (a))] = st;
+                })));
+            });
         });
-    });
-}).nThen((waitFor) => {
-    Fs.readdir('./pins', waitFor((err, list) => {
-        if (err) { throw err; }
-        dirList = list;
-    }));
-}).nThen((waitFor) => {
-    fileList.splice(0, fileList.length);
-    dirList.forEach((f) => {
-        sema.take((returnAfter) => {
-            Fs.readdir('./pins/' + f, waitFor(returnAfter((err, list2) => {
-                if (err) { throw err; }
-                list2.forEach((ff) => { fileList.push('./pins/' + f + '/' + ff); });
-            })));
+    }).nThen((waitFor) => {
+        Fs.readdir('./pins', waitFor((err, list) => {
+            if (err) { throw err; }
+            dirList = list;
+        }));
+    }).nThen((waitFor) => {
+        fileList.splice(0, fileList.length);
+        dirList.forEach((f) => {
+            sema.take((returnAfter) => {
+                Fs.readdir('./pins/' + f, waitFor(returnAfter((err, list2) => {
+                    if (err) { throw err; }
+                    list2.forEach((ff) => { fileList.push('./pins/' + f + '/' + ff); });
+                })));
+            });
         });
-    });
-}).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) {
-                    hashes.forEach((x) => { pinned[x] = 1; });
-                } else {
-                    out.push([f, Math.floor(size / (1024 * 1024))]);
+    }).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 (config.unpinned) {
+                        hashes.forEach((x) => { pinned[x] = 1; });
+                    } else {
+                        out.push([f, Math.floor(size / (1024 * 1024))]);
+                    }
+                })));
+            });
+        });
+    }).nThen(() => {
+        if (config.unpinned) {
+            let before = Infinity;
+            if (config.olderthan) {
+                before = config.olderthan;
+                if (isNaN(before)) {
+                    return void cb('--olderthan error [' + config.olderthan + '] not a valid date');
                 }
-            })));
-        });
+            }
+            let blobsbefore = before;
+            if (config.blobsolderthan) {
+                blobsbefore = config.blobsolderthan;
+                if (isNaN(blobsbefore)) {
+                    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].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]));
+            cb(null, out.slice());
+        }
     });
-}).nThen(() => {
-    if (process.argv.indexOf('--unpinned') > -1) {
-        const ot = process.argv.indexOf('--olderthan');
-        let before = Infinity;
-        if (ot > -1) {
-            before = new Date(process.argv[ot+1]);
-            if (isNaN(before)) {
-                throw new Error('--olderthan error [' + process.argv[ot+1] + '] not a valid date');
-            }
+};
+
+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'); });
         }
-        const bot = process.argv.indexOf('--blobsolderthan');
-        let blobsbefore = before;
-        if (bot > -1) {
-            blobsbefore = new Date(process.argv[bot+1]);
-            if (isNaN(blobsbefore)) {
-                throw new Error('--blobsolderthan error [' + process.argv[bot+1] + '] not a valid date');
-            }
-        }
-        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));
-            }
-        });
-    } else {
-        out.sort((a,b) => (a[1] - b[1]));
-        out.forEach((x) => { console.log(x[0] + '  ' + x[1] + ' MB'); });
-    }
-});
+    });
+}
diff --git a/www/common/common-interface.js b/www/common/common-interface.js
index d0c0e1961..4822e357b 100644
--- a/www/common/common-interface.js
+++ b/www/common/common-interface.js
@@ -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(); }
+                }
             });
         }
     };
diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js
index 7540f4a0b..d612387fa 100644
--- a/www/common/sframe-app-framework.js
+++ b/www/common/sframe-app-framework.js
@@ -426,15 +426,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
diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js
index 260efeda9..10fd2e9f9 100644
--- a/www/common/sframe-common-outer.js
+++ b/www/common/sframe-common-outer.js
@@ -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]; }
@@ -666,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();
         });
diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js
index 3f4070eaf..df6f77e87 100644
--- a/www/common/sframe-common.js
+++ b/www/common/sframe-common.js
@@ -167,6 +167,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,
diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js
index e014503c6..df97cf072 100644
--- a/www/common/toolbar3.js
+++ b/www/common/toolbar3.js
@@ -593,7 +593,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;
diff --git a/www/poll/inner.js b/www/poll/inner.js
index 8f31116a1..94d732a7f 100644
--- a/www/poll/inner.js
+++ b/www/poll/inner.js
@@ -1274,16 +1274,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;
diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js
index 8d2621aaf..3c540fe98 100644
--- a/www/whiteboard/inner.js
+++ b/www/whiteboard/inner.js
@@ -662,15 +662,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);
         });

From 66d83dc045db43b7f29ad88c4c07cf138215441c Mon Sep 17 00:00:00 2001
From: ansuz <ansuz@transitiontech.ca>
Date: Tue, 27 Feb 2018 15:13:39 +0100
Subject: [PATCH 08/21] update version

---
 customize.dist/pages.js | 2 +-
 package.json            | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/customize.dist/pages.js b/customize.dist/pages.js
index 37bdc5b42..55beb2884 100644
--- a/customize.dist/pages.js
+++ b/customize.dist/pages.js
@@ -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)")
         ]);
     };
 
diff --git a/package.json b/package.json
index 16e4f69fb..01dd38ab1 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "cryptpad",
   "description": "realtime collaborative visual editor with zero knowlege server",
-  "version": "1.26.0",
+  "version": "1.27.0",
   "dependencies": {
     "chainpad-server": "^2.0.0",
     "express": "~4.10.1",

From 60b2384885258f3413057d811343e89b49545d3e Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Tue, 27 Feb 2018 17:38:29 +0100
Subject: [PATCH 09/21] Move initial state into a help block

---
 customize.dist/pages.js                       |  6 --
 .../src/less2/include/ckeditor-fix.less       | 19 -------
 customize.dist/src/less2/include/toolbar.less | 32 +++++++++++
 customize.dist/translations/messages.el.js    | 15 +----
 customize.dist/translations/messages.es.js    |  9 +--
 customize.dist/translations/messages.fr.js    | 15 +----
 customize.dist/translations/messages.js       | 15 +----
 customize.dist/translations/messages.pt-br.js | 15 +----
 customize.dist/translations/messages.ro.js    | 14 +----
 customize.dist/translations/messages.zh.js    | 17 +-----
 www/code/app-code.less                        | 18 ++++++
 www/code/inner.js                             | 47 ++++++++++++++--
 www/common/common-ui-elements.js              | 39 +++++++++++++
 www/common/sframe-common-codemirror.js        |  1 -
 www/common/sframe-common.js                   |  1 +
 www/common/toolbar3.js                        | 25 +--------
 www/pad/app-pad.less                          | 38 ++++++++++---
 www/pad/inner.js                              | 36 ++++++++----
 www/poll/inner.js                             | 56 +++++--------------
 www/slide/app-slide.less                      | 10 ++++
 www/slide/inner.js                            | 15 ++++-
 www/slide/slide.js                            | 14 +++++
 22 files changed, 251 insertions(+), 206 deletions(-)

diff --git a/customize.dist/pages.js b/customize.dist/pages.js
index 37bdc5b42..32eab821d 100644
--- a/customize.dist/pages.js
+++ b/customize.dist/pages.js
@@ -819,12 +819,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', [
diff --git a/customize.dist/src/less2/include/ckeditor-fix.less b/customize.dist/src/less2/include/ckeditor-fix.less
index 96e6d893f..98672bd86 100644
--- a/customize.dist/src/less2/include/ckeditor-fix.less
+++ b/customize.dist/src/less2/include/ckeditor-fix.less
@@ -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;
diff --git a/customize.dist/src/less2/include/toolbar.less b/customize.dist/src/less2/include/toolbar.less
index f8cd43fd0..a070c8af6 100644
--- a/customize.dist/src/less2/include/toolbar.less
+++ b/customize.dist/src/less2/include/toolbar.less
@@ -255,6 +255,38 @@
             &.cp-markdown-help { float: right; }
         }
     }
+
+    // TODO put in a different less file
+    .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;
+            h1 {
+                font-size: 20px;
+            }
+            h2 {
+                font-size: 18px;
+            }
+            h3 {
+                font-size: 16px;
+            }
+            ul, ol, p { margin: 0; }
+        }
+    }
+
+
     .cp-toolbar-userlist-drawer {
         background-color: @bg-color;
         color: @color;
diff --git a/customize.dist/translations/messages.el.js b/customize.dist/translations/messages.el.js
index 1e30248ec..36a19da87 100644
--- a/customize.dist/translations/messages.el.js
+++ b/customize.dist/translations/messages.el.js
@@ -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',
diff --git a/customize.dist/translations/messages.es.js b/customize.dist/translations/messages.es.js
index 6df44d691..9e246294f 100644
--- a/customize.dist/translations/messages.es.js
+++ b/customize.dist/translations/messages.es.js
@@ -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.";
diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js
index 5e681e699..08b974821 100644
--- a/customize.dist/translations/messages.fr.js
+++ b/customize.dist/translations/messages.fr.js
@@ -780,13 +780,8 @@ define(function () {
         '<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 = [
@@ -798,14 +793,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',
diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js
index c5f7bcd8c..1e15eac1c 100644
--- a/customize.dist/translations/messages.js
+++ b/customize.dist/translations/messages.js
@@ -816,13 +816,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 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 = [
@@ -834,14 +829,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',
diff --git a/customize.dist/translations/messages.pt-br.js b/customize.dist/translations/messages.pt-br.js
index 79ba513c3..fbf63d067 100644
--- a/customize.dist/translations/messages.pt-br.js
+++ b/customize.dist/translations/messages.pt-br.js
@@ -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',
diff --git a/customize.dist/translations/messages.ro.js b/customize.dist/translations/messages.ro.js
index 14a55f8ab..56ec8b05d 100644
--- a/customize.dist/translations/messages.ro.js
+++ b/customize.dist/translations/messages.ro.js
@@ -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.";
diff --git a/customize.dist/translations/messages.zh.js b/customize.dist/translations/messages.zh.js
index c7ac7915a..25b7fe8c8 100644
--- a/customize.dist/translations/messages.zh.js
+++ b/customize.dist/translations/messages.zh.js
@@ -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',
diff --git a/www/code/app-code.less b/www/code/app-code.less
index 463bb07a3..f4153e95d 100644
--- a/www/code/app-code.less
+++ b/www/code/app-code.less
@@ -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 {
diff --git a/www/code/inner.js b/www/code/inner.js
index e7f080c02..091baa0e8 100644
--- a/www/code/inner.js
+++ b/www/code/inner.js
@@ -63,6 +63,31 @@ 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();
+        $codeMirrorContainer.prepend(helpMenu.menu);
+
+        $(helpMenu.text).html(DiffMd.render(Messages.codeInitialState));
+
+        framework._.toolbar.$rightside.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 +95,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 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); }
         };
@@ -118,7 +151,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 +178,6 @@ define([
                         }
                     }
                 });
-                markdownTb.setState(true);
                 return;
             }
             $editorContainer.removeClass('cp-app-code-present');
@@ -153,7 +185,6 @@ define([
             $previewContainer.hide();
             $previewButton.removeClass('active');
             $codeMirrorContainer.addClass('cp-app-code-fullpage');
-            markdownTb.setState(false);
         };
 
         var isVisible = function () {
@@ -252,8 +283,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);
@@ -315,7 +350,7 @@ define([
         });
 
         framework.onDefaultContentNeeded(function () {
-             editor.setValue(Messages.codeInitialState);
+             editor.setValue(''); //Messages.codeInitialState);
         });
 
         framework.setFileExporter(CodeMirror.getContentExtension, CodeMirror.fileExporter);
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 2a892cae6..502f18bea 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -855,6 +855,45 @@ define([
         };
     };
 
+    UIElements.createHelpMenu = function (common) {
+        var type = common.getMetadataMgr().getMetadata().type || 'pad';
+
+        var text = h('p.cp-help-text');
+        var closeButton = h('span.cp-help-close.fa.fa-window-close');
+        var $toolbarButton = common.createButton('', true).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');
+                return void $(help).removeClass('cp-help-hidden');
+            }
+            $toolbarButton.removeClass('cp-toolbar-button-active');
+            $(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 (val === true) { toggleHelp(true); }
+        });
+
+        return {
+            menu: help,
+            button: $toolbarButton,
+            text: text
+        };
+    };
+
     // Avatars
 
     // Enable mediatags
diff --git a/www/common/sframe-common-codemirror.js b/www/common/sframe-common-codemirror.js
index cb507c2a8..4386e22f0 100644
--- a/www/common/sframe-common-codemirror.js
+++ b/www/common/sframe-common-codemirror.js
@@ -141,7 +141,6 @@ define([
             mode: defaultMode || "javascript",
             readOnly: true
         });
-        //editor.setValue(Messages.codeInitialState);
         editor.focus();
 
         var setMode = exp.setMode = function (mode, cb) {
diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js
index df6f77e87..d8804349b 100644
--- a/www/common/sframe-common.js
+++ b/www/common/sframe-common.js
@@ -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);
diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js
index df97cf072..a09d13239 100644
--- a/www/common/toolbar3.js
+++ b/www/common/toolbar3.js
@@ -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) { return void hide(); }
             show();
         });
 
diff --git a/www/pad/app-pad.less b/www/pad/app-pad.less
index 742163285..9e1b1da78 100644
--- a/www/pad/app-pad.less
+++ b/www/pad/app-pad.less
@@ -15,19 +15,41 @@
         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 {
-        height: 28px;
-        padding: 2px 0;
+        .cke_toolbar {
+            height: 28px;
+            padding: 2px 0;
+        }
     }
     .cke_wysiwyg_frame {
         min-width: 60%;
     }
+    #cke_1_toolbox {
+        flex: 1;
+    }
+    #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 {
diff --git a/www/pad/inner.js b/www/pad/inner.js
index 22ec21e40..c1a2adbf2 100644
--- a/www/pad/inner.js
+++ b/www/pad/inner.js
@@ -136,6 +136,16 @@ define([
         check();
     };
 
+    var mkHelpMenu = function (framework) {
+        var $toolbarContainer = $('.cke_toolbox_main');
+        var helpMenu = framework._.sfCommon.createHelpMenu();
+        $toolbarContainer.before(helpMenu.menu);
+
+        $(helpMenu.text).html(Messages.initialState);
+
+        framework._.toolbar.$rightside.append(helpMenu.button);
+    };
+
     var mkDiffOptions = function (cursor, readOnly) {
         return {
             preDiffApply: function (info) {
@@ -269,8 +279,6 @@ 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) {
@@ -324,12 +332,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 +360,8 @@ define([
             }
         };
 
+        mkHelpMenu(framework);
+
         framework.onEditableChange(function (unlocked) {
             if (!framework.isReadOnly()) {
                 $(inner).attr('contenteditable', '' + Boolean(unlocked));
@@ -421,7 +431,7 @@ define([
 
         $bar.find('#cke_1_toolbar_collapser').hide();
         if (!framework.isReadOnly()) {
-            addToolbarHideBtn(framework, $bar);
+            addToolbarHideBtn(framework, $contentContainer);
         } else {
             $('.cke_toolbox_main').hide();
         }
@@ -466,9 +476,7 @@ define([
             });
         });
 
-        framework.onDefaultContentNeeded(function () {
-            documentBody.innerHTML = Messages.initialState;
-        });
+        framework.onDefaultContentNeeded(function () { });
 
         var importMediaTags = function (dom, cb) {
             var $dom = $(dom);
@@ -561,9 +569,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 +592,7 @@ define([
                         var range = module.cursor.makeRange();
                         module.cursor.fixSelection(sel, range);
                     }
-                }
+                }*/
             }, waitFor(function (fw) { window.APP.framework = framework = fw; }));
 
             nThen(function (waitFor) {
@@ -624,6 +632,14 @@ 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');
             }).nThen(waitFor());
 
         }).nThen(function (/*waitFor*/) {
diff --git a/www/poll/inner.js b/www/poll/inner.js
index 94d732a7f..2c7c4d7e0 100644
--- a/www/poll/inner.js
+++ b/www/poll/inner.js
@@ -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;
 
@@ -1221,10 +1198,19 @@ 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();
+        $('#cp-app-poll-form').prepend(helpMenu.menu);
+        $rightside.append(helpMenu.button);
+        var setHTML = function (e, html) {
+            e.innerHTML = html;
+            return e;
+        };
+        var help = h('div', [
+            setHTML(h('h1'), Messages.poll_subtitle),
+            h('p', Messages.poll_p_save),
+            h('p', Messages.poll_p_encryption)
+        ]);
+        $(helpMenu.text).html($(help).html());
 
         if (APP.readOnly) { publish(true); return; }
         var $publish = common.createButton('', true)
@@ -1344,18 +1330,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();
diff --git a/www/slide/app-slide.less b/www/slide/app-slide.less
index 8926f52eb..14090e9bc 100644
--- a/www/slide/app-slide.less
+++ b/www/slide/app-slide.less
@@ -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;
diff --git a/www/slide/inner.js b/www/slide/inner.js
index 173726d34..b41496024 100644
--- a/www/slide/inner.js
+++ b/www/slide/inner.js
@@ -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)
 {
@@ -426,6 +428,16 @@ define([
         framework._.toolbar.$rightside.append(markdownTb.button);
     };
 
+    var mkHelpMenu = function (framework) {
+        var $codeMirrorContainer = $('#cp-app-slide-editor-container');
+        var helpMenu = framework._.sfCommon.createHelpMenu();
+        $codeMirrorContainer.prepend(helpMenu.menu);
+
+        $(helpMenu.text).html(DiffMd.render(Messages.slideInitialState));
+
+        framework._.toolbar.$rightside.append(helpMenu.button);
+    };
+
     var activateLinks = function ($content, framework) {
         $content.click(function (e) {
             if (!e.target) { return; }
@@ -465,6 +477,7 @@ define([
         mkFilePicker(framework, editor);
         mkSlidePreviewPane(framework, $contentContainer);
         mkMarkdownToolbar(framework, editor);
+        mkHelpMenu(framework);
 
         CodeMirror.configureTheme(common);
 
@@ -519,7 +532,7 @@ define([
         });
 
         framework.onDefaultContentNeeded(function () {
-            CodeMirror.contentUpdate({ content: Messages.slideInitialState });
+            CodeMirror.contentUpdate({ content: '' });
         });
 
         Slide.setTitle(framework._.title);
diff --git a/www/slide/slide.js b/www/slide/slide.js
index adc0a808b..ed63ac2e4 100644
--- a/www/slide/slide.js
+++ b/www/slide/slide.js
@@ -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;

From d975bb9cc00543229515e58bfc87b9862775dc47 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Wed, 28 Feb 2018 13:16:30 +0100
Subject: [PATCH 10/21] Fix focus after pad creation screen

---
 www/code/inner.js  |  2 ++
 www/pad/inner.js   | 22 +++++++++++++++++-----
 www/slide/inner.js |  2 ++
 3 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/www/code/inner.js b/www/code/inner.js
index 091baa0e8..d7a9b25e4 100644
--- a/www/code/inner.js
+++ b/www/code/inner.js
@@ -327,6 +327,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());
diff --git a/www/pad/inner.js b/www/pad/inner.js
index c1a2adbf2..5ca7c24f6 100644
--- a/www/pad/inner.js
+++ b/www/pad/inner.js
@@ -437,6 +437,8 @@ define([
         }
 
         framework.onReady(function (newPad) {
+            editor.focus();
+
             if (!module.isMaximized) {
                 module.isMaximized = true;
                 $('iframe.cke_wysiwyg_frame').css('width', '');
@@ -444,7 +446,6 @@ define([
             }
             $('body').addClass('app-pad');
 
-            editor.focus();
             if (newPad) {
                 cursor.setToEnd();
             } else if (framework.isReadOnly()) {
@@ -474,9 +475,18 @@ 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 () { });
+        framework.onDefaultContentNeeded(function () {
+            inner.innerHTML = '<p></p>';
+        });
 
         var importMediaTags = function (dom, cb) {
             var $dom = $(dom);
@@ -616,9 +626,11 @@ 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;'+
+                    '}' +
+                    'html.cke_body_width { overflow: hidden; }' +
+                    '.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', {
diff --git a/www/slide/inner.js b/www/slide/inner.js
index b41496024..6ef545dff 100644
--- a/www/slide/inner.js
+++ b/www/slide/inner.js
@@ -502,6 +502,8 @@ define([
         });
 
         framework.onReady(function (/*newPad*/) {
+            editor.focus();
+
             CodeMirror.setMode('markdown', function () { });
             Slide.onChange(function (o, n, l) {
                 var slideNumber = '';

From dbe8ab014d7c7b6f9f48246f8fc2cde24cb0fe8e Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Wed, 28 Feb 2018 16:34:55 +0100
Subject: [PATCH 11/21] Select all in drive

---
 www/drive/inner.js | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/www/drive/inner.js b/www/drive/inner.js
index cbeb0b08f..26f504797 100644
--- a/www/drive/inner.js
+++ b/www/drive/inner.js
@@ -548,6 +548,13 @@ define([
                 return;
             }
 
+            // Ctrl+A select all
+            if (e.which === 65 && e.ctrlKey) {
+                $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 +2915,7 @@ define([
                 }
                 // else move to trash
                 moveElements(paths, [TRASH], false, refresh);
+                return;
             }
         });
         var isCharacterKey = function (e) {

From a4a6385e86e97ada55ffb7983c7eef419258ccbd Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Wed, 28 Feb 2018 16:38:28 +0100
Subject: [PATCH 12/21] Add keyboard shortcuts for the Ctrl+E or Cmd+E modal

---
 customize.dist/src/less2/include/icons.less   |  4 ++
 customize.dist/src/less2/include/toolbar.less |  1 +
 customize.dist/translations/messages.fr.js    |  3 +-
 customize.dist/translations/messages.js       |  3 +-
 www/common/common-ui-elements.js              | 51 ++++++++++++++++---
 www/common/toolbar3.js                        | 28 +++++-----
 6 files changed, 66 insertions(+), 24 deletions(-)

diff --git a/customize.dist/src/less2/include/icons.less b/customize.dist/src/less2/include/icons.less
index a79bc25f5..03a6af5ab 100644
--- a/customize.dist/src/less2/include/icons.less
+++ b/customize.dist/src/less2/include/icons.less
@@ -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;
diff --git a/customize.dist/src/less2/include/toolbar.less b/customize.dist/src/less2/include/toolbar.less
index a070c8af6..6f49250f4 100644
--- a/customize.dist/src/less2/include/toolbar.less
+++ b/customize.dist/src/less2/include/toolbar.less
@@ -208,6 +208,7 @@
             width: auto;
             margin: 0;
             padding: 0;
+            outline: none;
         }
         label[for="cp-app-toolbar-creation-advanced"] {
             margin: 0;
diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js
index 08b974821..1a22c70ab 100644
--- a/customize.dist/translations/messages.fr.js
+++ b/customize.dist/translations/messages.fr.js
@@ -864,7 +864,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
diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js
index 1e15eac1c..da9cad5e7 100644
--- a/customize.dist/translations/messages.js
+++ b/customize.dist/translations/messages.js
@@ -902,7 +902,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
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 502f18bea..5c0d3a2c0 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -1528,6 +1528,7 @@ define([
         if (!$blockContainer.length) {
             $blockContainer = $('<div>', {
                 'class': 'cp-modal-container',
+                tabindex: 1,
                 'id': cfg.id
             });
         }
@@ -1559,14 +1560,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',
@@ -1575,9 +1578,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; }
@@ -1586,7 +1592,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]));
@@ -1594,7 +1601,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;
@@ -1605,11 +1612,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) {
diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js
index a09d13239..a9554f634 100644
--- a/www/common/toolbar3.js
+++ b/www/common/toolbar3.js
@@ -768,21 +768,19 @@ define([
                 content: $('<div>').append(UI.getIcon(p)).html() + Messages.type[p]
             });
         });
-        if (Config.displayCreationScreen) {
-            pads_options.push({
-                tag: 'a',
-                attributes: {
-                    id: 'cp-app-toolbar-creation-advanced',
-                    href: origin
-                },
-                content: '<span class="fa fa-plus-circle"></span> ' + Messages.creation_appMenuName
-            });
-            $(window).keydown(function (e) {
-                if (e.which === 69 && e.ctrlKey) {
-                    Common.createNewPadModal();
-                }
-            });
-        }
+        pads_options.push({
+            tag: 'a',
+            attributes: {
+                id: 'cp-app-toolbar-creation-advanced',
+                href: origin
+            },
+            content: '<span class="fa fa-plus-circle"></span> ' + Messages.creation_appMenuName
+        });
+        $(window).keydown(function (e) {
+            if (e.which === 69 && (e.ctrlKey || e.metaKey)) {
+                Common.createNewPadModal();
+            }
+        });
         var dropdownConfig = {
             text: '', // Button initial text
             options: pads_options, // Entries displayed in the menu

From 753b7550ca208261d7e489c9af98a0a235790c15 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Wed, 28 Feb 2018 17:02:35 +0100
Subject: [PATCH 13/21] Save button in the pad creation screen

---
 .../src/less2/include/creation.less           |  6 ++-
 customize.dist/translations/messages.fr.js    |  1 +
 customize.dist/translations/messages.js       |  1 +
 www/common/common-ui-elements.js              | 49 ++++++++++++++++---
 4 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/customize.dist/src/less2/include/creation.less b/customize.dist/src/less2/include/creation.less
index 58d5a53ba..2ca8cc402 100644
--- a/customize.dist/src/less2/include/creation.less
+++ b/customize.dist/src/less2/include/creation.less
@@ -75,7 +75,7 @@
             }
         }
 
-        .cp-creation-create {
+        .cp-creation-create, .cp-creation-settings {
             button {
                 .tools_unselectable();
                 padding: 15px;
@@ -84,6 +84,7 @@
                 margin: 3px 10px;
                 border: none;
                 cursor: pointer;
+                outline: none;
                 &:hover {
                     background: darken(@colortheme_loading-bg, 5%);
                 }
@@ -159,6 +160,9 @@
                     color: lighten(#0275d8, 10%);
                 }
             }
+            &> span.fa {
+                margin-left: 15px;
+            }
         }
         .cp-creation-deleted {
             background: #111;
diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js
index 1a22c70ab..edf3362d4 100644
--- a/customize.dist/translations/messages.fr.js
+++ b/customize.dist/translations/messages.fr.js
@@ -857,6 +857,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";
diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js
index da9cad5e7..0dabbefe7 100644
--- a/customize.dist/translations/messages.js
+++ b/customize.dist/translations/messages.js
@@ -895,6 +895,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";
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 5c0d3a2c0..9a5a1bc42 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -1839,8 +1839,7 @@ define([
         UIElements.setExpirationValue(cfg.expire, $creation);
 
         // Create the pad
-        var create = function (template) {
-            $creationContainer.remove();
+        var getFormValues = function (template) {
             // Type of pad
             var ownedVal = parseInt($('input[name="cp-creation-owned"]:checked').val());
             // Life time
@@ -1856,11 +1855,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();
             });
         };
@@ -1905,10 +1909,39 @@ define([
 
         // Settings button
         var origin = common.getMetadataMgr().getPrivateData().origin;
-        $(h('div.cp-creation-settings', h('a', {
-            href: origin + '/settings/#creation',
-            target: '_blank'
-        }, Messages.creation_settings))).appendTo($creation);
+        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),
+            $ok[0],
+            $spinner[0]
+        ])).appendTo($creation);
     };
 
     UIElements.onServerError = function (common, err, toolbar, cb) {

From 2c90cb5907131336f2541ad89106541560260d68 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Wed, 28 Feb 2018 17:59:27 +0100
Subject: [PATCH 14/21] Move help button in drawer and fix drawer style

---
 customize.dist/src/less2/include/toolbar.less | 24 ++++++++++++++++++-
 customize.dist/translations/messages.fr.js    |  7 +++---
 customize.dist/translations/messages.js       |  7 +++---
 www/code/inner.js                             |  2 +-
 www/common/common-ui-elements.js              | 11 ++++++++-
 www/pad/inner.js                              |  2 +-
 www/poll/inner.js                             |  2 +-
 www/slide/inner.js                            |  2 +-
 8 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/customize.dist/src/less2/include/toolbar.less b/customize.dist/src/less2/include/toolbar.less
index 6f49250f4..07e5eff61 100644
--- a/customize.dist/src/less2/include/toolbar.less
+++ b/customize.dist/src/less2/include/toolbar.less
@@ -855,6 +855,7 @@
                 font-size: 17px;
             }
             &> span {
+                order: 8;
                 box-sizing: border-box;
                 min-width: 150px;
                 height: @toolbar_line-height;
@@ -869,15 +870,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; }
             }
         }
     }
diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js
index edf3362d4..145d50720 100644
--- a/customize.dist/translations/messages.fr.js
+++ b/customize.dist/translations/messages.fr.js
@@ -218,6 +218,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";
@@ -278,9 +282,6 @@ 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_total = 'TOTAL';
diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js
index 0dabbefe7..92702a339 100644
--- a/customize.dist/translations/messages.js
+++ b/customize.dist/translations/messages.js
@@ -220,6 +220,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";
@@ -280,9 +284,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';
diff --git a/www/code/inner.js b/www/code/inner.js
index d7a9b25e4..c782c3f08 100644
--- a/www/code/inner.js
+++ b/www/code/inner.js
@@ -86,7 +86,7 @@ define([
 
         $(helpMenu.text).html(DiffMd.render(Messages.codeInitialState));
 
-        framework._.toolbar.$rightside.append(helpMenu.button);
+        framework._.toolbar.$drawer.append(helpMenu.button);
     };
     var mkPreviewPane = function (editor, CodeMirror, framework, isPresentMode) {
         var $previewContainer = $('#cp-app-code-preview');
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 9a5a1bc42..c72ea7b07 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -657,6 +657,11 @@ define([
                     data.element.toggle();
                     var isVisible = data.element.is(':visible');
                     if (callback) { callback(isVisible); }
+                    if (isVisible) {
+                        button.addClass('cp-toolbar-button-active');
+                    } else {
+                        button.removeClass('cp-toolbar-button-active');
+                    }
                     updateIcon(isVisible);
                 });
                 updateIcon(data.element.is(':visible'));
@@ -681,7 +686,6 @@ define([
             default:
                 button = $('<button>', {
                     'class': "fa fa-question",
-                    style: 'font:'+size+' FontAwesome'
                 })
                 .click(common.prepareFeedback(type));
         }
@@ -861,6 +865,9 @@ define([
         var text = h('p.cp-help-text');
         var closeButton = h('span.cp-help-close.fa.fa-window-close');
         var $toolbarButton = common.createButton('', true).addClass('cp-toolbar-button-active');
+        $toolbarButton.attr('title', Messages.hide_help_button);
+        $toolbarButton
+            .append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.help_button));
         var help = h('div.cp-help-container', [
             closeButton,
             text
@@ -871,9 +878,11 @@ define([
                 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);
         };
diff --git a/www/pad/inner.js b/www/pad/inner.js
index 5ca7c24f6..8e11b2285 100644
--- a/www/pad/inner.js
+++ b/www/pad/inner.js
@@ -143,7 +143,7 @@ define([
 
         $(helpMenu.text).html(Messages.initialState);
 
-        framework._.toolbar.$rightside.append(helpMenu.button);
+        framework._.toolbar.$drawer.append(helpMenu.button);
     };
 
     var mkDiffOptions = function (cursor, readOnly) {
diff --git a/www/poll/inner.js b/www/poll/inner.js
index 2c7c4d7e0..c88cf7cbe 100644
--- a/www/poll/inner.js
+++ b/www/poll/inner.js
@@ -1200,7 +1200,7 @@ define([
 
         var helpMenu = common.createHelpMenu();
         $('#cp-app-poll-form').prepend(helpMenu.menu);
-        $rightside.append(helpMenu.button);
+        $drawer.append(helpMenu.button);
         var setHTML = function (e, html) {
             e.innerHTML = html;
             return e;
diff --git a/www/slide/inner.js b/www/slide/inner.js
index 6ef545dff..663814af2 100644
--- a/www/slide/inner.js
+++ b/www/slide/inner.js
@@ -435,7 +435,7 @@ define([
 
         $(helpMenu.text).html(DiffMd.render(Messages.slideInitialState));
 
-        framework._.toolbar.$rightside.append(helpMenu.button);
+        framework._.toolbar.$drawer.append(helpMenu.button);
     };
 
     var activateLinks = function ($content, framework) {

From ee3b926e848bc5d172250948d3a8982fda7c52c8 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Thu, 1 Mar 2018 10:33:28 +0100
Subject: [PATCH 15/21] Get rid of ckeditor's reset_all class...

---
 www/pad/inner.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/www/pad/inner.js b/www/pad/inner.js
index 8e11b2285..4aff47102 100644
--- a/www/pad/inner.js
+++ b/www/pad/inner.js
@@ -652,6 +652,7 @@ define([
                 $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*/) {

From 95bbc3d130b05aebf5f86797cf14c1a478f982a1 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Thu, 1 Mar 2018 11:23:16 +0100
Subject: [PATCH 16/21] Add the mediatag button back in the code app

---
 www/code/inner.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/www/code/inner.js b/www/code/inner.js
index c782c3f08..d10496993 100644
--- a/www/code/inner.js
+++ b/www/code/inner.js
@@ -54,6 +54,7 @@ define([
 
     var MEDIA_TAG_MODES = Object.freeze([
         'markdown',
+        'gfm',
         'html',
         'htmlembedded',
         'htmlmixed',

From 12bfcbe70124ed3b5a8067f2b641898dde676ce7 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Thu, 1 Mar 2018 14:32:14 +0100
Subject: [PATCH 17/21] Add fixed order for rightside icons in the toolbar

---
 customize.dist/src/less2/include/toolbar.less |  38 ++++---
 www/code/inner.js                             |  11 +-
 www/common/common-thumbnail.js                |   3 +-
 www/common/common-ui-elements.js              | 100 ++++++++++++------
 www/common/sframe-app-framework.js            |   6 +-
 www/drive/app-drive.less                      |   2 +-
 www/file/app-file.less                        |  24 +++++
 www/file/inner.js                             |  12 +--
 www/poll/inner.js                             |  16 ++-
 www/slide/inner.js                            |  63 ++++++-----
 www/slide/slide.js                            |   3 -
 www/whiteboard/app-whiteboard.less            |   7 ++
 www/whiteboard/inner.js                       |  30 +++---
 13 files changed, 183 insertions(+), 132 deletions(-)

diff --git a/customize.dist/src/less2/include/toolbar.less b/customize.dist/src/less2/include/toolbar.less
index 07e5eff61..b6258c4fe 100644
--- a/customize.dist/src/less2/include/toolbar.less
+++ b/customize.dist/src/less2/include/toolbar.less
@@ -389,11 +389,6 @@
             margin: 0;
         }*/
 
-        .cp-toolbar-rightside-button {
-            float: right;
-            cursor: pointer;
-        }
-
         select {
             margin-left: 5px;
             margin-right: 5px;
@@ -475,13 +470,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;
@@ -830,13 +818,37 @@
         }
     }
     .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;
         }
diff --git a/www/code/inner.js b/www/code/inner.js
index d10496993..1799819dd 100644
--- a/www/code/inner.js
+++ b/www/code/inner.js
@@ -102,7 +102,7 @@ define([
             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() === '') {
@@ -119,12 +119,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);
@@ -377,9 +371,6 @@ define([
         if ($preview.length && $preview.is(':visible')) {
             return $preview[0];
         }
-        if ($codeMirror.length) {
-            return $codeMirror[0];
-        }
     };
 
     var main = function () {
diff --git a/www/common/common-thumbnail.js b/www/common/common-thumbnail.js
index f0ba27194..f3864e6a3 100644
--- a/www/common/common-thumbnail.js
+++ b/www/common/common-thumbnail.js
@@ -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; }
+            oldThumbnailState = content;
             Thumb.fromDOM(opts, function (err, b64) {
-                oldThumbnailState = content;
                 Thumb.setPadThumbnail(common, opts.href, b64);
             });
         };
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index c72ea7b07..8bf3474c1 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -468,7 +468,7 @@ define([
         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));
 
@@ -479,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) {
@@ -525,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 () {
@@ -570,37 +571,45 @@ 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) {
-                    button
-                    .click(common.prepareFeedback(type))
-                    .click(function() {
-                        var msg = common.isLoggedIn() ? Messages.forgetPrompt : Messages.fm_removePermanentlyDialog;
-                        UI.confirm(msg, function (yes) {
-                            if (!yes) { return; }
-                            sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) {
-                                if (err) { return void callback(err); }
-                                var cMsg = common.isLoggedIn() ? Messages.movedToTrash : Messages.deleted;
-                                UI.alert(cMsg, undefined, true);
-                                callback();
-                                return;
-                            });
+                callback = typeof callback === "function" ? callback : function () {};
+                button
+                .click(common.prepareFeedback(type))
+                .click(function() {
+                    var msg = common.isLoggedIn() ? Messages.forgetPrompt : Messages.fm_removePermanentlyDialog;
+                    UI.confirm(msg, function (yes) {
+                        if (!yes) { return; }
+                        sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) {
+                            if (err) { return void callback(err); }
+                            var cMsg = common.isLoggedIn() ? Messages.movedToTrash : Messages.deleted;
+                            UI.alert(cMsg, undefined, true);
+                            callback();
+                            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>');
@@ -608,7 +617,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
@@ -622,19 +631,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))
@@ -642,7 +657,7 @@ define([
                 break;
             case 'toggle':
                 button = $('<button>', {
-                    'class': 'fa fa-caret-down',
+                    'class': 'fa fa-caret-down cp-toolbar-icon-toggle',
                 })
                 .click(common.prepareFeedback(type));
                 window.setTimeout(function () {
@@ -668,7 +683,7 @@ define([
                 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))
@@ -684,10 +699,24 @@ define([
                 });
                 break;
             default:
+                data = data || {};
+                var icon = data.icon || "fa-question";
                 button = $('<button>', {
-                    'class': "fa fa-question",
+                    'class': "fa " + icon,
                 })
                 .click(common.prepareFeedback(type));
+                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');
@@ -864,10 +893,11 @@ define([
 
         var text = h('p.cp-help-text');
         var closeButton = h('span.cp-help-close.fa.fa-window-close');
-        var $toolbarButton = common.createButton('', true).addClass('cp-toolbar-button-active');
-        $toolbarButton.attr('title', Messages.hide_help_button);
-        $toolbarButton
-            .append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.help_button));
+        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
diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js
index d612387fa..d5a4e7896 100644
--- a/www/common/sframe-app-framework.js
+++ b/www/common/sframe-app-framework.js
@@ -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']
diff --git a/www/drive/app-drive.less b/www/drive/app-drive.less
index 083eeebe4..188fcf0bf 100644
--- a/www/drive/app-drive.less
+++ b/www/drive/app-drive.less
@@ -724,7 +724,7 @@ span {
         }
     }
 
-    .history {
+    .cp-toolbar-icon-history {
         float: right;
         .cp-toolbar-drawer-element {
             display: none;
diff --git a/www/file/app-file.less b/www/file/app-file.less
index a777b6014..ec22a2e0c 100644
--- a/www/file/app-file.less
+++ b/www/file/app-file.less
@@ -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%;
+        }
+    }
+
+}
diff --git a/www/file/inner.js b/www/file/inner.js
index b4b327998..b90f392a1 100644
--- a/www/file/inner.js
+++ b/www/file/inner.js
@@ -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;
                         }
 
diff --git a/www/poll/inner.js b/www/poll/inner.js
index c88cf7cbe..6a77db8a9 100644
--- a/www/poll/inner.js
+++ b/www/poll/inner.js
@@ -1064,7 +1064,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();
@@ -1213,9 +1213,11 @@ define([
         $(helpMenu.text).html($(help).html());
 
         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();
 
@@ -1230,11 +1232,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']
diff --git a/www/slide/inner.js b/www/slide/inner.js
index 663814af2..abbf3828f 100644
--- a/www/slide/inner.js
+++ b/www/slide/inner.js
@@ -73,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')) {
@@ -102,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
@@ -122,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)) {
@@ -319,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();
@@ -363,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");
@@ -471,7 +473,7 @@ 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);
@@ -559,9 +561,6 @@ define([
         if ($c.hasClass('cp-app-slide-preview')) {
             return $('.cp-app-slide-frame').first()[0];
         }
-        if ($codeMirror.length) {
-            return $codeMirror[0];
-        }
     };
 
     var main = function () {
diff --git a/www/slide/slide.js b/www/slide/slide.js
index ed63ac2e4..17ad19c0c 100644
--- a/www/slide/slide.js
+++ b/www/slide/slide.js
@@ -139,7 +139,6 @@ define([
 
             change(null, Slide.index);
             Common.setPresentUrl(true);
-            $('.cp-app-slide-present-button').hide();
             updateFontSize();
             return;
         }
@@ -147,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();
     };
diff --git a/www/whiteboard/app-whiteboard.less b/www/whiteboard/app-whiteboard.less
index 29c739b6e..7bbb7fbaf 100644
--- a/www/whiteboard/app-whiteboard.less
+++ b/www/whiteboard/app-whiteboard.less
@@ -31,6 +31,13 @@
         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;
     }
diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js
index 3c540fe98..7228f50c4 100644
--- a/www/whiteboard/inner.js
+++ b/www/whiteboard/inner.js
@@ -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);
                 });
@@ -469,12 +470,13 @@ define([
                     };
                     reader.readAsDataURL(file);
                 };
-                common.createButton('', true)
-                    .attr('title', Messages.canvas_imageEmbed)
-                    .removeClass('fa-question').addClass('fa-file-image-o')
-                    .click(function () {
-                        $('<input>', {type:'file'}).on('change', onUpload).click();
-                    }).appendTo($rightside);
+                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);
 
                 if (common.isLoggedIn()) {
                     var fileDialogCfg = {
@@ -493,11 +495,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'],

From b7b560fcb8152aa27b81e4994b8b4b756333ec02 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Thu, 1 Mar 2018 14:48:36 +0100
Subject: [PATCH 18/21] Enable Cmd key support in shortcuts

---
 www/code/inner.js                | 1 -
 www/common/common-ui-elements.js | 1 -
 www/common/toolbar3.js           | 2 +-
 www/drive/inner.js               | 5 +++--
 www/slide/inner.js               | 1 -
 5 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/www/code/inner.js b/www/code/inner.js
index 1799819dd..66010a485 100644
--- a/www/code/inner.js
+++ b/www/code/inner.js
@@ -367,7 +367,6 @@ define([
 
     var getThumbnailContainer = function () {
         var $preview = $('#cp-app-code-preview-content');
-        var $codeMirror = $('.CodeMirror');
         if ($preview.length && $preview.is(':visible')) {
             return $preview[0];
         }
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 8bf3474c1..35a5d38ec 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -463,7 +463,6 @@ define([
     UIElements.createButton = function (common, type, rightside, data, callback) {
         var AppConfig = common.getAppConfig();
         var button;
-        var size = "17px";
         var sframeChan = common.getSframeChannel();
         switch (type) {
             case 'export':
diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js
index a9554f634..a000025d1 100644
--- a/www/common/toolbar3.js
+++ b/www/common/toolbar3.js
@@ -777,7 +777,7 @@ define([
             content: '<span class="fa fa-plus-circle"></span> ' + Messages.creation_appMenuName
         });
         $(window).keydown(function (e) {
-            if (e.which === 69 && (e.ctrlKey || e.metaKey)) {
+            if (e.which === 69 && (e.ctrlKey || (navigator.platform === "MacIntel" && e.metaKey))) {
                 Common.createNewPadModal();
             }
         });
diff --git a/www/drive/inner.js b/www/drive/inner.js
index 26f504797..58d547b0e 100644
--- a/www/drive/inner.js
+++ b/www/drive/inner.js
@@ -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) {
@@ -549,7 +550,7 @@ define([
             }
 
             // Ctrl+A select all
-            if (e.which === 65 && e.ctrlKey) {
+            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;
diff --git a/www/slide/inner.js b/www/slide/inner.js
index abbf3828f..44073bb46 100644
--- a/www/slide/inner.js
+++ b/www/slide/inner.js
@@ -556,7 +556,6 @@ 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];

From b0dba481d834d77bcf36b1ef0a488ce8f4fc3eb2 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Fri, 2 Mar 2018 18:33:43 +0100
Subject: [PATCH 19/21] Fix small UI issues and IE issues

---
 customize.dist/pages.js                       | 12 ++--
 .../src/less2/include/creation.less           |  5 ++
 customize.dist/src/less2/include/help.less    | 32 +++++++++++
 .../src/less2/include/markdown-toolbar.less   | 20 +++++++
 customize.dist/src/less2/include/toolbar.less | 57 +++----------------
 www/common/common-interface.js                |  4 +-
 www/common/common-ui-elements.js              | 26 ++++++++-
 www/common/diffMarked.js                      |  5 +-
 www/common/toolbar3.js                        |  4 +-
 www/pad/app-pad.less                          |  6 +-
 www/pad/inner.js                              |  1 -
 www/poll/inner.js                             |  1 +
 www/whiteboard/app-whiteboard.less            | 13 ++++-
 www/whiteboard/inner.js                       |  8 +++
 14 files changed, 126 insertions(+), 68 deletions(-)
 create mode 100644 customize.dist/src/less2/include/help.less
 create mode 100644 customize.dist/src/less2/include/markdown-toolbar.less

diff --git a/customize.dist/pages.js b/customize.dist/pages.js
index 811714531..9dd366da2 100644
--- a/customize.dist/pages.js
+++ b/customize.dist/pages.js
@@ -756,10 +756,14 @@ define([
     Pages['/whiteboard/'] = Pages['/whiteboard/index.html'] = function () {
         return [
             appToolbar(),
-            h('div#cp-app-whiteboard-canvas-area', h('canvas#cp-app-whiteboard-canvas', {
-                width: 600,
-                height: 600
-            })),
+            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',
diff --git a/customize.dist/src/less2/include/creation.less b/customize.dist/src/less2/include/creation.less
index 2ca8cc402..16bce0637 100644
--- a/customize.dist/src/less2/include/creation.less
+++ b/customize.dist/src/less2/include/creation.less
@@ -25,6 +25,7 @@
         text-align: center;
         font: @colortheme_app-font;
         width: 100%;
+        outline: none;
         & > div {
             width: 60vw;
             max-width: 100%;
@@ -88,6 +89,10 @@
                 &:hover {
                     background: darken(@colortheme_loading-bg, 5%);
                 }
+                &.cp-creation-button-selected {
+                    color: darken(@colortheme_loading-bg, 10%);
+                    background: @colortheme_loading-color;
+                }
             }
         }
 
diff --git a/customize.dist/src/less2/include/help.less b/customize.dist/src/less2/include/help.less
new file mode 100644
index 000000000..905c453fb
--- /dev/null
+++ b/customize.dist/src/less2/include/help.less
@@ -0,0 +1,32 @@
+@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;
+            h1 {
+                font-size: 20px;
+            }
+            h2 {
+                font-size: 18px;
+            }
+            h3 {
+                font-size: 16px;
+            }
+            ul, ol, p { margin: 0; }
+        }
+    }
+}
diff --git a/customize.dist/src/less2/include/markdown-toolbar.less b/customize.dist/src/less2/include/markdown-toolbar.less
new file mode 100644
index 000000000..4fb466525
--- /dev/null
+++ b/customize.dist/src/less2/include/markdown-toolbar.less
@@ -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; }
+        }
+    }
+}
diff --git a/customize.dist/src/less2/include/toolbar.less b/customize.dist/src/less2/include/toolbar.less
index b6258c4fe..a0c01e377 100644
--- a/customize.dist/src/less2/include/toolbar.less
+++ b/customize.dist/src/less2/include/toolbar.less
@@ -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;
@@ -239,55 +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; }
-        }
-    }
-
-    // TODO put in a different less file
-    .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;
-            h1 {
-                font-size: 20px;
-            }
-            h2 {
-                font-size: 18px;
-            }
-            h3 {
-                font-size: 16px;
-            }
-            ul, ol, p { margin: 0; }
-        }
-    }
-
-
     .cp-toolbar-userlist-drawer {
         background-color: @bg-color;
         color: @color;
@@ -450,6 +405,7 @@
                             font-size: @colortheme_app-font-size;
                             flex: 1;
                             max-width: none;
+                            line-height: calc(@toolbar_line-height - 12px); // padding + border
                         }
                     }
                 }
@@ -615,7 +571,7 @@
             }
             input {
                 max-width: ~"calc(100% - 40px)";
-                flex: 1;
+                //flex: 1;
                 vertical-align: middle;
                 box-sizing: border-box;
                 cursor: auto;
@@ -623,6 +579,7 @@
                 font-size: 20px;
                 padding: 5px 5px;
                 height: 40px;
+                line-height: 28px; // padding + border
             }
         }
         .cp-toolbar-link, .cp-toolbar-new {
diff --git a/www/common/common-interface.js b/www/common/common-interface.js
index 4822e357b..5286a9ac6 100644
--- a/www/common/common-interface.js
+++ b/www/common/common-interface.js
@@ -668,7 +668,6 @@ define([
                 position: 'bottom',
                 distance: 0,
                 performance: true,
-                dynamicTitle: true,
                 delay: [delay, 0],
                 sticky: true
             });
@@ -691,6 +690,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++) {
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 35a5d38ec..de550dc0c 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -588,7 +588,6 @@ define([
                             return;
                         });
                     });
-
                 });
                 break;
             case 'present':
@@ -1777,7 +1776,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;
@@ -1980,6 +1979,29 @@ define([
             $ok[0],
             $spinner[0]
         ])).appendTo($creation);
+
+        var selected = -1;
+        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) {
diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js
index f79d8cbdc..47e869fc9 100644
--- a/www/common/diffMarked.js
+++ b/www/common/diffMarked.js
@@ -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());
diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js
index a000025d1..38cb2d759 100644
--- a/www/common/toolbar3.js
+++ b/www/common/toolbar3.js
@@ -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);
@@ -610,7 +610,7 @@ define([
                 });
             });
             $('.cp-toolbar-top').append($msg);
-            UI.addTooltips();
+            //UI.addTooltips();
         });
     };
 
diff --git a/www/pad/app-pad.less b/www/pad/app-pad.less
index 9e1b1da78..fa7d1142a 100644
--- a/www/pad/app-pad.less
+++ b/www/pad/app-pad.less
@@ -13,7 +13,6 @@
     #cke_1_top {
         overflow: visible;
         padding: 0px;
-        display: flex;
     }
     .cke_toolbox_main {
         background-color: @colortheme_pad-toolbar-bg;
@@ -23,10 +22,7 @@
         }
     }
     .cke_wysiwyg_frame {
-        min-width: 60%;
-    }
-    #cke_1_toolbox {
-        flex: 1;
+        width: 100%;
     }
     #cke_editor1 {
         display: flex;
diff --git a/www/pad/inner.js b/www/pad/inner.js
index 4aff47102..5d161142b 100644
--- a/www/pad/inner.js
+++ b/www/pad/inner.js
@@ -629,7 +629,6 @@ define([
                         'max-width: 50em; padding: 20px 30px; margin: 0 auto; min-height: 100%;'+
                         'box-sizing: border-box; overflow: auto;'+
                     '}' +
-                    'html.cke_body_width { overflow: hidden; }' +
                     '.cke_body_width body > *:first-child { margin-top: 0; }';
                 Ckeditor.addCss(newCss);
                 Ckeditor.plugins.addExternal('mediatag','/pad/', 'mediatag-plugin.js');
diff --git a/www/poll/inner.js b/www/poll/inner.js
index 6a77db8a9..a8deebbfe 100644
--- a/www/poll/inner.js
+++ b/www/poll/inner.js
@@ -820,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 ||
diff --git a/www/whiteboard/app-whiteboard.less b/www/whiteboard/app-whiteboard.less
index 7bbb7fbaf..1aba715a9 100644
--- a/www/whiteboard/app-whiteboard.less
+++ b/www/whiteboard/app-whiteboard.less
@@ -42,6 +42,13 @@
         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;
@@ -51,6 +58,8 @@
     .cp-app-whiteboard-canvas-container {
         margin: auto;
         background: white;
+        flex: 1;
+        min-height: 0;
         & > canvas {
             border: 1px solid black;
         }
@@ -71,7 +80,7 @@
         border-top: 1px solid black;
         background: white;
 
-        padding: 1em;
+        padding: 10px;
 
         & > * + * {
             margin: 0;
@@ -127,7 +136,7 @@
         display: flex;
         justify-content: space-between;
 
-        padding: 1em;
+        padding: 10px;
 
         span.cp-app-whiteboard-palette-color {
             height: 4vw;
diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js
index 7228f50c4..efa8edd87 100644
--- a/www/whiteboard/inner.js
+++ b/www/whiteboard/inner.js
@@ -454,6 +454,14 @@ define([
             var $properties = common.createButton('properties', true);
             toolbar.$drawer.append($properties);
 
+            if (Messages.whiteboardHelp) {
+                var $appContainer = $('#cp-app-whiteboard-container');
+                var helpMenu = common.createHelpMenu();
+                $appContainer.prepend(helpMenu.menu);
+                $(helpMenu.text).html(Messages.whiteboardHelp);
+                toolbar.$drawer.append(helpMenu.button);
+            }
+
             if (!readOnly) {
                 makeColorButton($rightside);
 

From 62186bd4bd25a96220d79664bd2f3f2ba3cf2e9d Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Mon, 5 Mar 2018 11:19:01 +0100
Subject: [PATCH 20/21] Remove tooltip when the element is removed from DOM

---
 www/common/common-interface.js | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/www/common/common-interface.js b/www/common/common-interface.js
index 5286a9ac6..d10b289cd 100644
--- a/www/common/common-interface.js
+++ b/www/common/common-interface.js
@@ -677,6 +677,12 @@ define([
         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; }

From fdf2899e8819274f34605179cc4ad0520d8ebfb7 Mon Sep 17 00:00:00 2001
From: ansuz <ansuz@transitiontech.ca>
Date: Mon, 5 Mar 2018 14:51:22 +0100
Subject: [PATCH 21/21] @kpcyrd said I should update this

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 01dd38ab1..d91081835 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "version": "1.27.0",
   "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",