Browse Source

mv path to cryptopad/data/cutomize

samba 5 years ago
parent
commit
ee26b8fdcf
100 changed files with 3430 additions and 0 deletions
  1. 16 0
      cryptopad/data/customize/404.html
  2. 0 0
      cryptopad/data/customize/CryptPad-white-logo.svg
  3. 0 0
      cryptopad/data/customize/CryptPad_logo_color.svg
  4. 1 0
      cryptopad/data/customize/CryptPadlogo_op5.svg
  5. 16 0
      cryptopad/data/customize/about.html
  6. BIN
      cryptopad/data/customize/alt-favicon.png
  7. 12 0
      cryptopad/data/customize/application_config.js
  8. BIN
      cryptopad/data/customize/bg14.jpg
  9. BIN
      cryptopad/data/customize/bkabout.jpg
  10. BIN
      cryptopad/data/customize/bkregister.jpg
  11. BIN
      cryptopad/data/customize/bkwhat.jpg
  12. 70 0
      cryptopad/data/customize/ckeditor-config.js
  13. 207 0
      cryptopad/data/customize/ckeditor-contents.css
  14. 14 0
      cryptopad/data/customize/code.svg
  15. 369 0
      cryptopad/data/customize/config.js
  16. 16 0
      cryptopad/data/customize/contact.html
  17. 83 0
      cryptopad/data/customize/credential.js
  18. BIN
      cryptopad/data/customize/cryptofist_mini.png
  19. BIN
      cryptopad/data/customize/cryptofist_small.png
  20. BIN
      cryptopad/data/customize/cryptpad-new-logo-colors-logoonly.png
  21. 61 0
      cryptopad/data/customize/delta-words.js
  22. 1 0
      cryptopad/data/customize/error.html
  23. 17 0
      cryptopad/data/customize/faq.html
  24. 16 0
      cryptopad/data/customize/features.html
  25. 21 0
      cryptopad/data/customize/fonts/cptools/fonts/cptools.svg
  26. BIN
      cryptopad/data/customize/fonts/cptools/fonts/cptools.ttf
  27. BIN
      cryptopad/data/customize/fonts/cptools/fonts/cptools.woff
  28. 74 0
      cryptopad/data/customize/fonts/cptools/style.css
  29. BIN
      cryptopad/data/customize/fonts/lato/Lato-Black.ttf
  30. BIN
      cryptopad/data/customize/fonts/lato/Lato-Italic.ttf
  31. BIN
      cryptopad/data/customize/fonts/lato/Lato-Regular.ttf
  32. 106 0
      cryptopad/data/customize/fonts/lato/METADATA.json
  33. 93 0
      cryptopad/data/customize/fonts/lato/OFL.txt
  34. BIN
      cryptopad/data/customize/fonts/neuropolitical.ttf
  35. 133 0
      cryptopad/data/customize/fonts/open-sans.less
  36. 87 0
      cryptopad/data/customize/four-oh-four.js
  37. BIN
      cryptopad/data/customize/images/AGPL.png
  38. BIN
      cryptopad/data/customize/images/AaronMacSween.jpg
  39. BIN
      cryptopad/data/customize/images/CalebJames.jpg
  40. BIN
      cryptopad/data/customize/images/Catalin.jpg
  41. BIN
      cryptopad/data/customize/images/LudovicDuboist.jpg
  42. BIN
      cryptopad/data/customize/images/Pierre-new.jpg
  43. BIN
      cryptopad/data/customize/images/YannFlory.jpg
  44. BIN
      cryptopad/data/customize/images/aaron.jpg
  45. BIN
      cryptopad/data/customize/images/atest.jpg
  46. BIN
      cryptopad/data/customize/images/avatar.png
  47. BIN
      cryptopad/data/customize/images/bkcontact.jpg
  48. BIN
      cryptopad/data/customize/images/caleb.jpg
  49. BIN
      cryptopad/data/customize/images/code.png
  50. BIN
      cryptopad/data/customize/images/cover-faq.jpg
  51. BIN
      cryptopad/data/customize/images/cover-features.jpg
  52. BIN
      cryptopad/data/customize/images/cover-privacy.jpg
  53. BIN
      cryptopad/data/customize/images/drive_screenshot.png
  54. 25 0
      cryptopad/data/customize/images/email.svg
  55. 22 0
      cryptopad/data/customize/images/github.svg
  56. BIN
      cryptopad/data/customize/images/hash.png
  57. 1 0
      cryptopad/data/customize/images/icons/folder.svg
  58. 1 0
      cryptopad/data/customize/images/icons/folderOpen.svg
  59. 4 0
      cryptopad/data/customize/images/icons/ic_folder_black_24px.svg
  60. 4 0
      cryptopad/data/customize/images/icons/ic_folder_open_black_24px.svg
  61. 30 0
      cryptopad/data/customize/images/irc.svg
  62. 19 0
      cryptopad/data/customize/images/issue.svg
  63. BIN
      cryptopad/data/customize/images/key_small.png
  64. BIN
      cryptopad/data/customize/images/logo_white.png
  65. 0 0
      cryptopad/data/customize/images/logo_white.svg
  66. BIN
      cryptopad/data/customize/images/ludovic.jpg
  67. BIN
      cryptopad/data/customize/images/ngi.png
  68. BIN
      cryptopad/data/customize/images/organize.png
  69. BIN
      cryptopad/data/customize/images/pad.png
  70. BIN
      cryptopad/data/customize/images/pad_screenshot.png
  71. BIN
      cryptopad/data/customize/images/pierre.jpg
  72. BIN
      cryptopad/data/customize/images/poll.png
  73. BIN
      cryptopad/data/customize/images/realtime_small.png
  74. 17 0
      cryptopad/data/customize/images/sayhi.svg
  75. BIN
      cryptopad/data/customize/images/slide.png
  76. 17 0
      cryptopad/data/customize/images/twitter.svg
  77. BIN
      cryptopad/data/customize/images/useraccount.png
  78. BIN
      cryptopad/data/customize/images/yann.jpg
  79. BIN
      cryptopad/data/customize/images/zeroknowledge_small.png
  80. BIN
      cryptopad/data/customize/images/zk.png
  81. 16 0
      cryptopad/data/customize/index.html
  82. BIN
      cryptopad/data/customize/loading-logo.png
  83. 200 0
      cryptopad/data/customize/loading.js
  84. 544 0
      cryptopad/data/customize/login.js
  85. BIN
      cryptopad/data/customize/main-favicon.png
  86. 25 0
      cryptopad/data/customize/main.js
  87. 154 0
      cryptopad/data/customize/messages.js
  88. 146 0
      cryptopad/data/customize/pages.js
  89. 128 0
      cryptopad/data/customize/pages/about.js
  90. 53 0
      cryptopad/data/customize/pages/contact.js
  91. 61 0
      cryptopad/data/customize/pages/faq.js
  92. 115 0
      cryptopad/data/customize/pages/features.js
  93. 151 0
      cryptopad/data/customize/pages/index.js
  94. 43 0
      cryptopad/data/customize/pages/login.js
  95. 46 0
      cryptopad/data/customize/pages/privacy.js
  96. 69 0
      cryptopad/data/customize/pages/register.js
  97. 21 0
      cryptopad/data/customize/pages/terms.js
  98. 64 0
      cryptopad/data/customize/pages/what-is-cryptpad.js
  99. 16 0
      cryptopad/data/customize/privacy.html
  100. 25 0
      cryptopad/data/customize/readme.md

+ 16 - 0
cryptopad/data/customize/404.html

@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html class="cp" id="four-oh-four">
+<!-- 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/four-oh-four.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>
+<h1>404</h1>
+<h3>We couldn't find the page you were looking for</h3>
+
+</noscript>

File diff suppressed because it is too large
+ 0 - 0
cryptopad/data/customize/CryptPad-white-logo.svg


File diff suppressed because it is too large
+ 0 - 0
cryptopad/data/customize/CryptPad_logo_color.svg


+ 1 - 0
cryptopad/data/customize/CryptPadlogo_op5.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 393.94 476.2"><defs><style>.cls-1{opacity:0.04;}.cls-2{fill:#999;}.cls-3{fill:#4591c4;}</style></defs><title>CryptPadlogo</title><g id="Layer_2" data-name="Layer 2"><g id="svg2"><g id="g4764" class="cls-1"><path class="cls-2" d="M139.36,288.16l32.07-64.43a13.59,13.59,0,0,0-4.23-16.62,48.65,48.65,0,0,1-21.28-48.25l-23.47-15.31a75.74,75.74,0,0,0-3.36,21.87c0,22,9.52,41.45,24.35,55.54l-27.24,54.8c-.11,0-.2,0-.31,0a23.27,23.27,0,1,0,0,46.53,23,23,0,0,0,17.31-7.86h40.27V288.16Z"/><path class="cls-2" d="M278.28,275.73c-.57,0-1.11.13-1.68.17l-27.33-55.09c14.75-14.07,24.2-33.47,24.2-55.39a77.13,77.13,0,0,0-3.06-21.72l-23.62,15.45a51.69,51.69,0,0,1,.44,6.27A50.21,50.21,0,0,1,225.65,207a13.58,13.58,0,0,0-4.22,16.62l31.77,64.58h-34V314.4h41.63a23.23,23.23,0,1,0,17.41-38.67Z"/><polygon class="cls-2" points="270.41 143.7 270.41 143.7 270.41 143.7 270.41 143.7"/><circle class="cls-3" cx="196.06" cy="167.4" r="21.21"/><path class="cls-3" d="M362.25,21.36a31.14,31.14,0,0,0-18.1,5.8L197,0,50,27.16a31.62,31.62,0,1,0-33.68,53.4v212c0,19.95,8.93,41.51,26.62,64.08,15.66,20,37.82,40.46,65.89,60.83a603,603,0,0,0,57,36.21,31.54,31.54,0,0,0,60,1.25,606,606,0,0,0,59.26-37.46c28.09-20.37,50.23-40.86,65.9-60.83,17.7-22.6,26.61-44.13,26.61-64.08V80.38a31.46,31.46,0,0,0-15.39-59ZM62.82,55.94,196.61,31.32l134.33,24.8a29.58,29.58,0,0,0,.9,5.61L253.09,113a76.78,76.78,0,0,0-113.69.36L61.55,62.82A31.8,31.8,0,0,0,62.82,55.94ZM350.49,288.21a51.41,51.41,0,0,1-.73,8.51c-3.16,12.6-10.11,25.8-20.82,39.47-13.41,17.09-32.43,34.52-56.48,51.95a532.75,532.75,0,0,1-54.19,34.09,31.5,31.5,0,0,0-43.73-.62,534,534,0,0,1-53.06-33.47c-24-17.43-42.9-34.86-56.31-51.95-12.77-16.31-20.25-31.94-22.26-46.71,0-.41,0-.86,0-1.27V82.37a31.51,31.51,0,0,0,8.69-5.25l88.88,57.75.37-.36,10.5,6.88a51,51,0,0,1,45.07-27,50.37,50.37,0,0,1,45.08,27.15l22.09-14.3L341.44,76.4A32.55,32.55,0,0,0,350.49,82Z"/></g></g></g></svg>

+ 16 - 0
cryptopad/data/customize/about.html

@@ -0,0 +1,16 @@
+<!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>

BIN
cryptopad/data/customize/alt-favicon.png


+ 12 - 0
cryptopad/data/customize/application_config.js

@@ -0,0 +1,12 @@
+/*
+ * You can override the configurable values from this file.
+ * The recommended method is to make a copy of this file (/customize.dist/application_config.js)
+   in a 'customize' directory (/customize/application_config.js).
+ * If you want to check all the configurable values, you can open the internal configuration file
+   but you should not change it directly (/common/application_config_internal.js)
+*/
+define(['/common/application_config_internal.js'], function (AppConfig) {
+    AppConfig.availablePadTypes = ['drive','pad','code','slide'];
+    return AppConfig;
+});
+

BIN
cryptopad/data/customize/bg14.jpg


BIN
cryptopad/data/customize/bkabout.jpg


BIN
cryptopad/data/customize/bkregister.jpg


BIN
cryptopad/data/customize/bkwhat.jpg


+ 70 - 0
cryptopad/data/customize/ckeditor-config.js

@@ -0,0 +1,70 @@
+/* global CKEDITOR */
+CKEDITOR.editorConfig = function( config ) {
+    var fixThings = false;
+    // https://dev.ckeditor.com/ticket/10907
+    config.needsBrFiller= fixThings;
+    config.needsNbspFiller= fixThings;
+
+    config.removeButtons= 'Source,Maximize';
+    // magicline plugin inserts html crap into the document which is not part of the
+    // document itself and causes problems when it's sent across the wire and reflected back
+    config.removePlugins= 'resize,elementspath';
+    config.resize_enabled= false; //bottom-bar
+    config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock,justify,mediatag,print,blockbase64';
+    config.toolbarGroups= [
+        // {"name":"clipboard","groups":["clipboard","undo"]},
+        //{"name":"editing","groups":["find","selection"]},
+        {"name":"links"},
+        {"name":"insert"},
+        {"name":"forms"},
+        {"name":"tools"},
+        {"name":"document","groups":["mode","document","doctools"]},
+        {"name":"others"},
+        {"name":"basicstyles","groups":["basicstyles","cleanup"]},
+        {"name":"paragraph","groups":["list","indent","blocks","align","bidi"]},
+        {"name":"styles"},
+        {"name":"colors"},
+        {"name":"print"}];
+
+    config.font_defaultLabel = 'Arial';
+    config.fontSize_defaultLabel = '16';
+
+    config.keystrokes = [
+        [ CKEDITOR.ALT + 121 /*F10*/, 'toolbarFocus' ],
+        [ CKEDITOR.ALT + 122 /*F11*/, 'elementsPathFocus' ],
+
+        [ CKEDITOR.SHIFT + 121 /*F10*/, 'contextMenu' ],
+
+        [ CKEDITOR.CTRL + 90 /*Z*/, 'undo' ],
+        [ CKEDITOR.CTRL + 89 /*Y*/, 'redo' ],
+        [ CKEDITOR.CTRL + CKEDITOR.SHIFT + 90 /*Z*/, 'redo' ],
+
+        [ CKEDITOR.CTRL + CKEDITOR.SHIFT + 76 /*L*/, 'link' ],
+        [ CKEDITOR.CTRL + 76 /*L*/, undefined ],
+
+        [ CKEDITOR.CTRL + 66 /*B*/, 'bold' ],
+        [ CKEDITOR.CTRL + 73 /*I*/, 'italic' ],
+        [ CKEDITOR.CTRL + 85 /*U*/, 'underline' ],
+
+        [ CKEDITOR.ALT + 109 /*-*/, 'toolbarCollapse' ]
+    ];
+
+    //skin: 'moono-cryptpad,/pad/themes/moono-cryptpad/'
+    //skin: 'flat,/pad/themes/flat/'
+    //config.skin= 'moono-lisa,/pad/themes/moono-lisa/'
+    skin: 'moono-dark,/pad/themes/moono-dark/'
+    //skin: 'office2013,/pad/themes/office2013/'
+};
+
+(function () {
+    // These are overrides inside of ckeditor which add ?ver= to the CSS files so that
+    // every part of ckeditor will get in the browser cache.
+    var fix = function (x) {
+        if (x.map) { return x.map(fix); }
+        return (/\/bower_components\/.*\.css$/.test(x)) ? (x + '?ver=' + CKEDITOR.timestamp) : x;
+    };
+    CKEDITOR.tools._buildStyleHtml = CKEDITOR.tools.buildStyleHtml;
+    CKEDITOR.document._appendStyleSheet = CKEDITOR.document.appendStyleSheet;
+    CKEDITOR.tools.buildStyleHtml = function (x) { return CKEDITOR.tools._buildStyleHtml(fix(x)); };
+    CKEDITOR.document.appendStyleSheet = function (x) { return CKEDITOR.document._appendStyleSheet(fix(x)); };
+}());

+ 207 - 0
cryptopad/data/customize/ckeditor-contents.css

@@ -0,0 +1,207 @@
+/*
+Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.md or http://ckeditor.com/license
+*/
+
+body
+{
+    /* Font */
+    font-family: sans-serif, Arial, Verdana, "Trebuchet MS";
+    font-size: 13px;
+
+    /* Text color */
+    color: #333;
+
+    /* Remove the background color to make it transparent */
+    background-color: #fff;
+
+    margin: 0;
+    padding: 20px;
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+
+/* Remove margin-top for the first element */
+body > *:first-child {
+    margin-top: 0;
+}
+
+/* If the magic line is the first element, remove margin-top for the next one */
+body > .non-realtime:first-child + * {
+    margin-top: 0;
+}
+
+.cke_editable
+{
+    font-size: 16px;
+    line-height: 1.6;
+
+    /* Fix for missing scrollbars with RTL texts. (#10488) */
+    word-wrap: break-word;
+}
+
+blockquote
+{
+    font-style: italic;
+    font-family: Georgia, Times, "Times New Roman", serif;
+    padding: 2px 0;
+    border-style: solid;
+    border-color: #ccc;
+    border-width: 0;
+}
+
+.cke_contents_ltr blockquote
+{
+    padding-left: 20px;
+    padding-right: 8px;
+    border-left-width: 5px;
+}
+
+.cke_contents_rtl blockquote
+{
+    padding-left: 8px;
+    padding-right: 20px;
+    border-right-width: 5px;
+}
+
+a
+{
+    color: #0782C1;
+}
+
+ol,ul,dl
+{
+    /* IE7: reset rtl list margin. (#7334) */
+    *margin-right: 0px;
+    /* preserved spaces for list items with text direction other than the list. (#6249,#8049)*/
+    padding: 0 40px;
+}
+
+h1,h2,h3,h4,h5,h6
+{
+    font-weight: normal;
+    line-height: 1.2;
+}
+
+hr
+{
+    border: 0px;
+    border-top: 1px solid #ccc;
+}
+
+img.right
+{
+    border: 1px solid #ccc;
+    float: right;
+    margin-left: 15px;
+    padding: 5px;
+}
+
+img.left
+{
+    border: 1px solid #ccc;
+    float: left;
+    margin-right: 15px;
+    padding: 5px;
+}
+
+pre
+{
+    white-space: pre-wrap; /* CSS 2.1 */
+    word-wrap: break-word; /* IE7 */
+    -moz-tab-size: 4;
+    tab-size: 4;
+}
+
+.marker
+{
+    background-color: Yellow;
+}
+
+span[lang]
+{
+    font-style: italic;
+}
+
+figure
+{
+    text-align: center;
+    border: solid 1px #ccc;
+    border-radius: 2px;
+    background: rgba(0,0,0,0.05);
+    padding: 10px;
+    margin: 10px 20px;
+    display: inline-block;
+}
+
+figure > figcaption
+{
+    text-align: center;
+    display: block; /* For IE8 */
+}
+
+a > img {
+    padding: 1px;
+    margin: 1px;
+    border: none;
+    outline: 1px solid #0782C1;
+}
+
+.cp-cursor-position {
+    cursor: default;
+    background-color: red;
+    background-clip: padding-box;
+    padding: 0 1px;
+    border: 2px solid red;
+    border-right-color: transparent !important;
+    border-left-color: transparent !important;
+    margin-left: -3px;
+    margin-right: -3px;
+}
+.cp-cursor-position[data-type="start"] {
+    border-left: none;
+    border-right-width: 4px;
+    margin-right: -5px;
+    margin-left: -1px;
+}
+.cp-cursor-position[data-type="end"] {
+    border-right: none;
+    border-left-width: 4px;
+    margin-left: -5px;
+    margin-right: -1px;
+}
+.cp-cursor-avatar {
+    display: flex;
+    align-items: center;
+}
+.cp-cursor-avatar media-tag {
+    min-height: 32px;
+    max-height: 32px;
+    min-width: 32px;
+    max-width: 32px;
+    margin-right: 10px;
+}
+.cp-cursor-avatar media-tag img {
+    border-radius: 4px;
+    max-height: 100%;
+    max-width: 100%;
+}
+
+.cp-link-clicked {
+    position: absolute;
+    background: white;
+    border: 1px solid #333;
+    border-radius: 5px;
+    padding: 3px 8px;
+    display: inline-block;
+    max-width: 200px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+}
+.cp-link-clicked a {
+    cursor: pointer;
+}

+ 14 - 0
cryptopad/data/customize/code.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:url(#SVGID_1_);}
+</style>
+<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="1.6" y1="12" x2="22.4" y2="12">
+	<stop  offset="0" style="stop-color:#4592C4"/>
+	<stop  offset="1" style="stop-color:#545ACD"/>
+</linearGradient>
+<path class="st0" d="M12.9,3l2,0.4L11.1,21l-2-0.4L12.9,3 M19.6,12L16,8.4V5.6l6.4,6.4L16,18.4v-2.8L19.6,12 M1.6,12L8,5.6v2.8
+	L4.4,12L8,15.6v2.8L1.6,12z"/>
+</svg>

+ 369 - 0
cryptopad/data/customize/config.js

@@ -0,0 +1,369 @@
+/*@flow*/
+/*
+    globals module
+*/
+var _domain = 'http://pad.cisti.org:2000/';
+
+// You can `kill -USR2` the node process and it will write out a heap dump.
+// If your system doesn't support dumping, comment this out and install with
+// `npm install --production`
+// See: https://strongloop.github.io/strongloop.com/strongblog/how-to-heap-snapshots/
+
+// to enable this feature, uncomment the line below:
+// require('heapdump');
+
+
+// we prepend a space because every usage expects it
+// requiring admins to preserve it is unnecessarily confusing
+var domain = ' ' + _domain;
+module.exports = {
+
+    // the address you want to bind to, :: means all ipv4 and ipv6 addresses
+    // this may not work on all operating systems
+    httpAddress: '::',
+
+    // the port on which your httpd will listen
+
+    /*  CryptPad can be configured to send customized HTTP Headers
+     *  These settings may vary widely depending on your needs
+     *  Examples are provided below
+     */
+
+    httpHeaders: {
+        "X-XSS-Protection": "1; mode=block",
+        "X-Content-Type-Options": "nosniff",
+        "Access-Control-Allow-Origin": "*"
+    },
+
+    contentSecurity: [
+        "default-src 'none'",
+        "style-src 'unsafe-inline' 'self' " + domain,
+        "script-src 'self'" + domain,
+        "font-src 'self' data:" + domain,
+
+        /*  child-src is used to restrict iframes to a set of allowed domains.
+         *  connect-src is used to restrict what domains can connect to the websocket.
+         *
+         *  it is recommended that you configure these fields to match the
+         *  domain which will serve your CryptPad instance.
+         */
+        "child-src blob: *",
+        // IE/Edge
+        "frame-src blob: *",
+
+        "media-src * blob:",
+
+        /*  this allows connections over secure or insecure websockets
+            if you are deploying to production, you'll probably want to remove
+            the ws://* directive, and change '*' to your domain
+         */
+        "connect-src 'self' ws: wss: blob:" + domain,
+
+        // data: is used by codemirror
+        "img-src 'self' data: blob:" + domain,
+
+        // for accounts.cryptpad.fr authentication and pad2 cross-domain iframe sandbox
+        "frame-ancestors *",
+    ].join('; '),
+
+    // CKEditor requires significantly more lax content security policy in order to function.
+    padContentSecurity: [
+        "default-src 'none'",
+        "style-src 'unsafe-inline' 'self'" + domain,
+        // Unsafe inline, unsafe-eval are needed for ckeditor :(
+        "script-src 'self' 'unsafe-eval' 'unsafe-inline'" + domain,
+        "font-src 'self'" + domain,
+
+        /*  See above under 'contentSecurity' as to how these values should be
+         *  configured for best effect.
+         */
+        "child-src *",
+        // IE/Edge
+        "frame-src *",
+
+        // see the comment above in the 'contentSecurity' section
+         "connect-src 'self' ws: wss:" + domain,
+
+        // (insecure remote) images are included by users of the wysiwyg who embed photos in their pads
+        "img-src * blob:",
+    ].join('; '),
+
+    // OnlyOffice requires even more lax content security policy in order to function.
+    ooContentSecurity: [
+        "default-src 'none'",
+        "style-src 'unsafe-inline' 'self'" + domain,
+        // Unsafe inline, unsafe-eval are needed for ckeditor :(
+        "script-src 'self' 'unsafe-eval' 'unsafe-inline'" + domain,
+        "font-src 'self'" + domain,
+
+        /*  See above under 'contentSecurity' as to how these values should be
+         *  configured for best effect.
+         */
+        "child-src *",
+        // IE/Edge
+        "frame-src *",
+
+        // see the comment above in the 'contentSecurity' section
+         "connect-src 'self' blob: ws: wss:" + domain,
+
+        // (insecure remote) images are included by users of the wysiwyg who embed photos in their pads
+        "img-src * blob: data:",
+    ].join('; '),
+
+    httpPort: 2000,
+
+    // This is for allowing the cross-domain iframe to function when developing
+    httpSafePort: 2001,
+
+    // This is for deployment in production, CryptPad uses a separate origin (domain) to host the
+    // cross-domain iframe. It can simply host the same content as CryptPad.
+    // httpSafeOrigin: "https://some-other-domain.xyz",
+
+    httpUnsafeOrigin: domain,
+
+    /*  your server's websocket url is configurable
+     *  (default: '/cryptpad_websocket')
+     *
+     *  websocketPath can be relative, of the form '/path/to/websocket'
+     *  or absolute, specifying a particular URL
+     *
+     *  'wss://cryptpad.fr:3000/cryptpad_websocket'
+     */
+    websocketPath: '/cryptpad_websocket',
+
+    /*  CryptPad can log activity to stdout
+     *  This may be useful for debugging
+     */
+    logToStdout: true,
+
+    /*  CryptPad supports verbose logging
+     *  (false by default)
+     */
+     verbose: false,
+
+    /*  Main pages
+     *  add exceptions to the router so that we can access /privacy.html
+     *  and other odd pages
+     */
+    mainPages: [
+        'index',
+//        'privacy',
+//        'terms',
+//        'about',
+//        'contact',
+//        'what-is-cryptpad',
+//        'features',
+//        'faq'
+    ],
+
+    /*  Limits, Donations, Subscriptions and Contact
+     *
+     *  By default, CryptPad limits every registered user to 50MB of storage. It also shows a
+     *  subscribe button which allows them to upgrade to a paid account. We handle payment,
+     *  and keep 50% of the proceeds to fund ongoing development.
+     *
+     *  You can:
+     *  A: leave things as they are
+     *  B: disable accounts but display a donate button
+     *  C: hide any reference to paid accounts or donation
+     *
+     *  If you chose A then there's nothing to do.
+     *  If you chose B, set 'allowSubscriptions' to false.
+     *  If you chose C, set 'removeDonateButton' to true
+     */
+    allowSubscriptions: true,
+    removeDonateButton: true,
+
+    /*  Sales coming from your server will be identified by your domain
+     *
+     *  If you are using CryptPad in a business context, please consider taking a support contract
+     *  by contacting sales@cryptpad.fr
+     */
+    myDomain: _domain,
+
+    /*
+     *  If you are using CryptPad internally and you want to increase the per-user storage limit,
+     *  change the following value.
+     *
+     *  Please note: This limit is what makes people subscribe and what pays for CryptPad
+     *    development. Running a public instance that provides a "better deal" than cryptpad.fr
+     *    is effectively using the project against itself.
+     */
+    defaultStorageLimit: 50 * 1024 * 1024,
+
+    /*
+     *  CryptPad allows administrators to give custom limits to their friends.
+     *  add an entry for each friend, identified by their user id,
+     *  which can be found on the settings page. Include a 'limit' (number of bytes),
+     *  a 'plan' (string), and a 'note' (string).
+     *
+     *  hint: 1GB is 1024 * 1024 * 1024 bytes
+     */
+    customLimits: {
+        /*
+        "https://my.awesome.website/user/#/1/cryptpad-user1/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=": {
+            limit: 20 * 1024 * 1024 * 1024,
+            plan: 'insider',
+            note: 'storage space donated by my.awesome.website'
+        },
+        "https://my.awesome.website/user/#/1/cryptpad-user2/GdflkgdlkjeworijfkldfsdflkjeEAsdlEnkbx1vVOo=": {
+            limit: 10 * 1024 * 1024 * 1024,
+            plan: 'insider',
+            note: 'storage space donated by my.awesome.website'
+        }
+        */
+    },
+
+    /*  some features may require that the server be able to schedule tasks
+        far into the future, such as:
+            > "three months from now, this channel should expire"
+        To disable these features, set 'enableTaskScheduling' to false
+    */
+    enableTaskScheduling: true,
+
+    /*  if you would like the list of scheduled tasks to be stored in
+        a custom location, change the path below:
+    */
+    taskPath: './tasks',
+
+    /*  if you would like users' authenticated blocks to be stored in
+        a custom location, change the path below:
+    */
+    blockPath: './block',
+
+    /*
+     *  By default, CryptPad also contacts our accounts server once a day to check for changes in
+     *  the people who have accounts. This check-in will also send the version of your CryptPad
+     *  instance and your email so we can reach you if we are aware of a serious problem. We will
+     *  never sell it or send you marketing mail. If you want to block this check-in and remain
+     *  completely invisible, set this and allowSubscriptions both to false.
+     */
+    adminEmail: 'admin@cisti.org',
+
+
+    /*
+        You have the option of specifying an alternative storage adaptor.
+        These status of these alternatives are specified in their READMEs,
+        which are available at the following URLs:
+
+        mongodb: a noSQL database
+            https://github.com/xwiki-labs/cryptpad-mongo-store
+        amnesiadb: in memory storage
+            https://github.com/xwiki-labs/cryptpad-amnesia-store
+        leveldb: a simple, fast, key-value store
+            https://github.com/xwiki-labs/cryptpad-level-store
+        sql: an adaptor for a variety of sql databases via knexjs
+            https://github.com/xwiki-labs/cryptpad-sql-store
+
+        For the most up to date solution, use the default storage adaptor.
+    */
+    storage: './storage/file',
+
+    /*
+        CryptPad stores each document in an individual file on your hard drive.
+        Specify a directory where files should be stored.
+        It will be created automatically if it does not already exist.
+    */
+    filePath: './datastore/',
+
+    /*  CryptPad allows logged in users to request that particular documents be
+     *  stored by the server indefinitely. This is called 'pinning'.
+     *  Pin requests are stored in a pin-store. The location of this store is
+     *  defined here.
+     */
+    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.
+     */
+    blobPath: './blob',
+
+    /*  CryptPad stores incomplete blobs in a 'staging' area until they are
+     *  fully uploaded. Set its location here.
+     */
+    blobStagingPath: './blobstage',
+
+    /*  CryptPad's file storage adaptor closes unused files after a configurable
+     *  number of milliseconds (default 30000 (30 seconds))
+     */
+    channelExpirationMs: 30000,
+
+    /*  CryptPad's file storage adaptor is limited by the number of open files.
+     *  When the adaptor reaches openFileLimit, it will clean up older files
+     */
+    openFileLimit: 2048,
+
+    /*  CryptPad's socket server can be extended to respond to RPC calls
+     *  you can configure it to respond to custom RPC calls if you like.
+     *  provide the path to your RPC module here, or `false` if you would
+     *  like to disable the RPC interface completely
+     */
+    rpc: './rpc.js',
+
+    /*  RPC errors are shown by default, but if you really don't care,
+     *  you can suppress them
+     */
+    suppressRPCErrors: false,
+
+    /*  Setting this value to anything other than true will cause file upload
+     *  attempts to be rejected outright.
+     */
+    enableUploads: true,
+
+    /*  If you have enabled file upload, you have the option of restricting it
+     *  to a list of users identified by their public keys. If this value is set
+     *  to true, your server will query a file (cryptpad/privileged.conf) when
+     *  users connect via RPC. Only users whose public keys can be found within
+     *  the file will be allowed to upload.
+     *
+     *  privileged.conf uses '#' for line comments, and splits keys by newline.
+     *  This is a temporary measure until a better quota system is in place.
+     *  registered users' public keys can be found on the settings page.
+     */
+    //restrictUploads: false,
+
+    /*  Max Upload Size (bytes)
+     *  this sets the maximum size of any one file uploaded to the server.
+     *  anything larger than this size will be rejected
+     */
+    maxUploadSize: 20 * 1024 * 1024,
+
+    /*  clients can use the /settings/ app to opt out of usage feedback
+     *  which informs the server of things like how much each app is being
+     *  used, and whether certain clientside features are supported by
+     *  the client's browser. The intent is to provide feedback to the admin
+     *  such that the service can be improved. Enable this with `true`
+     *  and ignore feedback with `false` or by commenting the attribute
+     */
+    //logFeedback: true,
+
+    /*  If you wish to see which remote procedure calls clients request,
+     *  set this to true
+     */
+    //logRPC: true,
+
+    /* it is recommended that you serve CryptPad over https
+     * the filepaths below are used to configure your certificates
+     */
+    //privKeyAndCertFiles: [
+    //  '/etc/apache2/ssl/my_secret.key',
+    //  '/etc/apache2/ssl/my_public_cert.crt',
+    //  '/etc/apache2/ssl/my_certificate_authorities_cert_chain.ca'
+    //],
+
+    /* You can get a repl for debugging the server if you want it.
+     * to enable this, specify the debugReplName and then you can
+     * connect to it with `nc -U /tmp/repl/<your name>.sock`
+     * If you run multiple cryptpad servers, you need to use different
+     * repl names.
+     */
+    //debugReplName: "cryptpad"
+};

+ 16 - 0
cryptopad/data/customize/contact.html

@@ -0,0 +1,16 @@
+<!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>

+ 83 - 0
cryptopad/data/customize/credential.js

@@ -0,0 +1,83 @@
+define([
+    '/customize/application_config.js',
+    '/bower_components/scrypt-async/scrypt-async.min.js',
+], function (AppConfig) {
+    var Cred = {};
+    var Scrypt = window.scrypt;
+
+    Cred.MINIMUM_PASSWORD_LENGTH = typeof(AppConfig.minimumPasswordLength) === 'number'?
+        AppConfig.minimumPasswordLength: 8;
+
+    Cred.isLongEnoughPassword = function (passwd) {
+        return passwd.length >= Cred.MINIMUM_PASSWORD_LENGTH;
+    };
+
+    var isString = Cred.isString = function (x) {
+        return typeof(x) === 'string';
+    };
+
+    Cred.isValidUsername = function (name) {
+        return !!(name && isString(name));
+    };
+
+    Cred.isValidPassword = function (passwd) {
+        return !!(passwd && isString(passwd));
+    };
+
+    Cred.passwordsMatch = function (a, b) {
+        return isString(a) && isString(b) && a === b;
+    };
+
+    Cred.customSalt = function () {
+        return typeof(AppConfig.loginSalt) === 'string'?
+            AppConfig.loginSalt: '';
+    };
+
+    Cred.deriveFromPassphrase = function (username, password, len, cb) {
+        Scrypt(password,
+            username + Cred.customSalt(), // salt
+            8, // memoryCost (n)
+            1024, // block size parameter (r)
+            len || 128, // dkLen
+            200, // interruptStep
+            cb,
+            undefined); // format, could be 'base64'
+    };
+
+    Cred.dispenser = function (bytes) {
+        var entropy = {
+            used: 0,
+        };
+
+        // crypto hygeine
+        var consume = function (n) {
+            // explode if you run out of bytes
+            if (entropy.used + n > bytes.length) {
+                throw new Error('exceeded available entropy');
+            }
+            if (typeof(n) !== 'number') { throw new Error('expected a number'); }
+            if (n <= 0) {
+                throw new Error('expected to consume a positive number of bytes');
+            }
+
+            // grab an unused slice of the entropy
+            // Note: Internet Explorer doesn't support .slice on Uint8Array
+            var A;
+            if (bytes.slice) {
+                A = bytes.slice(entropy.used, entropy.used + n);
+            } else {
+                A = bytes.subarray(entropy.used, entropy.used + n);
+            }
+
+            // account for the bytes you used so you don't reuse bytes
+            entropy.used += n;
+
+            //console.info("%s bytes of entropy remaining", bytes.length - entropy.used);
+            return A;
+        };
+
+        return consume;
+    };
+
+    return Cred;
+});

BIN
cryptopad/data/customize/cryptofist_mini.png


BIN
cryptopad/data/customize/cryptofist_small.png


BIN
cryptopad/data/customize/cryptpad-new-logo-colors-logoonly.png


+ 61 - 0
cryptopad/data/customize/delta-words.js

@@ -0,0 +1,61 @@
+define([
+    '/bower_components/chainpad/chainpad.dist.js',
+], function (ChainPad) {
+    var Diff = ChainPad.Diff;
+
+    var isSpace = function (S, i) {
+        return /^\s$/.test(S.charAt(i));
+    };
+
+    var leadingBoundary = function (S, offset) {
+        if (/\s/.test(S.charAt(offset))) { return offset; }
+        while (offset > 0) {
+            offset--;
+            if (isSpace(S, offset)) { offset++; break; }
+        }
+        return offset;
+    };
+
+    var trailingBoundary = function (S, offset) {
+        if (isSpace(S, offset)) { return offset; }
+        while (offset < S.length && !/\s/.test(S.charAt(offset))) {
+            offset++;
+        }
+        return offset;
+    };
+
+    var opsToWords = function (previous, current) {
+        var output = [];
+        Diff.diff(previous, current).forEach(function (op) {
+            // ignore deleted sections...
+            var offset = op.offset;
+            var toInsert = op.toInsert;
+
+            // given an operation,  check whether it is a word fragment,
+            // if it is, expand it to its word boundaries
+            var first = current.slice(leadingBoundary(current, offset), offset);
+            var last = current.slice(offset + toInsert.length, trailingBoundary(current, offset + toInsert.length));
+
+            var result = first + toInsert + last;
+            // concat-in-place
+            Array.prototype.push.apply(output, result.split(/\s+/));
+        });
+        return output.filter(Boolean);
+    };
+
+    var runningDiff = function (getter, f, time) {
+        var last = getter();
+        // first time through, send all the words :D
+        f(opsToWords("", last));
+        return setInterval(function () {
+            var current = getter();
+
+            // find inserted words...
+            var words = opsToWords(last, current);
+            last = current;
+            f(words);
+        }, time);
+    };
+
+    return runningDiff;
+});

+ 1 - 0
cryptopad/data/customize/error.html

@@ -0,0 +1 @@
+ERROR 42: porcodio

+ 17 - 0
cryptopad/data/customize/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>
+

+ 16 - 0
cryptopad/data/customize/features.html

@@ -0,0 +1,16 @@
+<!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>

File diff suppressed because it is too large
+ 21 - 0
cryptopad/data/customize/fonts/cptools/fonts/cptools.svg


BIN
cryptopad/data/customize/fonts/cptools/fonts/cptools.ttf


BIN
cryptopad/data/customize/fonts/cptools/fonts/cptools.woff


+ 74 - 0
cryptopad/data/customize/fonts/cptools/style.css

@@ -0,0 +1,74 @@
+@font-face {
+  font-family: 'cptools';
+  src:
+    url('fonts/cptools.ttf?703wg5') format('truetype'),
+    url('fonts/cptools.woff?703wg5') format('woff'),
+    url('fonts/cptools.svg?703wg5#cptools') format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
+
+.cptools {
+  /* use !important to prevent issues with browser extensions that change fonts */
+  font-family: 'cptools' !important;
+  display: inline-block;
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+
+  /* Better Font Rendering =========== */
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.cptools-slide:before {
+  content: "\e902";
+}
+.cptools-shared-folder:before {
+  content: "\e903";
+}
+.cptools-poll:before {
+  content: "\e904";
+}
+.cptools-file-upload:before {
+  content: "\e905";
+}
+.cptools-whiteboard:before {
+  content: "\e906";
+}
+.cptools-todo:before {
+  content: "\e907";
+}
+.cptools-pad:before {
+  content: "\e908";
+}
+.cptools-folder-open:before {
+  content: "\e909";
+}
+.cptools-kanban:before {
+  content: "\e90a";
+}
+.cptools-folder:before {
+  content: "\e90b";
+}
+.cptools-shared-folder-open:before {
+  content: "\e90c";
+}
+.cptools-file:before {
+  content: "\e90d";
+}
+.cptools-contacts:before {
+  content: "\e90e";
+}
+.cptools-code:before {
+  content: "\e90f";
+}
+.cptools-template:before {
+  content: "\e900";
+}
+.cptools-new-template:before {
+  content: "\e901";
+}

BIN
cryptopad/data/customize/fonts/lato/Lato-Black.ttf


BIN
cryptopad/data/customize/fonts/lato/Lato-Italic.ttf


BIN
cryptopad/data/customize/fonts/lato/Lato-Regular.ttf


+ 106 - 0
cryptopad/data/customize/fonts/lato/METADATA.json

@@ -0,0 +1,106 @@
+{
+  "name": "Lato",
+  "designer": "Łukasz Dziedzic",
+  "license": "OFL",
+  "visibility": "External",
+  "category": "Sans Serif",
+  "size": 51318,
+  "fonts": [
+    {
+      "name": "Lato",
+      "style": "normal",
+      "weight": 100,
+      "filename": "Lato-Hairline.ttf",
+      "postScriptName": "Lato-Hairline",
+      "fullName": "Lato Hairline",
+      "copyright": "Copyright (c) 2010-2011 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name \"Lato\". Licensed under the SIL Open Font License, Version 1.1."
+    },
+    {
+      "name": "Lato",
+      "style": "italic",
+      "weight": 100,
+      "filename": "Lato-HairlineItalic.ttf",
+      "postScriptName": "Lato-HairlineItalic",
+      "fullName": "Lato Hairline Italic",
+      "copyright": "Copyright (c) 2010-2011 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name \"Lato\". Licensed under the SIL Open Font License, Version 1.1."
+    },
+    {
+      "name": "Lato",
+      "style": "normal",
+      "weight": 300,
+      "filename": "Lato-Light.ttf",
+      "postScriptName": "Lato-Light",
+      "fullName": "Lato Light",
+      "copyright": "Copyright (c) 2010-2011 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name \"Lato\". Licensed under the SIL Open Font License, Version 1.1."
+    },
+    {
+      "name": "Lato",
+      "style": "italic",
+      "weight": 300,
+      "filename": "Lato-LightItalic.ttf",
+      "postScriptName": "Lato-LightItalic",
+      "fullName": "Lato Light Italic",
+      "copyright": "Copyright (c) 2010-2011 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name \"Lato\". Licensed under the SIL Open Font License, Version 1.1."
+    },
+    {
+      "name": "Lato",
+      "style": "normal",
+      "weight": 400,
+      "filename": "Lato-Regular.ttf",
+      "postScriptName": "Lato-Regular",
+      "fullName": "Lato Regular",
+      "copyright": "Copyright (c) 2010-2011 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name \"Lato\". Licensed under the SIL Open Font License, Version 1.1."
+    },
+    {
+      "name": "Lato",
+      "style": "italic",
+      "weight": 400,
+      "filename": "Lato-Italic.ttf",
+      "postScriptName": "Lato-Italic",
+      "fullName": "Lato Italic",
+      "copyright": "Copyright (c) 2010-2011 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name \"Lato\". Licensed under the SIL Open Font License, Version 1.1."
+    },
+    {
+      "name": "Lato",
+      "style": "normal",
+      "weight": 700,
+      "filename": "Lato-Bold.ttf",
+      "postScriptName": "Lato-Bold",
+      "fullName": "Lato Bold",
+      "copyright": "Copyright (c) 2010-2011 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name \"Lato\". Licensed under the SIL Open Font License, Version 1.1."
+    },
+    {
+      "name": "Lato",
+      "style": "italic",
+      "weight": 700,
+      "filename": "Lato-BoldItalic.ttf",
+      "postScriptName": "Lato-BoldItalic",
+      "fullName": "Lato Bold Italic",
+      "copyright": "Copyright (c) 2010-2011 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name \"Lato\". Licensed under the SIL Open Font License, Version 1.1."
+    },
+    {
+      "name": "Lato",
+      "style": "normal",
+      "weight": 900,
+      "filename": "Lato-Black.ttf",
+      "postScriptName": "Lato-Black",
+      "fullName": "Lato Black",
+      "copyright": "Copyright (c) 2010-2011 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name \"Lato\". Licensed under the SIL Open Font License, Version 1.1."
+    },
+    {
+      "name": "Lato",
+      "style": "italic",
+      "weight": 900,
+      "filename": "Lato-BlackItalic.ttf",
+      "postScriptName": "Lato-BlackItalic",
+      "fullName": "Lato Black Italic",
+      "copyright": "Copyright (c) 2010-2011 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name \"Lato\". Licensed under the SIL Open Font License, Version 1.1."
+    }
+  ],
+  "subsets": [
+    "latin",
+    "latin-ext",
+    "menu"
+  ],
+  "dateAdded": "2010-12-15"
+}

+ 93 - 0
cryptopad/data/customize/fonts/lato/OFL.txt

@@ -0,0 +1,93 @@
+Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato"
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded, 
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.

BIN
cryptopad/data/customize/fonts/neuropolitical.ttf


+ 133 - 0
cryptopad/data/customize/fonts/open-sans.less

@@ -0,0 +1,133 @@
+/* Open Sans @font-face kit */
+
+@OpenSansPath: "/bower_components/open-sans-fontface/fonts";
+
+/* BEGIN Light */
+@font-face {
+  font-family: 'Open Sans';
+  src: url('@{OpenSansPath}/Light/OpenSans-Light.eot');
+  src: url('@{OpenSansPath}/Light/OpenSans-Light.eot?#iefix') format('embedded-opentype'),
+       url('@{OpenSansPath}/Light/OpenSans-Light.woff') format('woff'),
+       url('@{OpenSansPath}/Light/OpenSans-Light.ttf') format('truetype'),
+       url('@{OpenSansPath}/Light/OpenSans-Light.svg#OpenSansLight') format('svg');
+  font-weight: 300;
+  font-style: normal;
+}
+/* END Light */
+
+/* BEGIN Light Italic */
+@font-face {
+  font-family: 'Open Sans';
+  src: url('@{OpenSansPath}/LightItalic/OpenSans-LightItalic.eot');
+  src: url('@{OpenSansPath}/LightItalic/OpenSans-LightItalic.eot?#iefix') format('embedded-opentype'),
+       url('@{OpenSansPath}/LightItalic/OpenSans-LightItalic.woff') format('woff'),
+       url('@{OpenSansPath}/LightItalic/OpenSans-LightItalic.ttf') format('truetype'),
+       url('@{OpenSansPath}/LightItalic/OpenSans-LightItalic.svg#OpenSansLightItalic') format('svg');
+  font-weight: 300;
+  font-style: italic;
+}
+/* END Light Italic */
+
+/* BEGIN Regular */
+@font-face {
+  font-family: 'Open Sans';
+  src: url('@{OpenSansPath}/Regular/OpenSans-Regular.eot');
+  src: url('@{OpenSansPath}/Regular/OpenSans-Regular.eot?#iefix') format('embedded-opentype'),
+       url('@{OpenSansPath}/Regular/OpenSans-Regular.woff') format('woff'),
+       url('@{OpenSansPath}/Regular/OpenSans-Regular.ttf') format('truetype'),
+       url('@{OpenSansPath}/Regular/OpenSans-Regular.svg#OpenSansRegular') format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
+/* END Regular */
+
+/* BEGIN Italic */
+@font-face {
+  font-family: 'Open Sans';
+  src: url('@{OpenSansPath}/Italic/OpenSans-Italic.eot');
+  src: url('@{OpenSansPath}/Italic/OpenSans-Italic.eot?#iefix') format('embedded-opentype'),
+       url('@{OpenSansPath}/Italic/OpenSans-Italic.woff') format('woff'),
+       url('@{OpenSansPath}/Italic/OpenSans-Italic.ttf') format('truetype'),
+       url('@{OpenSansPath}/Italic/OpenSans-Italic.svg#OpenSansItalic') format('svg');
+  font-weight: normal;
+  font-style: italic;
+}
+/* END Italic */
+
+/* BEGIN Semibold */
+@font-face {
+  font-family: 'Open Sans';
+  src: url('@{OpenSansPath}/Semibold/OpenSans-Semibold.eot');
+  src: url('@{OpenSansPath}/Semibold/OpenSans-Semibold.eot?#iefix') format('embedded-opentype'),
+       url('@{OpenSansPath}/Semibold/OpenSans-Semibold.woff') format('woff'),
+       url('@{OpenSansPath}/Semibold/OpenSans-Semibold.ttf') format('truetype'),
+       url('@{OpenSansPath}/Semibold/OpenSans-Semibold.svg#OpenSansSemibold') format('svg');
+  font-weight: 600;
+  font-style: normal;
+}
+/* END Semibold */
+
+/* BEGIN Semibold Italic */
+@font-face {
+  font-family: 'Open Sans';
+  src: url('@{OpenSansPath}/SemiboldItalic/OpenSans-SemiboldItalic.eot');
+  src: url('@{OpenSansPath}/SemiboldItalic/OpenSans-SemiboldItalic.eot?#iefix') format('embedded-opentype'),
+       url('@{OpenSansPath}/SemiboldItalic/OpenSans-SemiboldItalic.woff') format('woff'),
+       url('@{OpenSansPath}/SemiboldItalic/OpenSans-SemiboldItalic.ttf') format('truetype'),
+       url('@{OpenSansPath}/SemiboldItalic/OpenSans-SemiboldItalic.svg#OpenSansSemiboldItalic') format('svg');
+  font-weight: 600;
+  font-style: italic;
+}
+/* END Semibold Italic */
+
+/* BEGIN Bold */
+@font-face {
+  font-family: 'Open Sans';
+  src: url('@{OpenSansPath}/Bold/OpenSans-Bold.eot');
+  src: url('@{OpenSansPath}/Bold/OpenSans-Bold.eot?#iefix') format('embedded-opentype'),
+       url('@{OpenSansPath}/Bold/OpenSans-Bold.woff') format('woff'),
+       url('@{OpenSansPath}/Bold/OpenSans-Bold.ttf') format('truetype'),
+       url('@{OpenSansPath}/Bold/OpenSans-Bold.svg#OpenSansBold') format('svg');
+  font-weight: bold;
+  font-style: normal;
+}
+/* END Bold */
+
+/* BEGIN Bold Italic */
+@font-face {
+  font-family: 'Open Sans';
+  src: url('@{OpenSansPath}/BoldItalic/OpenSans-BoldItalic.eot');
+  src: url('@{OpenSansPath}/BoldItalic/OpenSans-BoldItalic.eot?#iefix') format('embedded-opentype'),
+       url('@{OpenSansPath}/BoldItalic/OpenSans-BoldItalic.woff') format('woff'),
+       url('@{OpenSansPath}/BoldItalic/OpenSans-BoldItalic.ttf') format('truetype'),
+       url('@{OpenSansPath}/BoldItalic/OpenSans-BoldItalic.svg#OpenSansBoldItalic') format('svg');
+  font-weight: bold;
+  font-style: italic;
+}
+/* END Bold Italic */
+
+/* BEGIN Extrabold */
+@font-face {
+  font-family: 'Open Sans';
+  src: url('@{OpenSansPath}/ExtraBold/OpenSans-ExtraBold.eot');
+  src: url('@{OpenSansPath}/ExtraBold/OpenSans-ExtraBold.eot?#iefix') format('embedded-opentype'),
+       url('@{OpenSansPath}/ExtraBold/OpenSans-ExtraBold.woff') format('woff'),
+       url('@{OpenSansPath}/ExtraBold/OpenSans-ExtraBold.ttf') format('truetype'),
+       url('@{OpenSansPath}/ExtraBold/OpenSans-ExtraBold.svg#OpenSansExtrabold') format('svg');
+  font-weight: 800;
+  font-style: normal;
+}
+/* END Extrabold */
+
+/* BEGIN Extrabold Italic */
+@font-face {
+  font-family: 'Open Sans';
+  src: url('@{OpenSansPath}/ExtraBoldItalic/OpenSans-ExtraBoldItalic.eot');
+  src: url('@{OpenSansPath}/ExtraBoldItalic/OpenSans-ExtraBoldItalic.eot?#iefix') format('embedded-opentype'),
+       url('@{OpenSansPath}/ExtraBoldItalic/OpenSans-ExtraBoldItalic.woff') format('woff'),
+       url('@{OpenSansPath}/ExtraBoldItalic/OpenSans-ExtraBoldItalic.ttf') format('truetype'),
+       url('@{OpenSansPath}/ExtraBoldItalic/OpenSans-ExtraBoldItalic.svg#OpenSansExtraboldItalic') format('svg');
+  font-weight: 800;
+  font-style: italic;
+}
+/* END Extrabold Italic */

+ 87 - 0
cryptopad/data/customize/four-oh-four.js

@@ -0,0 +1,87 @@
+define([
+    'jquery',
+    '/api/config',
+    '/common/hyperscript.js',
+    '/common/outer/local-store.js',
+    '/customize/messages.js',
+
+    'less!/customize/src/less2/pages/page-404.less',
+], function ($, Config, h, LocalStore, Messages) {
+    var urlArgs = Config.requireConf.urlArgs;
+    var img = h('img#cp-logo', {
+        src: '/customize/cryptpad-new-logo-colors-logoonly.png?' + urlArgs
+    });
+
+    var brand = h('h1#cp-brand', 'CryptPad');
+    var message = h('h2#cp-scramble', Messages.four04_pageNotFound);
+    var title = h('h2#cp-title', "404");
+
+    var loggedIn = LocalStore.isLoggedIn();
+    var link = h('a#cp-link', {
+        href: loggedIn? '/drive/': '/',
+    }, loggedIn? Messages.header_logoTitle: Messages.header_homeTitle);
+
+    if (Config.httpUnsafeOrigin && Config.httpUnsafeOrigin !== window.location.origin
+        && window.parent) {
+        $(link).click(function (e) {
+            e.preventDefault();
+            window.parent.location = Config.httpUnsafeOrigin + $(link).attr('href').slice(1);
+        });
+    }
+
+    var content = h('div#cp-main', [
+        img,
+        brand,
+        title,
+        message,
+        link,
+    ]);
+    document.body.appendChild(content);
+
+    var die = function (n) { return Math.floor(Math.random() * n); };
+    var randomChar = function () {
+        return String.fromCharCode(die(94) + 34);
+    };
+    var mutate = function (S, i, c) {
+        var A = S.split("");
+        A[i] = c;
+        return A.join("");
+    };
+
+    var take = function (A) {
+        var n = die(A.length);
+        var choice = A[n];
+        A.splice(n, 1);
+        return choice;
+    };
+
+    var makeDecryptor = function (el, t, difficulty, cb) {
+        var Orig = el.innerText;
+        var options = [];
+        el.innerText = el.innerText.split("").map(function (c, i) {
+            Orig[i] = c;
+            options.push(i);
+            return randomChar();
+        }).join("");
+
+        return function f () {
+            if (die(difficulty) === 0) {
+                var choice = take(options);
+                el.innerText = mutate(el.innerText, choice, Orig.charAt(choice));
+            } else { // make a superficial change
+                el.innerText = mutate(el.innerText,
+                    options[die(options.length)],
+                    randomChar());
+            }
+            setTimeout(options.length > 0? f: cb, t);
+        };
+    };
+
+    makeDecryptor(brand, 70, 2, function () { })();
+    makeDecryptor(title, 50, 14, function () { })();
+    makeDecryptor(link, 20, 4, function () {})();
+    makeDecryptor(message, 12, 3, function () {
+        console.log('done');
+    })();
+});
+

BIN
cryptopad/data/customize/images/AGPL.png


BIN
cryptopad/data/customize/images/AaronMacSween.jpg


BIN
cryptopad/data/customize/images/CalebJames.jpg


BIN
cryptopad/data/customize/images/Catalin.jpg


BIN
cryptopad/data/customize/images/LudovicDuboist.jpg


BIN
cryptopad/data/customize/images/Pierre-new.jpg


BIN
cryptopad/data/customize/images/YannFlory.jpg


BIN
cryptopad/data/customize/images/aaron.jpg


BIN
cryptopad/data/customize/images/atest.jpg


BIN
cryptopad/data/customize/images/avatar.png


BIN
cryptopad/data/customize/images/bkcontact.jpg


BIN
cryptopad/data/customize/images/caleb.jpg


BIN
cryptopad/data/customize/images/code.png


BIN
cryptopad/data/customize/images/cover-faq.jpg


BIN
cryptopad/data/customize/images/cover-features.jpg


BIN
cryptopad/data/customize/images/cover-privacy.jpg


BIN
cryptopad/data/customize/images/drive_screenshot.png


+ 25 - 0
cryptopad/data/customize/images/email.svg

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:url(#SVGID_1_);}
+	.st1{fill:url(#SVGID_2_);}
+</style>
+<g>
+	<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="0.9122" y1="8.1983" x2="23.1969" y2="8.1983">
+		<stop  offset="0" style="stop-color:#4592C4"/>
+		<stop  offset="1" style="stop-color:#545ACD"/>
+	</linearGradient>
+	<path class="st0" d="M1.6,6.1c0.6,0.3,9,4.9,9.3,5c0.3,0.2,0.6,0.2,1.1,0.2c0.5,0,0.8-0.1,1.1-0.2c0.3-0.2,8.7-4.7,9.3-5
+		c0.2-0.1,0.6-0.3,0.7-0.6c0.1-0.4,0-0.6-0.6-0.6H12.1H1.6C1,5,0.8,5.2,1,5.6C1,5.8,1.4,6,1.6,6.1z"/>
+	<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="0.6056" y1="12.6427" x2="23.3944" y2="12.6427">
+		<stop  offset="0" style="stop-color:#4592C4"/>
+		<stop  offset="1" style="stop-color:#545ACD"/>
+	</linearGradient>
+	<path class="st1" d="M22.9,6.4c-0.4,0.2-4.5,3.1-7.1,4.8l4.5,5.1c0.1,0.1,0.2,0.2,0.1,0.3c-0.1,0.1-0.2,0-0.3-0.1l-5.4-4.6
+		c-0.8,0.5-1.4,0.9-1.5,0.9c-0.4,0.2-0.7,0.2-1.1,0.2c-0.4,0-0.7,0-1.1-0.2c-0.1-0.1-0.7-0.4-1.5-0.9L4,16.5
+		c-0.1,0.1-0.3,0.1-0.3,0.1c-0.1-0.1,0-0.2,0.1-0.3l4.5-5.1C5.6,9.5,1.6,6.6,1.1,6.4c-0.5-0.2-0.5,0-0.5,0.3c0,0.2,0,11.2,0,11.2
+		c0,0.5,0.8,1.1,1.3,1.1h10.2h10.2c0.5,0,1.2-0.6,1.2-1.1c0,0,0-11,0-11.2C23.4,6.4,23.4,6.1,22.9,6.4z"/>
+</g>
+</svg>

+ 22 - 0
cryptopad/data/customize/images/github.svg

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:url(#Shape_1_);}
+</style>
+<g id="Octicons">
+	<g id="mark-github">
+		<linearGradient id="Shape_1_" gradientUnits="userSpaceOnUse" x1="1.0181" y1="12" x2="22.9819" y2="12">
+			<stop  offset="0" style="stop-color:#4592C4"/>
+			<stop  offset="1" style="stop-color:#545ACD"/>
+		</linearGradient>
+		<path id="Shape" class="st0" d="M12,1.3c-6.1,0-11,4.9-11,11c0,4.9,3.1,9,7.5,10.4c0.5,0.1,0.8-0.2,0.8-0.5c0-0.3,0-1.1,0-2
+			c-2.8,0.5-3.5-0.7-3.7-1.3c-0.1-0.3-0.7-1.3-1.1-1.6c-0.4-0.2-0.9-0.7,0-0.7c0.9,0,1.5,0.8,1.7,1.1c1,1.7,2.6,1.2,3.2,0.9
+			c0.1-0.7,0.4-1.2,0.7-1.5c-2.4-0.3-5-1.2-5-5.4c0-1.2,0.4-2.2,1.1-3C6,8.5,5.7,7.3,6.3,5.8c0,0,0.9-0.3,3,1.1
+			c0.9-0.2,1.8-0.4,2.7-0.4c0.9,0,1.9,0.1,2.7,0.4c2.1-1.4,3-1.1,3-1.1c0.6,1.5,0.2,2.6,0.1,2.9c0.7,0.8,1.1,1.7,1.1,3
+			c0,4.2-2.6,5.1-5,5.4c0.4,0.3,0.7,1,0.7,2c0,1.5,0,2.6,0,3c0,0.3,0.2,0.6,0.8,0.5c4.3-1.5,7.5-5.6,7.5-10.4
+			C23,6.2,18.1,1.3,12,1.3L12,1.3z"/>
+	</g>
+</g>
+</svg>

BIN
cryptopad/data/customize/images/hash.png


+ 1 - 0
cryptopad/data/customize/images/icons/folder.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 13"><defs><style>.cls-1{fill:#444;}</style></defs><title>Asset 2</title><g id="Layer_2" data-name="Layer 2"><g id="_490_Icons" data-name="490 Icons"><path class="cls-1" d="M7,0,9,2h7V13H0V0Z"/></g></g></svg>

+ 1 - 0
cryptopad/data/customize/images/icons/folderOpen.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.44 13"><defs><style>.cls-1{fill:#444;}</style></defs><title>Asset 1</title><g id="Layer_2" data-name="Layer 2"><g id="_490_Icons" data-name="490 Icons"><path class="cls-1" d="M13,13l2.44-6.5h-13L0,13ZM2,4,0,13V0H4.5l2,2H13V4Z"/></g></g></svg>

+ 4 - 0
cryptopad/data/customize/images/icons/ic_folder_black_24px.svg

@@ -0,0 +1,4 @@
+<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
+    <path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/>
+    <path d="M0 0h24v24H0z" fill="none"/>
+</svg>

+ 4 - 0
cryptopad/data/customize/images/icons/ic_folder_open_black_24px.svg

@@ -0,0 +1,4 @@
+<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
+    <path d="M0 0h24v24H0z" fill="none"/>
+    <path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z"/>
+</svg>

+ 30 - 0
cryptopad/data/customize/images/irc.svg

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:url(#SVGID_1_);}
+	.st1{fill:url(#SVGID_2_);}
+</style>
+<g>
+	<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="4.7893" y1="12.0669" x2="6.7712" y2="12.0669">
+		<stop  offset="0" style="stop-color:#4592C4"/>
+		<stop  offset="1" style="stop-color:#545ACD"/>
+	</linearGradient>
+	<polygon class="st0" points="4.8,13 6.4,13 6.8,11.2 5.1,11.2 	"/>
+	<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="0.4529" y1="12" x2="23.5471" y2="12">
+		<stop  offset="0" style="stop-color:#4592C4"/>
+		<stop  offset="1" style="stop-color:#545ACD"/>
+	</linearGradient>
+	<path class="st1" d="M21.6,17.8c0-0.1,0.1-0.1,0.1-0.2c1.2-1.7,1.8-3.8,1.8-6c0-6.2-5.2-11.2-11.5-11.2S0.5,5.4,0.5,11.6
+		c0,6.2,5.2,11.1,11.6,11.1c1.6,0,3.1-0.3,4.5-0.9c0,0,0.1,0,0.1,0c0,0,0,0,0,0c0.2-0.1,0.4-0.1,0.6-0.1c0.2,0,0.5,0,0.7,0.1
+		l4.7,1.7l-1.2-4.9C21.4,18.3,21.4,18.1,21.6,17.8z M9.1,11.2H7.5L7.2,13h1.5v0.7H7L6.6,16H5.8l0.5-2.3H4.7L4.2,16H3.5l0.4-2.3H2.5
+		V13h1.5l0.4-1.8H2.9v-0.7h1.6L5,8.1h0.7l-0.4,2.3h1.6l0.5-2.3h0.7l-0.5,2.3h1.4V11.2z M11.2,16h-0.9v-5.9h0.9V16z M11.1,8.9
+		C11,9,10.9,9.1,10.7,9.1c-0.2,0-0.3,0-0.4-0.1c-0.1-0.1-0.2-0.2-0.2-0.4c0-0.2,0.1-0.4,0.2-0.4c0.1-0.1,0.2-0.1,0.4-0.1
+		c0.1,0,0.3,0,0.4,0.1s0.2,0.2,0.2,0.4S11.2,8.9,11.1,8.9z M16.3,10.9c-0.2-0.1-0.5-0.1-0.6-0.1c-0.5,0-0.9,0.2-1.2,0.6
+		c-0.3,0.4-0.5,0.9-0.5,1.4V16h-0.9v-5.9h0.7l0.1,1.1h0c0.2-0.4,0.5-0.7,0.8-0.9c0.3-0.2,0.6-0.3,1-0.3c0.3,0,0.5,0,0.7,0.1
+		L16.3,10.9z M17.9,15.3c-0.5-0.5-0.7-1.3-0.7-2.2c0-1,0.2-1.7,0.7-2.3s1.2-0.8,2-0.8c0.3,0,0.6,0,0.8,0.1c0.3,0.1,0.5,0.1,0.7,0.2
+		l-0.3,0.8c-0.2-0.1-0.4-0.1-0.6-0.2c-0.2-0.1-0.4-0.1-0.6-0.1c-1.2,0-1.8,0.8-1.8,2.3c0,0.7,0.1,1.3,0.4,1.7s0.7,0.6,1.3,0.6
+		c0.5,0,1-0.1,1.5-0.3v0.8c-0.4,0.2-0.9,0.3-1.5,0.3C19.1,16.1,18.4,15.8,17.9,15.3z"/>
+</g>
+</svg>

+ 19 - 0
cryptopad/data/customize/images/issue.svg

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:url(#Shape_2_);}
+</style>
+<g id="Octicons_1_">
+	<g id="issue-opened">
+		<linearGradient id="Shape_2_" gradientUnits="userSpaceOnUse" x1="1.1619" y1="12" x2="22.8381" y2="12">
+			<stop  offset="0" style="stop-color:#4592C4"/>
+			<stop  offset="1" style="stop-color:#545ACD"/>
+		</linearGradient>
+		<path id="Shape_1_" class="st0" d="M12,3.2c4.9,0,8.8,4,8.8,8.8s-4,8.8-8.8,8.8s-8.8-4-8.8-8.8S7.1,3.2,12,3.2L12,3.2z M12,1.2
+			C6,1.2,1.2,6,1.2,12S6,22.8,12,22.8S22.8,18,22.8,12S18,1.2,12,1.2L12,1.2z M13.5,5.8h-3.1v7.7h3.1V5.8L13.5,5.8z M13.5,15.1h-3.1
+			v3.1h3.1V15.1L13.5,15.1z"/>
+	</g>
+</g>
+</svg>

BIN
cryptopad/data/customize/images/key_small.png


BIN
cryptopad/data/customize/images/logo_white.png


File diff suppressed because it is too large
+ 0 - 0
cryptopad/data/customize/images/logo_white.svg


BIN
cryptopad/data/customize/images/ludovic.jpg


BIN
cryptopad/data/customize/images/ngi.png


BIN
cryptopad/data/customize/images/organize.png


BIN
cryptopad/data/customize/images/pad.png


BIN
cryptopad/data/customize/images/pad_screenshot.png


BIN
cryptopad/data/customize/images/pierre.jpg


BIN
cryptopad/data/customize/images/poll.png


BIN
cryptopad/data/customize/images/realtime_small.png


+ 17 - 0
cryptopad/data/customize/images/sayhi.svg

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:url(#SVGID_1_);}
+</style>
+<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="0.4529" y1="12" x2="23.5471" y2="12">
+	<stop  offset="0" style="stop-color:#4592C4"/>
+	<stop  offset="1" style="stop-color:#545ACD"/>
+</linearGradient>
+<path class="st0" d="M12,22.7c1.6,0,3.1-0.3,4.5-0.9c0,0,0.1,0,0.1,0c0,0,0,0,0,0c0.2-0.1,0.4-0.1,0.6-0.1c0.2,0,0.5,0,0.7,0.1
+	l4.7,1.7l-1.2-4.9c0-0.3,0.1-0.6,0.2-0.8l0,0c0-0.1,0.1-0.1,0.1-0.2c1.2-1.7,1.8-3.8,1.8-6c0-6.2-5.2-11.2-11.5-11.2
+	S0.5,5.4,0.5,11.6C0.5,17.8,5.6,22.7,12,22.7z M17.3,10.2c1,0,1.8,0.8,1.8,1.8s-0.8,1.8-1.8,1.8c-1,0-1.8-0.8-1.8-1.8
+	S16.4,10.2,17.3,10.2z M12,10.2c1,0,1.8,0.8,1.8,1.8S13,13.8,12,13.8S10.2,13,10.2,12S11,10.2,12,10.2z M6.7,10.2
+	c1,0,1.8,0.8,1.8,1.8s-0.8,1.8-1.8,1.8S4.9,13,4.9,12S5.7,10.2,6.7,10.2z"/>
+</svg>

BIN
cryptopad/data/customize/images/slide.png


+ 17 - 0
cryptopad/data/customize/images/twitter.svg

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:url(#SVGID_1_);}
+</style>
+<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="0.3991" y1="12" x2="23.6009" y2="12">
+	<stop  offset="0" style="stop-color:#4592C4"/>
+	<stop  offset="1" style="stop-color:#545ACD"/>
+</linearGradient>
+<path class="st0" d="M23.6,4.8c-0.9,0.4-1.8,0.6-2.7,0.8C21.9,5,22.6,4,23,2.9c-0.9,0.5-1.9,0.9-3,1.2c-0.9-0.9-2.1-1.5-3.5-1.5
+	c-2.6,0-4.8,2.1-4.8,4.8c0,0.4,0,0.7,0.1,1.1c-4-0.2-7.5-2.1-9.8-5C1.6,4.1,1.4,5,1.4,5.8c0,1.7,0.8,3.1,2.1,4
+	C2.7,9.8,2,9.6,1.3,9.2c0,0,0,0,0,0.1c0,2.3,1.6,4.2,3.8,4.7c-0.4,0.1-0.8,0.2-1.3,0.2c-0.3,0-0.6,0-0.9-0.1
+	c0.6,1.9,2.4,3.3,4.4,3.3c-1.6,1.3-3.7,2-5.9,2c-0.4,0-0.8,0-1.1-0.1c2.1,1.4,4.6,2.2,7.3,2.2c8.8,0,13.6-7.3,13.6-13.6
+	c0-0.2,0-0.4,0-0.6C22.2,6.6,23,5.8,23.6,4.8z"/>
+</svg>

BIN
cryptopad/data/customize/images/useraccount.png


BIN
cryptopad/data/customize/images/yann.jpg


BIN
cryptopad/data/customize/images/zeroknowledge_small.png


BIN
cryptopad/data/customize/images/zk.png


+ 16 - 0
cryptopad/data/customize/index.html

@@ -0,0 +1,16 @@
+<!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>

BIN
cryptopad/data/customize/loading-logo.png


+ 200 - 0
cryptopad/data/customize/loading.js

@@ -0,0 +1,200 @@
+// dark #326599
+// light #4591c4
+define([], function () {
+    var loadingStyle = (function(){/*
+#cp-loading {
+  transition: opacity 0.75s, visibility 0s 0.75s;
+  visibility: visible;
+  position: fixed;
+  z-index: 10000000;
+  top: 0px;
+  bottom: 0px;
+  left: 0px;
+  right: 0px;
+  background: linear-gradient(to right, #326599 0%, #326599 50%, #4591c4 50%, #4591c4 100%);
+  color: #fafafa;
+  font-size: 1.5em;
+  opacity: 1;
+  display: flex;
+  flex-flow: column;
+  justify-content: center;
+  align-items: center;
+}
+#cp-loading.cp-loading-hidden {
+  opacity: 0;
+  visibility: hidden;
+}
+#cp-loading .cp-loading-logo {
+    height: 300px;
+    width: 300px;
+    margin-top: 50px;
+    flex: 0 1 auto;
+    min-height: 0;
+    text-align: center;
+}
+#cp-loading .cp-loading-logo img {
+    max-width: 100%;
+    max-height: 100%;
+}
+#cp-loading .cp-loading-container {
+    width: 700px;
+    max-width: 90vw;
+    height: 500px;
+    max-height: calc(100vh - 20px);
+    margin: 50px;
+    flex-shrink: 0;
+    display: flex;
+    flex-flow: column;
+    justify-content: space-around;
+    align-items: center;
+}
+@media screen and (max-height: 800px) {
+    #cp-loading .cp-loading-container {
+        height: auto;
+    }
+}
+@media screen and (max-width: 600px) {
+    #cp-loading .cp-loading-container {
+        height: auto;
+    }
+}
+#cp-loading .cp-loading-cryptofist {
+  margin-left: auto;
+  margin-right: auto;
+  //height: 300px;
+  max-width: 90vw;
+  max-height: 300px;
+  width: auto;
+  height: auto;
+  margin-bottom: 2em;
+}
+@media screen and (max-height: 500px) {
+  #cp-loading .cp-loading-logo {
+      display: none;
+  }
+}
+#cp-loading-message {
+    background: #FFF;
+    padding: 20px;
+    width: 100%;
+    color: #000;
+    text-align: center;
+    display: none;
+}
+#cp-loading-password-prompt {
+    font-size: 18px;
+}
+#cp-loading-password-prompt .cp-password-error {
+    color: white;
+    background: #9e0000;
+    padding: 5px;
+    margin-bottom: 15px;
+}
+#cp-loading-password-prompt .cp-password-info {
+    text-align: left;
+    margin-bottom: 15px;
+}
+#cp-loading-password-prompt .cp-password-form {
+    display: flex;
+    justify-content: space-around;
+    flex-wrap: wrap;
+}
+#cp-loading-password-prompt .cp-password-form button,
+#cp-loading-password-prompt .cp-password-form .cp-password-input {
+    background-color: #4591c4;
+    color: white;
+    border: 1px solid #4591c4;
+}
+#cp-loading-password-prompt .cp-password-form .cp-password-container {
+    flex-shrink: 1;
+    min-width: 0;
+}
+#cp-loading-password-prompt .cp-password-form input {
+    flex: 1;
+    padding: 0 5px;
+    min-width: 0;
+    text-overflow: ellipsis;
+}
+#cp-loading-password-prompt .cp-password-form button:hover {
+    background-color: #326599;
+}
+#cp-loading .cp-loading-spinner-container {
+  position: relative;
+  height: 100px;
+}
+#cp-loading .cp-loading-spinner-container > div {
+  height: 100px;
+}
+#cp-loading-tip {
+  position: fixed;
+  z-index: 10000000;
+  top: 80%;
+  left: 0;
+  right: 0;
+  text-align: center;
+  transition: opacity 750ms;
+  transition-delay: 3000ms;
+}
+@media screen and (max-height: 600px) {
+  #cp-loading-tip {
+    display: none;
+  }
+}
+#cp-loading-tip span {
+  background: #222;
+  color: #fafafa;
+  text-align: center;
+  font-size: 1.5em;
+  opacity: 0.7;
+  font-family: 'Open Sans', 'Helvetica Neue', sans-serif;
+  padding: 15px;
+  max-width: 60%;
+  display: inline-block;
+}
+.cp-loading-progress {
+    width: 100%;
+    margin: 20px;
+}
+.cp-loading-progress p {
+    margin: 5px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+}
+.cp-loading-progress-bar {
+    height: 24px;
+    background: white;
+}
+.cp-loading-progress-bar-value {
+    height: 100%;
+    background: #5cb85c;
+}
+*/}).toString().slice(14, -3);
+    var urlArgs = window.location.href.replace(/^.*\?([^\?]*)$/, function (all, x) { return x; });
+    var elem = document.createElement('div');
+    elem.setAttribute('id', 'cp-loading');
+    elem.innerHTML = [
+        '<style>',
+        loadingStyle,
+        '</style>',
+        '<div class="cp-loading-logo">',
+            '<img class="cp-loading-cryptofist" src="/customize/loading-logo.png?' + urlArgs + '">',
+        '</div>',
+        '<div class="cp-loading-container">',
+            '<div class="cp-loading-spinner-container">',
+                '<span class="fa fa-spinner fa-pulse fa-4x fa-fw"></span>',
+            '</div>',
+            '<p id="cp-loading-message"></p>',
+        '</div>'
+    ].join('');
+    return function () {
+        var intr;
+        var append = function () {
+            if (!document.body) { return; }
+            clearInterval(intr);
+            document.body.appendChild(elem);
+        };
+        intr = setInterval(append, 100);
+        append();
+    };
+});

+ 544 - 0
cryptopad/data/customize/login.js

@@ -0,0 +1,544 @@
+define([
+    'jquery',
+    '/bower_components/chainpad-listmap/chainpad-listmap.js',
+    '/bower_components/chainpad-crypto/crypto.js',
+    '/common/common-util.js',
+    '/common/outer/network-config.js',
+    '/customize/credential.js',
+    '/bower_components/chainpad/chainpad.dist.js',
+    '/common/common-realtime.js',
+    '/common/common-constants.js',
+    '/common/common-interface.js',
+    '/common/common-feedback.js',
+    '/common/outer/local-store.js',
+    '/customize/messages.js',
+    '/bower_components/nthen/index.js',
+    '/common/outer/login-block.js',
+    '/common/common-hash.js',
+
+    '/bower_components/tweetnacl/nacl-fast.min.js',
+    '/bower_components/scrypt-async/scrypt-async.min.js', // better load speed
+], function ($, Listmap, Crypto, Util, NetConfig, Cred, ChainPad, Realtime, Constants, UI,
+            Feedback, LocalStore, Messages, nThen, Block, Hash) {
+    var Exports = {
+        Cred: Cred,
+        // this is depended on by non-customizable files
+        // be careful when modifying login.js
+        requiredBytes: 192,
+    };
+
+    var Nacl = window.nacl;
+    var allocateBytes = Exports.allocateBytes = function (bytes) {
+        var dispense = Cred.dispenser(bytes);
+
+        var opt = {};
+
+        // dispense 18 bytes of entropy for your encryption key
+        var encryptionSeed = dispense(18);
+        // 16 bytes for a deterministic channel key
+        var channelSeed = dispense(16);
+        // 32 bytes for a curve key
+        var curveSeed = dispense(32);
+
+        var curvePair = Nacl.box.keyPair.fromSecretKey(new Uint8Array(curveSeed));
+        opt.curvePrivate = Nacl.util.encodeBase64(curvePair.secretKey);
+        opt.curvePublic = Nacl.util.encodeBase64(curvePair.publicKey);
+
+        // 32 more for a signing key
+        var edSeed = opt.edSeed = dispense(32);
+
+        // 64 more bytes to seed an additional signing key
+        var blockKeys = opt.blockKeys = Block.genkeys(new Uint8Array(dispense(64)));
+        opt.blockHash = Block.getBlockHash(blockKeys);
+
+        // derive a private key from the ed seed
+        var signingKeypair = Nacl.sign.keyPair.fromSeed(new Uint8Array(edSeed));
+
+        opt.edPrivate = Nacl.util.encodeBase64(signingKeypair.secretKey);
+        opt.edPublic = Nacl.util.encodeBase64(signingKeypair.publicKey);
+
+        var keys = opt.keys = Crypto.createEditCryptor(null, encryptionSeed);
+
+        // 24 bytes of base64
+        keys.editKeyStr = keys.editKeyStr.replace(/\//g, '-');
+
+        // 32 bytes of hex
+        var channelHex = opt.channelHex = Util.uint8ArrayToHex(channelSeed);
+
+        // should never happen
+        if (channelHex.length !== 32) { throw new Error('invalid channel id'); }
+
+        var channel64 = Util.hexToBase64(channelHex);
+
+        // we still generate a v1 hash because this function needs to deterministically
+        // derive the same values as it always has. New accounts will generate their own
+        // userHash values
+        opt.userHash = '/1/edit/' + [channel64, opt.keys.editKeyStr].join('/') + '/';
+
+        return opt;
+    };
+
+
+    var loginOptionsFromBlock = function (blockInfo) {
+        var opt = {};
+        var parsed = Hash.getSecrets('pad', blockInfo.User_hash);
+        opt.channelHex = parsed.channel;
+        opt.keys = parsed.keys;
+        opt.edPublic = blockInfo.edPublic;
+        opt.User_name = blockInfo.User_name;
+        return opt;
+    };
+
+    var loadUserObject = function (opt, cb) {
+        var config = {
+            websocketURL: NetConfig.getWebsocketURL(),
+            channel: opt.channelHex,
+            data: {},
+            validateKey: opt.keys.validateKey, // derived validation key
+            crypto: Crypto.createEncryptor(opt.keys),
+            logLevel: 1,
+            classic: true,
+            ChainPad: ChainPad,
+            owners: [opt.edPublic]
+        };
+
+        var rt = opt.rt = Listmap.create(config);
+        rt.proxy
+        .on('ready', function () {
+            setTimeout(function () { cb(void 0, rt); });
+        })
+        .on('disconnect', function (info) {
+            cb('E_DISCONNECT', info);
+        });
+    };
+
+    var isProxyEmpty = function (proxy) {
+        var l = Object.keys(proxy).length;
+        return l === 0 || (l === 2 && proxy._events && proxy.on);
+    };
+
+    var setMergeAnonDrive = function () {
+        sessionStorage.migrateAnonDrive = 1;
+    };
+
+    var setCreateReadme = function () {
+        sessionStorage.createReadme = 1;
+    };
+
+    Exports.loginOrRegister = function (uname, passwd, isRegister, shouldImport, cb) {
+        if (typeof(cb) !== 'function') { return; }
+
+        // Usernames are all lowercase. No going back on this one
+        uname = uname.toLowerCase();
+
+        // validate inputs
+        if (!Cred.isValidUsername(uname)) { return void cb('INVAL_USER'); }
+        if (!Cred.isValidPassword(passwd)) { return void cb('INVAL_PASS'); }
+        if (isRegister && !Cred.isLongEnoughPassword(passwd)) {
+            return void cb('PASS_TOO_SHORT');
+        }
+
+        // results...
+        var res = {
+            register: isRegister,
+        };
+
+        var RT, blockKeys, blockHash, Pinpad, rpc, userHash;
+
+        nThen(function (waitFor) {
+            // derive a predefined number of bytes from the user's inputs,
+            // and allocate them in a deterministic fashion
+            Cred.deriveFromPassphrase(uname, passwd, Exports.requiredBytes, waitFor(function (bytes) {
+                res.opt = allocateBytes(bytes);
+                blockHash = res.opt.blockHash;
+                blockKeys = res.opt.blockKeys;
+            }));
+        }).nThen(function (waitFor) {
+            // the allocated bytes can be used either in a legacy fashion,
+            // or in such a way that a previously unused byte range determines
+            // the location of a layer of indirection which points users to
+            // an encrypted block, from which they can recover the location of
+            // the rest of their data
+
+            // determine where a block for your set of keys would be stored
+            var blockUrl = Block.getBlockUrl(res.opt.blockKeys);
+
+            // Check whether there is a block at that location
+            Util.fetch(blockUrl, waitFor(function (err, block) {
+                // if users try to log in or register, we must check
+                // whether there is a block.
+
+                // the block is only useful if it can be decrypted, though
+                if (err) {
+                    console.log("no block found");
+                    return;
+                }
+
+                var decryptedBlock = Block.decrypt(block, blockKeys);
+                if (!decryptedBlock) {
+                    console.error("Found a login block but failed to decrypt");
+                    return;
+                }
+
+                console.error(decryptedBlock);
+                res.blockInfo = decryptedBlock;
+            }));
+        }).nThen(function (waitFor) {
+            // we assume that if there is a block, it was created in a valid manner
+            // so, just proceed to the next block which handles that stuff
+            if (res.blockInfo) { return; }
+
+            var opt = res.opt;
+
+            // load the user's object using the legacy credentials
+            loadUserObject(opt, waitFor(function (err, rt) {
+                if (err) {
+                    waitFor.abort();
+                    return void cb(err);
+                }
+
+                // if a proxy is marked as deprecated, it is because someone had a non-owned drive
+                // but changed their password, and couldn't delete their old data.
+                // if they are here, they have entered their old credentials, so we should not
+                // allow them to proceed. In time, their old drive should get deleted, since
+                // it will should be pinned by anyone's drive.
+                if (rt.proxy[Constants.deprecatedKey]) {
+                    waitFor.abort();
+                    return void cb('NO_SUCH_USER', res);
+                }
+
+                if (isRegister && isProxyEmpty(rt.proxy)) {
+                    // If they are trying to register,
+                    // and the proxy is empty, then there is no 'legacy user' either
+                    // so we should just shut down this session and disconnect.
+                    rt.network.disconnect();
+                    return; // proceed to the next async block
+                }
+
+                // they tried to just log in but there's no such user
+                // and since we're here at all there is no modern-block
+                if (!isRegister && isProxyEmpty(rt.proxy)) {
+                    rt.network.disconnect(); // clean up after yourself
+                    waitFor.abort();
+                    return void cb('NO_SUCH_USER', res);
+                }
+
+                // they tried to register, but those exact credentials exist
+                if (isRegister && !isProxyEmpty(rt.proxy)) {
+                    rt.network.disconnect();
+                    waitFor.abort();
+                    Feedback.send('LOGIN', true);
+                    return void cb('ALREADY_REGISTERED', res);
+                }
+
+                // if you are here, then there is no block, the user is trying
+                // to log in. The proxy is **not** empty. All values assigned here
+                // should have been deterministically created using their credentials
+                // so setting them is just a precaution to keep things in good shape
+                res.proxy = rt.proxy;
+                res.realtime = rt.realtime;
+
+                // they're registering...
+                res.userHash = opt.userHash;
+                res.userName = uname;
+
+                // export their signing key
+                res.edPrivate = opt.edPrivate;
+                res.edPublic = opt.edPublic;
+
+                // export their encryption key
+                res.curvePrivate = opt.curvePrivate;
+                res.curvePublic = opt.curvePublic;
+
+                if (shouldImport) { setMergeAnonDrive(); }
+
+                // don't proceed past this async block.
+                waitFor.abort();
+
+                // We have to call whenRealtimeSyncs asynchronously here because in the current
+                // version of listmap, onLocal calls `chainpad.contentUpdate(newValue)`
+                // asynchronously.
+                // The following setTimeout is here to make sure whenRealtimeSyncs is called after
+                // `contentUpdate` so that we have an update userDoc in chainpad.
+                setTimeout(function () {
+                    Realtime.whenRealtimeSyncs(rt.realtime, function () {
+                        // the following stages are there to initialize a new drive
+                        // if you are registering
+                        LocalStore.login(res.userHash, res.userName, function () {
+                            setTimeout(function () { cb(void 0, res); });
+                        });
+                    });
+                });
+            }));
+        }).nThen(function (waitFor) { // MODERN REGISTRATION / LOGIN
+            var opt;
+            if (res.blockInfo) {
+                opt = loginOptionsFromBlock(res.blockInfo);
+                userHash = res.blockInfo.User_hash;
+                console.error(opt, userHash);
+            } else {
+                console.log("allocating random bytes for a new user object");
+                opt = allocateBytes(Nacl.randomBytes(Exports.requiredBytes));
+                // create a random v2 hash, since we don't need backwards compatibility
+                userHash = opt.userHash = Hash.createRandomHash('drive');
+                var secret = Hash.getSecrets('drive', userHash);
+                opt.keys = secret.keys;
+                opt.channelHex = secret.channel;
+            }
+
+            // according to the location derived from the credentials which you entered
+            loadUserObject(opt, waitFor(function (err, rt) {
+                if (err) {
+                    waitFor.abort();
+                    return void cb('MODERN_REGISTRATION_INIT');
+                }
+
+                console.error(JSON.stringify(rt.proxy));
+
+                // export the realtime object you checked
+                RT = rt;
+
+                var proxy = rt.proxy;
+                if (isRegister && !isProxyEmpty(proxy) && (!proxy.edPublic || !proxy.edPrivate)) {
+                    console.error("INVALID KEYS");
+                    console.log(JSON.stringify(proxy));
+                    return;
+                }
+
+                res.proxy = rt.proxy;
+                res.realtime = rt.realtime;
+
+                // they're registering...
+                res.userHash = userHash;
+                res.userName = uname;
+
+                // somehow they have a block present, but nothing in the user object it specifies
+                // this shouldn't happen, but let's send feedback if it does
+                if (!isRegister && isProxyEmpty(rt.proxy)) {
+                    // this really shouldn't happen, but let's handle it anyway
+                    Feedback.send('EMPTY_LOGIN_WITH_BLOCK');
+
+                    rt.network.disconnect(); // clean up after yourself
+                    waitFor.abort();
+                    return void cb('NO_SUCH_USER', res);
+                }
+
+                // they tried to register, but those exact credentials exist
+                if (isRegister && !isProxyEmpty(rt.proxy)) {
+                    rt.network.disconnect();
+                    waitFor.abort();
+                    res.blockHash = blockHash;
+                    if (shouldImport) {
+                        setMergeAnonDrive();
+                    }
+
+                    return void cb('ALREADY_REGISTERED', res);
+                }
+
+                if (!isRegister && !isProxyEmpty(rt.proxy)) {
+                    LocalStore.setBlockHash(blockHash);
+                    waitFor.abort();
+                    if (shouldImport) {
+                        setMergeAnonDrive();
+                    }
+                    return void LocalStore.login(userHash, uname, function () {
+                        cb(void 0, res);
+                    });
+                }
+
+                if (isRegister && isProxyEmpty(rt.proxy)) {
+                    proxy.edPublic = opt.edPublic;
+                    proxy.edPrivate = opt.edPrivate;
+                    proxy.curvePublic = opt.curvePublic;
+                    proxy.curvePrivate = opt.curvePrivate;
+                    proxy.login_name = uname;
+                    proxy[Constants.displayNameKey] = uname;
+                    setCreateReadme();
+                    if (shouldImport) {
+                        setMergeAnonDrive();
+                    } else {
+                        proxy.version = 6;
+                    }
+
+                    Feedback.send('REGISTRATION', true);
+                } else {
+                    Feedback.send('LOGIN', true);
+                }
+
+                setTimeout(waitFor(function () {
+                    Realtime.whenRealtimeSyncs(rt.realtime, waitFor());
+                }));
+            }));
+        }).nThen(function (waitFor) {
+            require(['/common/pinpad.js'], waitFor(function (_Pinpad) {
+                console.log("loaded rpc module");
+                Pinpad = _Pinpad;
+            }));
+        }).nThen(function (waitFor) {
+            // send an RPC to store the block which you created.
+            console.log("initializing rpc interface");
+
+            Pinpad.create(RT.network, RT.proxy, waitFor(function (e, _rpc) {
+                if (e) {
+                    waitFor.abort();
+                    console.error(e); // INVALID_KEYS
+                    return void cb('RPC_CREATION_ERROR');
+                }
+                rpc = _rpc;
+                console.log("rpc initialized");
+            }));
+        }).nThen(function (waitFor) {
+            console.log("creating request to publish a login block");
+
+            // Finally, create the login block for the object you just created.
+            var toPublish = {};
+
+            toPublish[Constants.userNameKey] = uname;
+            toPublish[Constants.userHashKey] = userHash;
+            toPublish.edPublic = RT.proxy.edPublic;
+
+            var blockRequest = Block.serialize(JSON.stringify(toPublish), res.opt.blockKeys);
+
+            rpc.writeLoginBlock(blockRequest, waitFor(function (e) {
+                if (e) { return void console.error(e); }
+
+                console.log("blockInfo available at:", blockHash);
+                LocalStore.setBlockHash(blockHash);
+                LocalStore.login(userHash, uname, function () {
+                    cb(void 0, res);
+                });
+            }));
+        });
+    };
+    Exports.redirect = function () {
+        if (sessionStorage.redirectTo) {
+            var h = sessionStorage.redirectTo;
+            var parser = document.createElement('a');
+            parser.href = h;
+            if (parser.origin === window.location.origin) {
+                delete sessionStorage.redirectTo;
+                window.location.href = h;
+                return;
+            }
+        }
+        window.location.href = '/drive/';
+    };
+
+    var hashing;
+    Exports.loginOrRegisterUI = function (uname, passwd, isRegister, shouldImport, testing, test) {
+        if (hashing) { return void console.log("hashing is already in progress"); }
+        hashing = true;
+
+        var proceed = function (result) {
+            hashing = false;
+            if (test && typeof test === "function" && test()) { return; }
+            Realtime.whenRealtimeSyncs(result.realtime, function () {
+                Exports.redirect();
+            });
+        };
+
+        // setTimeout 100ms to remove the keyboard on mobile devices before the loading screen
+        // pops up
+        window.setTimeout(function () {
+            UI.addLoadingScreen({
+                loadingText: Messages.login_hashing,
+                hideTips: true,
+            });
+
+            // We need a setTimeout(cb, 0) otherwise the loading screen is only displayed
+            // after hashing the password
+            window.setTimeout(function () {
+                Exports.loginOrRegister(uname, passwd, isRegister, shouldImport, function (err, result) {
+                    var proxy;
+                    if (result) { proxy = result.proxy; }
+
+                    if (err) {
+                        switch (err) {
+                            case 'NO_SUCH_USER':
+                                UI.removeLoadingScreen(function () {
+                                    UI.alert(Messages.login_noSuchUser, function () {
+                                        hashing = false;
+                                    });
+                                });
+                                break;
+                            case 'INVAL_USER':
+                                UI.removeLoadingScreen(function () {
+                                    UI.alert(Messages.login_invalUser, function () {
+                                        hashing = false;
+                                    });
+                                });
+                                break;
+                            case 'INVAL_PASS':
+                                UI.removeLoadingScreen(function () {
+                                    UI.alert(Messages.login_invalPass, function () {
+                                        hashing = false;
+                                    });
+                                });
+                                break;
+                            case 'PASS_TOO_SHORT':
+                                UI.removeLoadingScreen(function () {
+                                    var warning = Messages._getKey('register_passwordTooShort', [
+                                        Cred.MINIMUM_PASSWORD_LENGTH
+                                    ]);
+                                    UI.alert(warning, function () {
+                                        hashing = false;
+                                    });
+                                });
+                                break;
+                            case 'ALREADY_REGISTERED':
+                                UI.removeLoadingScreen(function () {
+                                    UI.confirm(Messages.register_alreadyRegistered, function (yes) {
+                                        if (!yes) {
+                                            hashing = false;
+                                            return;
+                                        }
+                                        proxy.login_name = uname;
+
+                                        if (!proxy[Constants.displayNameKey]) {
+                                            proxy[Constants.displayNameKey] = uname;
+                                        }
+                                        LocalStore.eraseTempSessionValues();
+
+
+                                        if (result.blockHash) {
+                                            LocalStore.setBlockHash(result.blockHash);
+                                        }
+
+                                        LocalStore.login(result.userHash, result.userName, function () {
+                                            setTimeout(function () { proceed(result); });
+                                        });
+                                    });
+                                });
+                                break;
+                            default: // UNHANDLED ERROR
+                                hashing = false;
+                                UI.errorLoadingScreen(Messages.login_unhandledError);
+                        }
+                        return;
+                    }
+
+                    if (testing) { return void proceed(result); }
+
+                    if (!(proxy.curvePrivate && proxy.curvePublic &&
+                          proxy.edPrivate && proxy.edPublic)) {
+
+                        console.log("recovering derived public/private keypairs");
+                        // **** reset keys ****
+                        proxy.curvePrivate = result.curvePrivate;
+                        proxy.curvePublic  = result.curvePublic;
+                        proxy.edPrivate    = result.edPrivate;
+                        proxy.edPublic     = result.edPublic;
+                    }
+
+                    setTimeout(function () {
+                        Realtime.whenRealtimeSyncs(result.realtime, function () {
+                            proceed(result);
+                        });
+                    });
+                });
+            }, 500);
+        }, 200);
+    };
+
+    return Exports;
+});

BIN
cryptopad/data/customize/main-favicon.png


+ 25 - 0
cryptopad/data/customize/main.js

@@ -0,0 +1,25 @@
+define([
+    'jquery',
+    '/common/outer/local-store.js',
+], function ($, LocalStore) {
+
+    $(function () {
+        var $main = $('#mainBlock');
+
+        // main block is hidden in case javascript is disabled
+        $main.removeClass('hidden');
+
+        // Make sure we don't display non-translated content (empty button)
+        $main.find('#data').removeClass('hidden');
+
+        if (LocalStore.isLoggedIn()) {
+            if (window.location.pathname === '/') {
+                window.location = '/drive/';
+                return;
+            }
+        }
+        $(window).click(function () {
+            $('.cp-dropdown-content').hide();
+        });
+    });
+});

+ 154 - 0
cryptopad/data/customize/messages.js

@@ -0,0 +1,154 @@
+(function () {
+// add your module to this map so it gets used
+var map = {
+    'fr': 'Français',
+    'es': 'Español',
+    'pl': 'Polski',
+    'de': 'Deutsch',
+    'pt-br': 'Português do Brasil',
+    'ro': 'Română',
+    'zh': '繁體中文',
+    'el': 'Ελληνικά',
+};
+
+var messages = {};
+var LS_LANG = "CRYPTPAD_LANG";
+var getStoredLanguage = function () { return localStorage && localStorage.getItem(LS_LANG); };
+var getBrowserLanguage = function () { return navigator.language || navigator.userLanguage || ''; };
+var getLanguage = messages._getLanguage = function () {
+    if (window.cryptpadLanguage) { return window.cryptpadLanguage; }
+    if (getStoredLanguage()) { return getStoredLanguage(); }
+    var l = getBrowserLanguage();
+    // Edge returns 'fr-FR' --> transform it to 'fr' and check again
+    return map[l] ? l :
+            (map[l.split('-')[0]] ? l.split('-')[0] : 'en');
+};
+var language = getLanguage();
+
+// Translations files were migrated from requirejs modules to json.
+// To avoid asking every administrator to update their customized translation files,
+// we use a requirejs map to redirect the old path to the new one and to use the
+// requirejs json plugin
+var reqPaths = {
+    "/common/translations/messages.js":"json!/common/translations/messages.json"
+};
+Object.keys(map).forEach(function (k) {
+    reqPaths["/common/translations/messages."+k+".js"] = "json!/common/translations/messages."+k+".json";
+});
+require.config({
+    map: {
+        "*": reqPaths
+    }
+});
+
+var req = ['/common/common-util.js', '/customize/translations/messages.js'];
+if (language && map[language]) { req.push('/customize/translations/messages.' + language + '.js'); }
+
+define(req, function(Util, Default, Language) {
+    map.en = 'English';
+    var defaultLanguage = 'en';
+
+    Util.extend(messages, Default);
+    if (Language && language !== defaultLanguage) {
+        // Add the translated keys to the returned object
+        Util.extend(messages, Language);
+    }
+
+    messages._languages = map;
+    messages._languageUsed = language;
+
+    messages._checkTranslationState = function (cb) {
+        if (typeof(cb) !== "function") { return; }
+        var allMissing = [];
+        var reqs = [];
+        Object.keys(map).forEach(function (code) {
+            if (code === defaultLanguage) { return; }
+            reqs.push('/customize/translations/messages.' + code + '.js');
+        });
+        require(reqs, function () {
+            var langs = arguments;
+            Object.keys(map).forEach(function (code, i) {
+                if (code === defaultLanguage) { return; }
+                var translation = langs[i];
+                var missing = [];
+                var checkInObject = function (ref, translated, path) {
+                    var updated = {};
+                    Object.keys(ref).forEach(function (k) {
+                        if (/^updated_[0-9]+_/.test(k) && !translated[k]) {
+                            var key = k.split('_').slice(2).join('_');
+                            // Make sure we don't already have an update for that key. It should not happen
+                            // but if it does, keep the latest version
+                            if (updated[key]) {
+                                var ek = updated[key];
+                                if (parseInt(ek.split('_')[1]) > parseInt(k.split('_')[1])) { return; }
+                            }
+                            updated[key] = k;
+                        }
+                    });
+                    Object.keys(ref).forEach(function (k) {
+                        if (/^_/.test(k) || k === 'driveReadme') { return; }
+                        var nPath = path.slice();
+                        nPath.push(k);
+                        if (!translated[k] || updated[k]) {
+                            if (updated[k]) {
+                                var uPath = path.slice();
+                                uPath.unshift('out');
+                                missing.push([code, nPath, 2, uPath.join('.') + '.' + updated[k]]);
+                                return;
+                            }
+                            return void missing.push([code, nPath, 1]);
+                        }
+                        if (typeof ref[k] !== typeof translated[k]) {
+                            return void missing.push([code, nPath, 3]);
+                        }
+                        if (typeof ref[k] === "object" && !Array.isArray(ref[k])) {
+                            checkInObject(ref[k], translated[k], nPath);
+                        }
+                    });
+                    Object.keys(translated).forEach(function (k) {
+                        if (/^_/.test(k) || k === 'driveReadme') { return; }
+                        var nPath = path.slice();
+                        nPath.push(k);
+                        if (typeof ref[k] === "undefined") {
+                            missing.push([code, nPath, 0]);
+                        }
+                    });
+                };
+                checkInObject(Default, translation, []);
+                // Push the removals at the end
+                missing.sort(function (a, b) {
+                    if (a[2] === 0 && b[2] !== 0) { return 1; }
+                    if (a[2] !== 0 && b[2] === 0) { return -1; }
+                    return 0;
+                });
+                Array.prototype.push.apply(allMissing, missing); // Destructive concat
+            });
+            cb(allMissing);
+        });
+    };
+
+    // Get keys with parameters
+    messages._getKey = function (key, argArray) {
+        if (!messages[key]) { return '?'; }
+        var text = messages[key];
+        if (typeof(text) === 'string') {
+            return text.replace(/\{(\d+)\}/g, function (str, p1) {
+                if (typeof(argArray[p1]) === 'string' || typeof(argArray[p1]) === "number") {
+                    return argArray[p1];
+                }
+                console.error("Only strings and numbers can be used in _getKey params!");
+                return '';
+            });
+        } else {
+            return text;
+        }
+    };
+
+    messages.driveReadme = '["BODY",{"class":"cke_editable cke_editable_themed cke_contents_ltr cke_show_borders","contenteditable":"true","spellcheck":"false","style":"color: rgb(51, 51, 51);"},' +
+        '[["H1",{},["'+messages.readme_welcome+'"]],["P",{},["'+messages.readme_p1+'"]],["P",{},["'+messages.readme_p2+'"]],["HR",{},[]],["H2",{},["'+messages.readme_cat1+'",["BR",{},[]]]],["UL",{},[["LI",{},["'+messages._getKey("readme_cat1_l1", ['",["STRONG",{},["'+messages.newButton+'"]],"', '",["STRONG",{},["'+messages.type.pad+'"]],"'])+'"]],["LI",{},["'+messages.readme_cat1_l2+'"]],["LI",{},["'+messages._getKey("readme_cat1_l3", ['",["STRONG",{},["'+messages.fm_unsortedName+'"]],"'])+'",["UL",{},[["LI",{},["'+messages._getKey("readme_cat1_l3_l1", ['",["STRONG",{},["'+messages.fm_rootName+'"]],"'])+'"]],["LI",{},["'+messages.readme_cat1_l3_l2+'"]]]]]],["LI",{},["'+messages._getKey("readme_cat1_l4", ['",["STRONG",{},["'+messages.fm_trashName+'"]],"'])+'",["BR",{},[]]]]]],["P",{},[["BR",{},[]]]],["H2",{},["'+messages.readme_cat2+'",["BR",{},[]]]],["UL",{},[["LI",{},["'+messages._getKey("readme_cat2_l1", ['",["STRONG",{},["'+messages.shareButton+'"]],"', '",["STRONG",{},["'+messages.edit+'"]],"', '",["STRONG",{},["'+messages.view+'"]],"'])+'"]],["LI",{},["'+messages.readme_cat2_l2+'"]]]],["P",{},[["BR",{},[]]]],["H2",{},["'+messages.readme_cat3+'"]],["UL",{},[["LI",{},["'+messages.readme_cat3_l1+'"]],["LI",{},["'+messages.readme_cat3_l2+'"]],["LI",{},["'+messages.readme_cat3_l3+'",["BR",{},[]]]]]]],' +
+        '{"metadata":{"defaultTitle":"' + messages.driveReadmeTitle + '","title":"' + messages.driveReadmeTitle + '"}}]';
+
+    return messages;
+
+});
+}());

+ 146 - 0
cryptopad/data/customize/pages.js

@@ -0,0 +1,146 @@
+define([
+    '/common/hyperscript.js',
+    '/common/common-language.js',
+    '/customize/messages.js',
+    'jquery',
+], function (h, Language, Msg, $) {
+    var Pages = {};
+
+    Pages.setHTML = function (e, html) {
+        e.innerHTML = html;
+        return e;
+    };
+
+    var languageSelector = function () {
+        var options = [];
+        var languages = Msg._languages;
+        var selected = Msg._languageUsed;
+        var keys = Object.keys(languages).sort();
+        keys.forEach(function (l) {
+            var attr = { value: l };
+            if (selected === l) { attr.selected = 'selected'; }
+            options.push(h('option', attr, languages[l]));
+        });
+        var select = h('select', {}, options);
+        $(select).change(function () {
+            Language.setLanguage($(select).val() || '', null, function () {
+                window.location.reload();
+            });
+        });
+        return select;
+    };
+
+    var footerCol = function (title, L, literal) {
+        return h('div.col-6.col-sm-3', [
+            h('ul.list-unstyled', [
+                h('li.footer-title', {
+                    'data-localization': title,
+                }, title? Msg[title]: literal )
+                ].concat(L.map(function (l) {
+                    return h('li', [ l ]);
+                }))
+            )
+        ]);
+    };
+
+    var footLink = function (ref, loc, text) {
+        var attrs =  {
+            href: ref,
+        };
+        if (!/^\//.test(ref)) {
+            attrs.target = '_blank';
+            attrs.rel = 'noopener noreferrer';
+        }
+        if (loc) {
+            attrs['data-localization'] =  loc;
+            text = Msg[loc];
+        }
+        return h('a', attrs, text);
+    };
+
+    Pages.infopageFooter = function () {
+        return h('footer', [
+            h('div.container', [
+                h('div.row', [
+                    footerCol(null, [
+                        h('div.cp-bio-foot', [
+                            h('p', Msg.main_footerText),
+                            languageSelector()
+                        ])
+                    ], ''),
+                    footerCol('footer_applications', [
+                        footLink('/drive/', 'main_drive'),
+                        footLink('/pad/', 'main_richText'),
+                        footLink('/code/', 'main_code'),
+                        footLink('/slide/', 'main_slide'),
+                        footLink('/poll/', 'main_poll'),
+                        footLink('/kanban/', 'main_kanban'),
+                        footLink('/whiteboard/', null, Msg.type.whiteboard)
+                    ]),
+//                    footerCol('footer_aboutUs', [
+//                        footLink('https://blog.cryptpad.fr', 'blog'),
+//                        footLink('https://labs.xwiki.com', null, 'XWiki Labs'),
+//                        footLink('http://www.xwiki.com', null, 'XWiki SAS'),
+//                        footLink('https://www.open-paas.org', null, 'OpenPaaS')
+//                    ]),
+//                    footerCol('footer_contact', [
+//                        footLink('https://riot.im/app/#/room/#cryptpad:matrix.org', null, 'Chat'),
+//                        footLink('https://twitter.com/cryptpad', null, 'Twitter'),
+//                        footLink('https://github.com/xwiki-labs/cryptpad', null, 'GitHub'),
+//                        footLink('/contact.html', null, 'Email')
+//                    ])
+                ])
+            ]),
+            h('div.cp-version-footer', "CryptPad v2.18.0 (Sloth)")
+        ]);
+    };
+
+    Pages.infopageTopbar = function () {
+        var rightLinks;
+        var username = window.localStorage.getItem('User_name');
+        if (username === null) {
+            rightLinks = [
+                h('a.nav-item.nav-link.cp-login-btn', { href: '/login/'}, Msg.login_login),
+                h('a.nav-item.nav-link.cp-register-btn', { href: '/register/'}, Msg.login_register)
+            ];
+        } else {
+            rightLinks = h('a.nav-item.nav-link.cp-user-btn', { href: '/drive/' }, [
+                h('i.fa.fa-user-circle'),
+                " ",
+                username
+            ]);
+        }
+
+        var button = h('button.navbar-toggler', {
+            'type':'button',
+            /*'data-toggle':'collapse',
+            'data-target':'#menuCollapse',
+            'aria-controls': 'menuCollapse',
+            'aria-expanded':'false',
+            'aria-label':'Toggle navigation'*/
+        }, h('i.fa.fa-bars '));
+
+        $(button).click(function () {
+            if ($('#menuCollapse').is(':visible')) {
+                return void $('#menuCollapse').slideUp();
+            }
+            $('#menuCollapse').slideDown();
+        });
+
+        return h('nav.navbar.navbar-expand-lg',
+            h('a.navbar-brand', { href: '/index.html'}),
+            button,
+           /* h('div.collapse.navbar-collapse.justify-content-end#menuCollapse', [
+                //h('a.nav-item.nav-link', { href: '/what-is-cryptpad.html'}, Msg.topbar_whatIsCryptpad), // Moved the FAQ
+                h('a.nav-item.nav-link', { href: '/faq.html'}, Msg.faq_link),
+                h('a.nav-item.nav-link', { href: 'https://blog.cryptpad.fr/'}, Msg.blog),
+                h('a.nav-item.nav-link', { href: '/features.html'}, Msg.features),
+                h('a.nav-item.nav-link', { href: '/privacy.html'}, Msg.privacy),
+                h('a.nav-item.nav-link', { href: '/contact.html'}, Msg.contact),
+                h('a.nav-item.nav-link', { href: '/about.html'}, Msg.about),
+            ].concat(rightLinks))*/
+        );
+    };
+
+    return Pages;
+});

+ 128 - 0
cryptopad/data/customize/pages/about.js

@@ -0,0 +1,128 @@
+define([
+    '/common/hyperscript.js',
+    '/customize/messages.js',
+    '/customize/pages.js'
+], function (h, Msg, Pages) {
+    return function () {
+        return h('div#cp-main', [
+            Pages.infopageTopbar(),
+            h('div.container-fluid.cp-about-intro', [
+                h('div.container', [
+                    h('center', [
+                        h('h1', Msg.about),
+                        Pages.setHTML(h('p'), Msg.about_intro),
+                    ]),
+                ]),
+            ]),
+            h('div.container.cp-container', [
+                h('div.row', [
+                    h('div.cp-develop-about.col-12',[
+                            h('div.cp-icon-cent'),
+                            h('h2.text-center', Msg.about_core)
+                        ]),
+                    ]),
+                h('div.row.align-items-center', [
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-bio-avatar', [
+                        h('img.img-fluid', {'src': '/customize/images/CalebJames.jpg'})
+                            ]),
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-profile-det', [
+                        h('h3', "Caleb James Delisle"),
+                        h('hr'),
+                        Pages.setHTML(h('div#bioCaleb'), '<p>Caleb is a cryptography developer, Machine Technology graduate of the Franklin County Technical School and lifelong tinkerer.<br/>In 2011, he started the cjdns Open Source project to show that secure networking could be invisible and easily deployed.<br/>After joining XWiki SAS in 2014, he started the CryptPad project with the intent of bringing the same transparent security to collaborative editing.<br/>He\'s always trying to learn from more experienced colleagues and when someone passes through the Research Team office, his favorite words are "Pull up a chair!".</p>'),
+                        h('a.cp-soc-media', { href : 'https://twitter.com/cjdelisle'}, [
+                                h('i.fa.fa-twitter')
+                            ]),
+                        h('a.cp-soc-media', { href : 'https://github.com/cjdelisle'}, [
+                                h('i.fa.fa-github')
+                            ])
+                    ]),
+                ]),
+                h('div.row.align-items-center',[
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.order-lg-2.cp-bio-avatar.cp-bio-avatar-right', [
+                            h('img.img-fluid', {'src': '/customize/images/AaronMacSween.jpg'})
+                    ]),
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.order-lg-1.cp-profile-det',[
+                        h('h3', "Aaron MacSween"),
+                        h('hr'),
+                        Pages.setHTML(h('div#bioAaron'), '<p>Aaron transitioned into distributed systems development from a background in jazz and live stage performance. <br/> He appreciates the elegance of biological systems and functional programming, and focused on both as a student at the University of Toronto, where he studied cognitive and computer sciences.<br/>He moved to Paris in 2015 to work as a research engineer at XWiki SAS, after having dedicated significant time to various cryptography-related software projects.<br/>He spends his spare time experimenting with guitars, photography, science fiction, and spicy food.</p>'),
+                        h('a.cp-soc-media', { href : 'https://twitter.com/fc00ansuz'}, [
+                                h('i.fa.fa-twitter')
+                            ]),
+                        h('a.cp-soc-media', { href : 'https://github.com/ansuz/'}, [
+                                h('i.fa.fa-github')
+                            ])
+                    ]),
+                ]),
+                h('div.row.align-items-center', [
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-bio-avatar', [
+                        h('img.img-fluid', {'src': '/customize/images/YannFlory.jpg'})
+                            ]),
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-profile-det', [
+                        h('h3', "Yann Flory"),
+                        h('hr'),
+                        Pages.setHTML(h('div#bioYann'), '<p>In 2015, Yann graduated with an engineering degree from Ecole Centrale de Lille majoring in Data Science. In his studies he worked on a project to detect defects in optical fiber using image processing technology.<br/>Upon joining XWiki SAS, Yann developed a Wiki page recommendation system, a common API for accessing data server-side and client-side, and an integrated development environment for development of XWiki applications.<br/>Yann is soft spoken but brutally efficient, he is known to say "It will take 5 minutes".</p>'),
+                        h('a.cp-soc-media', { href : 'https://github.com/yflory/'}, [
+                                h('i.fa.fa-github')
+                            ])
+                    ]),
+                ]),
+                h('div.row', [
+                    h('div.cp-develop-about.col-12.cp-contrib',[
+                            h('div.cp-icon-cent'),
+                            h('h2.text-center', Msg.about_contributors)
+                        ]),
+                    ]),
+                h('div.row.align-items-center', [
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-bio-avatar', [
+                        h('img.img-fluid', {'src': '/customize/images/Pierre-new.jpg'})
+                            ]),
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-profile-det', [
+                        h('h3', "Pierre Bondoerffer"),
+                        h('hr'),
+                        Pages.setHTML(h('div#bioPierre'), '<p>Resident CSS wizard and emoji extraordinaire, Pierre is passionate about anything related to technology. He loves to hack around computers and put parts together.<br/>He is currently studying at 42, where he learns about algorithms, networking, kernel programming and graphics.<br/>As a part of an internship, he joined XWiki SAS and worked on CryptPad to improve user experience. He also maintains the Spanish translation.</p>'),
+                        h('a.cp-soc-media', { href : 'https://twitter.com/pbondoer'}, [
+                                h('i.fa.fa-twitter')
+                            ]),
+                        h('a.cp-soc-media', { href : 'https://github.com/pbondoer'}, [
+                                h('i.fa.fa-github')
+                            ])
+                    ]),
+                ]),
+                h('div.row.align-items-center',[
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.order-lg-2.cp-bio-avatar.cp-bio-avatar-right', [
+                            h('img.img-fluid', {'src': '/customize/images/Catalin.jpg'})
+                    ]),
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.order-lg-1.cp-profile-det',[
+                        h('h3', "Catalin Scripcariu"),
+                        h('hr'),
+                        Pages.setHTML(h('div#bioCatalin'), '<p> Catalin is a Maths majour and has worked in B2B sales for 12 years. Design was always his passion and 3 years ago he started to dedicate himself to web design and front-end.<br/>At the beginning of 2017 he joined the XWiki, where he worked both on the business and the community side of XWiki, including the research team and CryptPad. </p>'),
+                        h('a.cp-soc-media', { href : 'https://twitter.com/catalinscr'}, [
+                                h('i.fa.fa-twitter')
+                            ]),
+                        h('a.cp-soc-media', { href : 'https://www.linkedin.com/in/catalinscripcariu/'}, [
+                                h('i.fa.fa-linkedin')
+                            ])
+                    ]),
+                ]),
+                h('div.row.align-items-center.cp-margin-bot', [
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-bio-avatar', [
+                        h('img.img-fluid', {'src': '/customize/images/LudovicDuboist.jpg'})
+                            ]),
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-profile-det', [
+                        h('h3', "Ludovic Dubost"),
+                        h('hr'),
+                        Pages.setHTML(h('div#bioLudovic'), '<p>A graduate of PolyTech (X90) and Telecom School in Paris, Ludovic Dubost started his career as a software architect for Netscape Communications Europe. He then became CTO of NetValue, one of the first French start-ups that went public. He left NetValue after the company was purchased by Nielsen/NetRatings and in 2004 launched XWiki, the next generation wiki.<br/>Since the very beginning, Ludovic has been immensely helpful to the CryptPad project. He believed in the idea when there was nothing more than the collaborative pad and his help with sales strategy for the project.</p>'),
+                        h('a.cp-soc-media', { href : 'https://twitter.com/ldubost'}, [
+                                h('i.fa.fa-twitter')
+                            ]),
+                        h('a.cp-soc-media', { href : 'https://github.com/ldubost'}, [
+                                h('i.fa.fa-github')
+                            ])
+                    ]),
+                ]),
+            ]),
+            Pages.infopageFooter()
+        ]);
+    };
+});
+

+ 53 - 0
cryptopad/data/customize/pages/contact.js

@@ -0,0 +1,53 @@
+define([
+    '/common/hyperscript.js',
+    '/customize/messages.js',
+    '/customize/pages.js'
+], function (h, Msg, Pages) {
+    return function () {
+        return h('div#cp-main', [
+            Pages.infopageTopbar(),
+            h('div.container-fluid.cp-contdet', [
+                h('row.col-12.col-sm-12',
+                    h('h1.text-center', Msg.contact )
+                )
+            ]),
+            h('div.container.cp-container', [
+                h('div.row.cp-iconCont.align-items-center', [
+                    h('div.col-12',
+                        Pages.setHTML(h('h4.text-center'), Msg.main_about_p26)
+                    ),
+                    h('div.col-12.col-sm-6.col-md-3.col-lg-3',
+                        h('a.card', {href : "https://twitter.com/cryptpad"}, 
+                            h('div.card-body', 
+                                Pages.setHTML(h('p'), Msg.main_about_p22)
+                            )
+                        )
+                    ),
+                    h('div.col-12.col-sm-6.col-md-3.col-lg-3',
+                        h('a.card', {href : "https://github.com/xwiki-labs/cryptpad/issues/"},
+                            h('div.card-body', 
+                                Pages.setHTML(h('p'), Msg.main_about_p23)
+                            )
+                        )
+                    ),
+                    h('div.col-12.col-sm-6.col-md-3.col-lg-3',
+                        h('a.card', {href : "https://riot.im/app/#/room/#cryptpad:matrix.org"},
+                            h('div.card-body', 
+                                Pages.setHTML(h('p'), Msg.main_about_p24)
+                            )
+                        )
+                    ),
+                    h('div.col-12.col-sm-6.col-md-3.col-lg-3',
+                        h('a.card', {href : "mailto:research@xwiki.com"},
+                            h('div.card-body', 
+                                Pages.setHTML(h('p'), Msg.main_about_p25)
+                            )
+                        )
+                    ),
+                ]),
+            ]),
+            Pages.infopageFooter(),
+        ]);
+    };
+});
+

+ 61 - 0
cryptopad/data/customize/pages/faq.js

@@ -0,0 +1,61 @@
+define([
+    'jquery',
+    '/common/hyperscript.js',
+    '/customize/messages.js',
+    '/customize/pages.js'
+], function ($, h, Msg, Pages) {
+    return 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 hash = c + '-' + q;
+                var question = h('p.cp-faq-questions-q#' + hash);
+                $(question).click(function () {
+                    if ($(answer).is(':visible')) {
+                        $(question).toggleClass('cp-active-faq');
+                        return void $(answer).slideUp();
+                    }
+                    $(question).toggleClass('cp-active-faq');
+                    $(answer).slideDown();
+                    var t = $(window).scrollTop();
+                    window.location.hash = hash;
+                    $(window).scrollTop(t);
+                });
+                questions.push(h('div.cp-faq-questions-items', [
+                    Pages.setHTML(question, item.q),
+                    Pages.setHTML(answer, item.a)
+                ]));
+            });
+            categories.push(h('div.cp-faq-category', [
+                h('h3', faq[c].title),
+                h('div.cp-faq-category-questions', questions)
+            ]));
+        });
+        var hash = window.location.hash;
+        if (hash) {
+            $(categories).find(hash).click();
+        }
+        return h('div#cp-main', [
+            Pages.infopageTopbar(),
+            h('div.container-fluid.cp-faq', [
+                h('div.container',[
+                    h('center', h('h1', Msg.faq_title)),
+                ]),
+            ]),
+            h('div.container.cp-faq-ques-det',[
+                h('div.cp-faq-header.text-center', h('a.nav-item.nav-link', {
+                    href: '/what-is-cryptpad.html'
+                }, Pages.setHTML(h('h4'),Msg.faq_whatis))),
+                h('div.cp-faq-container', categories)
+            ]),
+            Pages.infopageFooter()
+        ]);
+    };
+
+});
+

+ 115 - 0
cryptopad/data/customize/pages/features.js

@@ -0,0 +1,115 @@
+define([
+    'jquery',
+    '/common/hyperscript.js',
+    '/customize/messages.js',
+    '/customize/application_config.js',
+    '/common/outer/local-store.js',
+    '/customize/pages.js'
+], function ($, h, Msg, AppConfig, LocalStore, Pages) {
+    var origin = encodeURIComponent(window.location.hostname);
+    var accounts = {
+        donateURL: 'https://accounts.cryptpad.fr/#/donate?on=' + origin,
+        upgradeURL: 'https://accounts.cryptpad.fr/#/?on=' + origin,
+    };
+    return function () {
+        Msg.features_f_apps_note = AppConfig.availablePadTypes.map(function (app) {
+            if (AppConfig.registeredOnlyTypes.indexOf(app) !== -1) { return; }
+            return Msg.type[app];
+        }).filter(function (x) { return x; }).join(', ');
+        var premiumButton = h('a', {
+            href: accounts.upgradeURL,
+            target: '_blank',
+            rel: 'noopener noreferrer'
+        }, h('button.cp-features-register-button', Msg.features_f_subscribe));
+        $(premiumButton).click(function (e) {
+            if (LocalStore.isLoggedIn()) { return; }
+            // Not logged in: go to /login with a redirect to this page
+            e.preventDefault();
+            e.stopPropagation();
+            sessionStorage.redirectTo = '/features.html';
+            window.location.href = '/login/';
+        });
+        return h('div#cp-main', [
+            Pages.infopageTopbar(),
+            h('div.container-fluid.cp_cont_features',[
+                h('div.container',[
+                    h('center', h('h1', Msg.features_title)),
+                ]),
+            ]),
+            h('div.container',[
+                h('div.row.cp-container.cp-features-web.justify-content-sm-center',[
+                    h('div.col-12.col-sm-4.cp-anon-user',[
+                        h('div.card',[
+                            h('div.card-body',[
+                                h('h3.text-center',Msg.features_anon)
+                            ]),
+                            h('ul.list-group.list-group-flush',
+                                ['apps', 'core', 'file0', 'cryptdrive0', 'storage0'].map(function (f) {
+                                    return h('li.list-group-item', [
+                                        h('div.cp-check'),
+                                        h('div.cp-content', [
+                                            h('div.cp-feature', Msg['features_f_' + f]),
+                                            h('div.cp-note', Msg['features_f_' + f + '_note'])
+                                        ])
+                                    ]);
+                                })
+                            ),
+                        ]),
+                    ]),
+                    h('div.col-12.col-sm-4.cp-regis-user',[
+                        h('div.card',[
+                            h('div.card-body',[
+                                h('h3.text-center',Msg.features_registered)
+                            ]),
+                            h('ul.list-group.list-group-flush', [
+                                ['anon', 'social', 'file1', 'cryptdrive1', 'devices', 'storage1'].map(function (f) {
+                                    return h('li.list-group-item', [
+                                        h('div.cp-check'),
+                                        h('div.cp-content', [
+                                            h('div.cp-feature', Msg['features_f_' + f]),
+                                            h('div.cp-note', Msg['features_f_' + f + '_note'])
+                                        ])
+                                    ]);
+                                }),
+                            ]),
+                            h('div.card-body',[
+                                h('div.cp-features-register#cp-features-register', [
+                                    h('a', {
+                                        href: '/register/'
+                                    }, h('button.cp-features-register-button', Msg.features_f_register))
+                                ]),
+                                h('div.cp-note', Msg.features_f_register_note)
+                            ]),
+                        ]),
+                    ]),
+                    h('div.col-12.col-sm-4.cp-anon-user',[
+                        h('div.card',[
+                            h('div.card-body',[
+                                h('h3.text-center',Msg.features_premium)
+                            ]),
+                            h('ul.list-group.list-group-flush', [
+                                ['reg', 'storage2', 'support', 'supporter'].map(function (f) {
+                                    return h('li.list-group-item', [
+                                        h('div.cp-check'),
+                                        h('div.cp-content', [
+                                            h('div.cp-feature', Msg['features_f_' + f]),
+                                            h('div.cp-note', Msg['features_f_' + f + '_note'])
+                                        ])
+                                    ]);
+                                }),
+                            ]),
+                            h('div.card-body',[
+                                h('div.cp-features-register#cp-features-subscribe', [
+                                    premiumButton
+                                ]),
+                                LocalStore.isLoggedIn() ? undefined : h('div.cp-note', Msg.features_f_subscribe_note)
+                            ]),
+                        ]),
+                    ]),
+                ]),
+            ]),
+            Pages.infopageFooter()
+        ]);
+    };
+});
+

+ 151 - 0
cryptopad/data/customize/pages/index.js

@@ -0,0 +1,151 @@
+define([
+    'jquery',
+    '/api/config',
+    '/common/hyperscript.js',
+    '/common/common-feedback.js',
+    '/customize/messages.js',
+    '/customize/application_config.js',
+    '/common/outer/local-store.js',
+    '/customize/pages.js'
+], function ($, Config, h, Feedback, Msg, AppConfig, LocalStore, Pages) {
+    var urlArgs = Config.requireConf.urlArgs;
+
+    var isAvailableType = function (x) {
+        if (!Array.isArray(AppConfig.availablePadTypes)) { return true; }
+        return AppConfig.availablePadTypes.indexOf(x) !== -1;
+    };
+
+    var checkRegisteredType = function (x) {
+        // Return true if we're registered or if the app is not registeredOnly
+        if (LocalStore.isLoggedIn()) { return true; }
+        if (!Array.isArray(AppConfig.registeredOnlyTypes)) { return true; }
+        return AppConfig.registeredOnlyTypes.indexOf(x) === -1;
+    };
+
+    return function () {
+        var showingMore = false;
+
+        var icons = [
+                [ 'pad', Msg.main_richTextPad],
+                [ 'code', Msg.main_codePad],
+                [ 'slide', Msg.main_slidePad],
+                [ 'sheet', Msg.main_sheetPad],
+                [ 'poll', Msg.main_pollPad],
+                [ 'kanban', Msg.main_kanbanPad],
+                [ 'whiteboard', Msg.main_whiteboardPad],
+                [ 'drive', LocalStore.isLoggedIn() ? Msg.main_yourCryptDrive : Msg.main_localPads]
+            ].filter(function (x) {
+                return isAvailableType(x[0]) && checkRegisteredType(x[0]);
+            })
+            .map(function (x, i) {
+                var s = 'div.bs-callout.cp-callout-' + x[0];
+                if (i > 8) { s += '.cp-more.cp-hidden'; }
+                var icon = AppConfig.applicationsIcon[x[0]];
+                var font = icon.indexOf('cptools') === 0 ? 'cptools' : 'fa';
+                return h('a', [
+                    { href: '/'+ x[0] +'/' },
+                    h(s, [
+                        h('i.' + font + '.' + icon),
+                        h('div.pad-button-text', [ h('h4', x[1]) ])
+                    ])
+                ]);
+            });
+
+        var more = icons.length < 9? undefined: h('div.bs-callout.cp-callout-more', [
+                h('div.cp-callout-more-lessmsg.cp-hidden', [
+                    "see less ",
+                    h('i.fa.fa-caret-up')
+                ]),
+                h('div.cp-callout-more-moremsg', [
+                    "see more ",
+                    h('i.fa.fa-caret-down')
+                ]),
+                {
+                    onclick: function () {
+                        if (showingMore) {
+                            $('.cp-more, .cp-callout-more-lessmsg').addClass('cp-hidden');
+                            $('.cp-callout-more-moremsg').removeClass('cp-hidden');
+                        } else {
+                            $('.cp-more, .cp-callout-more-lessmsg').removeClass('cp-hidden');
+                            $('.cp-callout-more-moremsg').addClass('cp-hidden');
+                        }
+                        showingMore = !showingMore;
+                    }
+                }
+            ]);
+
+        var _link = h('a', {
+            href: "https://opencollective.com/cryptpad/contribute",
+            target: '_blank',
+            rel: 'noopener',
+        });
+
+        var crowdFunding = AppConfig.disableCrowdfundingMessages ? undefined : h('button', [
+            Msg.crowdfunding_button
+        ]);
+
+        $(crowdFunding).click(function () {
+            _link.click();
+            Feedback.send('HOME_SUPPORT_CRYPTPAD');
+        });
+
+        var blocks = h('div.container',[
+            h('div.row.justify-content-sm-center',[
+                h('div.col-12.col-sm-4.cp-index-block.cp-index-block-host', h('div', [
+                    Pages.setHTML(h('span'), Msg.home_host),
+                    h('div.cp-img-container', [
+                        h('img.agpl', {
+                            src: "/customize/images/AGPL.png",
+                            title: Msg.home_host_agpl
+                        }),
+                        h('a.img', {
+                            href: 'https://blog.cryptpad.fr/2018/11/13/CryptPad-receives-NGI-Startup-Award/',
+                            target: '_blank'
+                        }, h('img.ngi', {
+                            src: "/customize/images/ngi.png",
+                            title: Msg.home_ngi
+                        }))
+                    ])
+                ])),
+                h('div.col-12.col-sm-4.cp-index-block.cp-index-block-product', h('div', [
+                    Msg.home_product
+                ])),
+                h('div.col-12.col-sm-4.cp-index-block.cp-index-block-help', h('div', [
+                    Msg.crowdfunding_home1,
+                    h('br'),
+                    Msg.crowdfunding_home2,
+                    h('br'),
+                    crowdFunding,
+                    _link
+                ])),
+            ])
+        ]);
+
+        return [
+            h('div#cp-main', [
+                Pages.infopageTopbar(),
+                h('div.container.cp-container', [
+                    h('div.row', [
+                        h('div.cp-title.col-12.col-sm-6', [
+                            h('img', { src: '/customize/cryptpad-new-logo-colors-logoonly.png?' + urlArgs }),
+                            h('h1', 'CryptPad'),
+                            h('p', Msg.main_catch_phrase)
+                        ]),
+                        h('div.col-12.col-sm-6', [
+                            icons,
+                            more
+                        ])
+                    ]),
+                    //blocks,
+                    /*h('div.row', [
+                        h('div.cp-crowdfunding', [
+                            crowdFunding
+                        ])
+                    ])*/
+                ]),
+            ]),
+            //Pages.infopageFooter(),
+        ];
+    };
+});
+

+ 43 - 0
cryptopad/data/customize/pages/login.js

@@ -0,0 +1,43 @@
+define([
+    '/common/hyperscript.js',
+    '/common/common-interface.js',
+    '/customize/messages.js',
+    '/customize/pages.js'
+], function (h, UI, Msg, Pages) {
+    return function () {
+        return [h('div#cp-main', [
+            Pages.infopageTopbar(),
+            h('div.container.cp-container', [
+                h('div.row.align-items-center', [
+                    h('div#data.hidden.col-md-6', Pages.setHTML(h('p.left'), Msg.main_info)),
+                    h('div#userForm.form-group.hidden.col-md-6', [
+                        h('input.form-control#name', {
+                            name: 'name',
+                            type: 'text',
+                            autocomplete: 'off',
+                            autocorrect: 'off',
+                            autocapitalize: 'off',
+                            spellcheck: false,
+                            placeholder: Msg.login_username,
+                            autofocus: true,
+                        }),
+                        h('input.form-control#password', {
+                            type: 'password',
+                            'name': 'password',
+                            placeholder: Msg.login_password,
+                        }),
+                        h('div.checkbox-container', [
+                            UI.createCheckbox('import-recent', Msg.register_importRecent),
+                        ]),
+                        h('div.extra', [
+                            h('button.login.first.btn', Msg.login_login),
+                            h('button#register.first.btn', Msg.login_register)
+                        ])
+                    ])
+                ]),
+            ]),
+            Pages.infopageFooter(),
+        ])];
+    };
+});
+

+ 46 - 0
cryptopad/data/customize/pages/privacy.js

@@ -0,0 +1,46 @@
+define([
+    '/common/hyperscript.js',
+    '/customize/messages.js',
+    '/customize/pages.js'
+], function (h, Msg, Pages) {
+    return function () {
+        return h('div#cp-main', [
+            Pages.infopageTopbar(),
+            h('.container-fluid.cp-privacy-top', [
+                h('div.container',[
+                    h('center', h('h1', Msg.policy_title)),
+                ]),
+            ]),
+            h('div.container.cp-container.cp-privacy',[
+                h('h3', Msg.policy_whatweknow),
+                h('hr'),
+                Pages.setHTML(h('p'), Msg.policy_whatweknow_p1),
+
+                h('h3', Msg.policy_howweuse),
+                h('hr'),
+                h('p', Msg.policy_howweuse_p1),
+                h('p', Msg.policy_howweuse_p2),
+
+                h('h3', Msg.policy_whatwetell),
+                h('hr'),
+                h('p', Msg.policy_whatwetell_p1),
+
+                h('h3', Msg.policy_links),
+                h('hr'),
+                h('p', Msg.policy_links_p1),
+
+                h('h3', Msg.policy_ads),
+                h('hr'),
+                h('p', Msg.policy_ads_p1),
+
+                h('h3', Msg.policy_choices),
+                h('hr'),
+                h('p', Msg.policy_choices_open),
+                Pages.setHTML(h('p'), Msg.policy_choices_vpn),
+            ]),
+            Pages.infopageFooter()
+        ]);
+    };
+
+});
+

+ 69 - 0
cryptopad/data/customize/pages/register.js

@@ -0,0 +1,69 @@
+define([
+    'jquery',
+    '/common/hyperscript.js',
+    '/common/common-interface.js',
+    '/customize/messages.js',
+    '/customize/pages.js'
+], function ($, h, UI, Msg, Pages) {
+    return function () {
+        return [h('div#cp-main', [
+            Pages.infopageTopbar(),
+            h('div.container-fluid.cp-register-wel',[
+                h('div.container',[
+                    h('div.row',[
+                        h('div.col-12',[
+                            h('h1.text-center', Msg.register_header)
+                        ])
+                    ])
+                ])
+            ]),
+            h('div.container.cp-container', [
+                h('div.row.cp-register-det', [
+                h('div#data.hidden.col-md-6', [
+                    Pages.setHTML(h('p.register-explanation'), Msg.register_explanation)
+                ]),
+                h('div#userForm.form-group.hidden.col-md-6', [
+                    h('a', {
+                        href: '/features.html'
+                    }, Msg.register_whyRegister),
+                    h('input.form-control#username', {
+                        type: 'text',
+                        autocomplete: 'off',
+                        autocorrect: 'off',
+                        autocapitalize: 'off',
+                        spellcheck: false,
+                        placeholder: Msg.login_username,
+                        autofocus: true,
+                    }),
+                    h('input.form-control#password', {
+                        type: 'password',
+                        placeholder: Msg.login_password,
+                    }),
+                    h('input.form-control#password-confirm', {
+                        type: 'password',
+                        placeholder: Msg.login_confirm,
+                    }),
+                    h('div.checkbox-container', [
+                        UI.createCheckbox('import-recent', Msg.register_importRecent, true)
+                    ]),
+                    h('div.checkbox-container', [
+                        $(UI.createCheckbox('accept-terms')).find('.cp-checkmark-label').append(Msg.register_acceptTerms).parent()[0]
+                    ]),
+                    h('button#register.btn.cp-login-register', Msg.login_register)
+                ])
+                ]),
+                h('div.row.cp-register-test',[
+                    h('hr'),
+                    h('div.col-12', [
+                        Pages.setHTML(h('p.test-details'), " \"Tools like Etherpad and Google Docs [...] all share a weakness, which is that whomever owns the document server can see everything you're typing. Cryptpad is a free/open project that uses some of the ideas behind blockchain to implement a \"zero-knowledge\" version of a collaborative document editor, ensuring that only the people working on a document can see it.\" "),
+                        h('a.cp-test-source.pull-right', { href : 'http://boingboing.net/2016/09/26/cryptpad-a-freeopen-end-to.html'}, "Cory Doctorow")
+                    ])
+                ])
+            ]),
+
+            Pages.infopageFooter(),
+        ])];
+    };
+
+});
+

+ 21 - 0
cryptopad/data/customize/pages/terms.js

@@ -0,0 +1,21 @@
+define([
+    '/common/hyperscript.js',
+    '/customize/messages.js',
+    '/customize/pages.js'
+], function (h, Msg, Pages) {
+    return function () {
+        return h('div#cp-main', [
+            Pages.infopageTopbar(),
+            h('div.container.cp-container', [
+                h('center', h('h1', Msg.tos_title)),
+                h('p', Msg.tos_legal),
+                h('p', Msg.tos_availability),
+                h('p', Msg.tos_e2ee),
+                h('p', Msg.tos_logs),
+                h('p', Msg.tos_3rdparties),
+            ]),
+            Pages.infopageFooter()
+        ]);
+    };
+});
+

+ 64 - 0
cryptopad/data/customize/pages/what-is-cryptpad.js

@@ -0,0 +1,64 @@
+define([
+    '/api/config',
+    '/common/hyperscript.js',
+    '/customize/messages.js',
+    '/customize/pages.js'
+], function (Config, h, Msg, Pages) {
+    var urlArgs = Config.requireConf.urlArgs;
+    return function () {
+        return h('div#cp-main', [
+            Pages.infopageTopbar(),
+            h('div.container-fluid.cp-what-is',[
+                h('div.container',[
+                    h('div.row',[
+                        h('div.col-12.text-center', h('h1', Msg.whatis_title)),
+                    ]),
+                ]),
+            ]),
+            h('div.container.cp-container', [
+                h('div.row.align-items-center', [
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6', [
+                        Pages.setHTML(h('h2'), Msg.whatis_collaboration),
+                        Pages.setHTML(h('p'), Msg.whatis_collaboration_p1),
+                        Pages.setHTML(h('p'), Msg.whatis_collaboration_p2),
+                        Pages.setHTML(h('p'), Msg.whatis_collaboration_p3),
+                    ]),
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6', [
+                        h('img', { src: '/customize/images/pad_screenshot.png?' + urlArgs }),
+                    ]),
+                ]),
+                h('div.row.align-items-center', [
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.order-2', [
+                        Pages.setHTML(h('h2'), Msg.whatis_zeroknowledge),
+                        Pages.setHTML(h('p'), Msg.whatis_zeroknowledge_p1),
+                        Pages.setHTML(h('p'), Msg.whatis_zeroknowledge_p2),
+                        Pages.setHTML(h('p'), Msg.whatis_zeroknowledge_p3),
+                    ]),
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6.order-1', [
+                        h('img#zeroknowledge', { src: '/customize/images/zeroknowledge_small.png?' + urlArgs }),
+                    ]),
+                ]),
+                h('div.row.align-items-center', [
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6', [
+                        Pages.setHTML(h('h2'), Msg.whatis_drive),
+                        Pages.setHTML(h('p'), Msg.whatis_drive_p1),
+                        Pages.setHTML(h('p'), Msg.whatis_drive_p2),
+                        Pages.setHTML(h('p'), Msg.whatis_drive_p3),
+                    ]),
+                    h('div.col-12.col-sm-12.col-md-12.col-lg-6', [
+                        h('img', { src: '/customize/images/drive_screenshot.png?' + urlArgs }),
+                    ]),
+                ]),
+                h('div.row.align-items-center', [
+                    h('div.col-12', [
+                        Pages.setHTML(h('h2.text-center'), Msg.whatis_business),
+                        Pages.setHTML(h('p'), Msg.whatis_business_p1),
+                        Pages.setHTML(h('p'), Msg.whatis_business_p2),
+                    ]),
+                ]),
+            ]),
+            Pages.infopageFooter(),
+        ]);
+    };
+});
+

+ 16 - 0
cryptopad/data/customize/privacy.html

@@ -0,0 +1,16 @@
+<!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>

+ 25 - 0
cryptopad/data/customize/readme.md

@@ -0,0 +1,25 @@
+# Customizing CryptPad
+
+In order allow a variety of features to be changed and to allow site-specific changes
+to CryptPad apps while still keeping the git repository pristine, this directory exists
+to allow a set of hooks to be run.
+
+The server is configured to load files from the `/customize/` path preferentially from
+`cryptpad/customize/`, and to fall back to `cryptpad/customize.dist/` if they are not found
+
+If you wish to customize cryptpad, please **copy**
+`/customize.dist/` to `/customize` and then edit it there, this way you will still be able
+to pull from (and make pull requests to (!) the git repository. 
+
+## Files you may be interested in
+
+* index.html is the main page
+* main.js contains javascript for the home page
+* application_config.js allows you to modify settings used by the various applications
+* messages.js contains functions for applying translations to various pages
+  * look inside `/translations/` for the rest of the files which contain translated strings
+* `/share/` implements an iframe RPC which allows multiple domains to access the same localStorage
+* `/src/` contains source files for html and css (in the form of html templates and .less stylesheets)
+
+All other content which is placed in this directory will be referencable at the `/customize/`
+URL location.

Some files were not shown because too many files changed in this diff