+ CryptPad: Zero Knowledge, Collaborative Real Time Editing
+
+
+
+
+
+
+
diff --git a/custom/cryptopad_custom/customize/CryptPad-white-logo.svg b/custom/cryptopad_custom/customize/CryptPad-white-logo.svg
new file mode 100644
index 0000000..26131b5
--- /dev/null
+++ b/custom/cryptopad_custom/customize/CryptPad-white-logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/custom/cryptopad_custom/customize/CryptPad_logo_color.svg b/custom/cryptopad_custom/customize/CryptPad_logo_color.svg
new file mode 100644
index 0000000..64fc866
--- /dev/null
+++ b/custom/cryptopad_custom/customize/CryptPad_logo_color.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/custom/cryptopad_custom/customize/CryptPadlogo_op5.svg b/custom/cryptopad_custom/customize/CryptPadlogo_op5.svg
new file mode 100644
index 0000000..aa5acb2
--- /dev/null
+++ b/custom/cryptopad_custom/customize/CryptPadlogo_op5.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/custom/cryptopad_custom/customize/about.html b/custom/cryptopad_custom/customize/about.html
new file mode 100644
index 0000000..31d4c99
--- /dev/null
+++ b/custom/cryptopad_custom/customize/about.html
@@ -0,0 +1,16 @@
+
+
+
+
+ CryptPad: Zero Knowledge, Collaborative Real Time Editing
+
+
+
+
+
+
+
+
diff --git a/custom/cryptopad_custom/customize/alt-favicon.png b/custom/cryptopad_custom/customize/alt-favicon.png
new file mode 100644
index 0000000..2375368
Binary files /dev/null and b/custom/cryptopad_custom/customize/alt-favicon.png differ
diff --git a/custom/cryptopad_custom/customize/application_config.js b/custom/cryptopad_custom/customize/application_config.js
new file mode 100644
index 0000000..fb4e848
--- /dev/null
+++ b/custom/cryptopad_custom/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;
+});
+
diff --git a/custom/cryptopad_custom/customize/bg14.jpg b/custom/cryptopad_custom/customize/bg14.jpg
new file mode 100644
index 0000000..1d8192e
Binary files /dev/null and b/custom/cryptopad_custom/customize/bg14.jpg differ
diff --git a/custom/cryptopad_custom/customize/bkabout.jpg b/custom/cryptopad_custom/customize/bkabout.jpg
new file mode 100644
index 0000000..d943460
Binary files /dev/null and b/custom/cryptopad_custom/customize/bkabout.jpg differ
diff --git a/custom/cryptopad_custom/customize/bkregister.jpg b/custom/cryptopad_custom/customize/bkregister.jpg
new file mode 100644
index 0000000..a67a3b0
Binary files /dev/null and b/custom/cryptopad_custom/customize/bkregister.jpg differ
diff --git a/custom/cryptopad_custom/customize/bkwhat.jpg b/custom/cryptopad_custom/customize/bkwhat.jpg
new file mode 100644
index 0000000..90a2841
Binary files /dev/null and b/custom/cryptopad_custom/customize/bkwhat.jpg differ
diff --git a/custom/cryptopad_custom/customize/ckeditor-config.js b/custom/cryptopad_custom/customize/ckeditor-config.js
new file mode 100644
index 0000000..7c44114
--- /dev/null
+++ b/custom/cryptopad_custom/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)); };
+}());
diff --git a/custom/cryptopad_custom/customize/ckeditor-contents.css b/custom/cryptopad_custom/customize/ckeditor-contents.css
new file mode 100644
index 0000000..4d2abae
--- /dev/null
+++ b/custom/cryptopad_custom/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;
+}
diff --git a/custom/cryptopad_custom/customize/code.svg b/custom/cryptopad_custom/customize/code.svg
new file mode 100644
index 0000000..29647b5
--- /dev/null
+++ b/custom/cryptopad_custom/customize/code.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/custom/cryptopad_custom/customize/config.js b/custom/cryptopad_custom/customize/config.js
new file mode 100644
index 0000000..3a06b30
--- /dev/null
+++ b/custom/cryptopad_custom/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/.sock`
+ * If you run multiple cryptpad servers, you need to use different
+ * repl names.
+ */
+ //debugReplName: "cryptpad"
+};
diff --git a/custom/cryptopad_custom/customize/contact.html b/custom/cryptopad_custom/customize/contact.html
new file mode 100644
index 0000000..31d4c99
--- /dev/null
+++ b/custom/cryptopad_custom/customize/contact.html
@@ -0,0 +1,16 @@
+
+
+
+
+ CryptPad: Zero Knowledge, Collaborative Real Time Editing
+
+
+
+
+
+
+
+
diff --git a/custom/cryptopad_custom/customize/credential.js b/custom/cryptopad_custom/customize/credential.js
new file mode 100644
index 0000000..d1d88bd
--- /dev/null
+++ b/custom/cryptopad_custom/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;
+});
diff --git a/custom/cryptopad_custom/customize/cryptofist_mini.png b/custom/cryptopad_custom/customize/cryptofist_mini.png
new file mode 100644
index 0000000..73845e4
Binary files /dev/null and b/custom/cryptopad_custom/customize/cryptofist_mini.png differ
diff --git a/custom/cryptopad_custom/customize/cryptofist_small.png b/custom/cryptopad_custom/customize/cryptofist_small.png
new file mode 100644
index 0000000..9c818eb
Binary files /dev/null and b/custom/cryptopad_custom/customize/cryptofist_small.png differ
diff --git a/custom/cryptopad_custom/customize/cryptpad-new-logo-colors-logoonly.png b/custom/cryptopad_custom/customize/cryptpad-new-logo-colors-logoonly.png
new file mode 100644
index 0000000..722438d
Binary files /dev/null and b/custom/cryptopad_custom/customize/cryptpad-new-logo-colors-logoonly.png differ
diff --git a/custom/cryptopad_custom/customize/delta-words.js b/custom/cryptopad_custom/customize/delta-words.js
new file mode 100644
index 0000000..4eec0d0
--- /dev/null
+++ b/custom/cryptopad_custom/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;
+});
diff --git a/custom/cryptopad_custom/customize/error.html b/custom/cryptopad_custom/customize/error.html
new file mode 100644
index 0000000..36cff7a
--- /dev/null
+++ b/custom/cryptopad_custom/customize/error.html
@@ -0,0 +1 @@
+ERROR 42: porcodio
diff --git a/custom/cryptopad_custom/customize/faq.html b/custom/cryptopad_custom/customize/faq.html
new file mode 100644
index 0000000..d485505
--- /dev/null
+++ b/custom/cryptopad_custom/customize/faq.html
@@ -0,0 +1,17 @@
+
+
+
+
+ CryptPad: Zero Knowledge, Collaborative Real Time Editing
+
+
+
+
+
+
+
+
+
diff --git a/custom/cryptopad_custom/customize/features.html b/custom/cryptopad_custom/customize/features.html
new file mode 100644
index 0000000..31d4c99
--- /dev/null
+++ b/custom/cryptopad_custom/customize/features.html
@@ -0,0 +1,16 @@
+
+
+
+
+ CryptPad: Zero Knowledge, Collaborative Real Time Editing
+
+
+
+
+
+
+
+
diff --git a/custom/cryptopad_custom/customize/fonts/cptools/fonts/cptools.svg b/custom/cryptopad_custom/customize/fonts/cptools/fonts/cptools.svg
new file mode 100644
index 0000000..b283937
--- /dev/null
+++ b/custom/cryptopad_custom/customize/fonts/cptools/fonts/cptools.svg
@@ -0,0 +1,26 @@
+
+
+
\ No newline at end of file
diff --git a/custom/cryptopad_custom/customize/fonts/cptools/fonts/cptools.ttf b/custom/cryptopad_custom/customize/fonts/cptools/fonts/cptools.ttf
new file mode 100644
index 0000000..3dfa366
Binary files /dev/null and b/custom/cryptopad_custom/customize/fonts/cptools/fonts/cptools.ttf differ
diff --git a/custom/cryptopad_custom/customize/fonts/cptools/fonts/cptools.woff b/custom/cryptopad_custom/customize/fonts/cptools/fonts/cptools.woff
new file mode 100644
index 0000000..93fc56f
Binary files /dev/null and b/custom/cryptopad_custom/customize/fonts/cptools/fonts/cptools.woff differ
diff --git a/custom/cryptopad_custom/customize/fonts/cptools/style.css b/custom/cryptopad_custom/customize/fonts/cptools/style.css
new file mode 100644
index 0000000..af5b4c4
--- /dev/null
+++ b/custom/cryptopad_custom/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";
+}
diff --git a/custom/cryptopad_custom/customize/fonts/lato/Lato-Black.ttf b/custom/cryptopad_custom/customize/fonts/lato/Lato-Black.ttf
new file mode 100644
index 0000000..6848db0
Binary files /dev/null and b/custom/cryptopad_custom/customize/fonts/lato/Lato-Black.ttf differ
diff --git a/custom/cryptopad_custom/customize/fonts/lato/Lato-Italic.ttf b/custom/cryptopad_custom/customize/fonts/lato/Lato-Italic.ttf
new file mode 100644
index 0000000..3d3b7a2
Binary files /dev/null and b/custom/cryptopad_custom/customize/fonts/lato/Lato-Italic.ttf differ
diff --git a/custom/cryptopad_custom/customize/fonts/lato/Lato-Regular.ttf b/custom/cryptopad_custom/customize/fonts/lato/Lato-Regular.ttf
new file mode 100644
index 0000000..04ea8ef
Binary files /dev/null and b/custom/cryptopad_custom/customize/fonts/lato/Lato-Regular.ttf differ
diff --git a/custom/cryptopad_custom/customize/fonts/lato/METADATA.json b/custom/cryptopad_custom/customize/fonts/lato/METADATA.json
new file mode 100644
index 0000000..31213e2
--- /dev/null
+++ b/custom/cryptopad_custom/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"
+}
diff --git a/custom/cryptopad_custom/customize/fonts/lato/OFL.txt b/custom/cryptopad_custom/customize/fonts/lato/OFL.txt
new file mode 100644
index 0000000..98383e3
--- /dev/null
+++ b/custom/cryptopad_custom/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.
diff --git a/custom/cryptopad_custom/customize/fonts/neuropolitical.ttf b/custom/cryptopad_custom/customize/fonts/neuropolitical.ttf
new file mode 100644
index 0000000..26bbb63
Binary files /dev/null and b/custom/cryptopad_custom/customize/fonts/neuropolitical.ttf differ
diff --git a/custom/cryptopad_custom/customize/fonts/open-sans.less b/custom/cryptopad_custom/customize/fonts/open-sans.less
new file mode 100644
index 0000000..7a1bcbb
--- /dev/null
+++ b/custom/cryptopad_custom/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 */
diff --git a/custom/cryptopad_custom/customize/four-oh-four.js b/custom/cryptopad_custom/customize/four-oh-four.js
new file mode 100644
index 0000000..bcae8b4
--- /dev/null
+++ b/custom/cryptopad_custom/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');
+ })();
+});
+
diff --git a/custom/cryptopad_custom/customize/images/AGPL.png b/custom/cryptopad_custom/customize/images/AGPL.png
new file mode 100644
index 0000000..c3db548
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/AGPL.png differ
diff --git a/custom/cryptopad_custom/customize/images/AaronMacSween.jpg b/custom/cryptopad_custom/customize/images/AaronMacSween.jpg
new file mode 100644
index 0000000..95afcda
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/AaronMacSween.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/CalebJames.jpg b/custom/cryptopad_custom/customize/images/CalebJames.jpg
new file mode 100644
index 0000000..0e29dd1
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/CalebJames.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/Catalin.jpg b/custom/cryptopad_custom/customize/images/Catalin.jpg
new file mode 100644
index 0000000..508c069
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/Catalin.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/LudovicDuboist.jpg b/custom/cryptopad_custom/customize/images/LudovicDuboist.jpg
new file mode 100644
index 0000000..09bf4af
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/LudovicDuboist.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/Pierre-new.jpg b/custom/cryptopad_custom/customize/images/Pierre-new.jpg
new file mode 100644
index 0000000..c8dafbe
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/Pierre-new.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/YannFlory.jpg b/custom/cryptopad_custom/customize/images/YannFlory.jpg
new file mode 100644
index 0000000..9c358d1
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/YannFlory.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/aaron.jpg b/custom/cryptopad_custom/customize/images/aaron.jpg
new file mode 100644
index 0000000..f443f44
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/aaron.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/atest.jpg b/custom/cryptopad_custom/customize/images/atest.jpg
new file mode 100644
index 0000000..a6d6e0f
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/atest.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/avatar.png b/custom/cryptopad_custom/customize/images/avatar.png
new file mode 100644
index 0000000..f6ee7d7
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/avatar.png differ
diff --git a/custom/cryptopad_custom/customize/images/bkcontact.jpg b/custom/cryptopad_custom/customize/images/bkcontact.jpg
new file mode 100644
index 0000000..bae61ba
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/bkcontact.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/caleb.jpg b/custom/cryptopad_custom/customize/images/caleb.jpg
new file mode 100644
index 0000000..415ff0a
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/caleb.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/code.png b/custom/cryptopad_custom/customize/images/code.png
new file mode 100644
index 0000000..3c57fd5
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/code.png differ
diff --git a/custom/cryptopad_custom/customize/images/cover-faq.jpg b/custom/cryptopad_custom/customize/images/cover-faq.jpg
new file mode 100644
index 0000000..27be3af
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/cover-faq.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/cover-features.jpg b/custom/cryptopad_custom/customize/images/cover-features.jpg
new file mode 100644
index 0000000..6652c9b
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/cover-features.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/cover-privacy.jpg b/custom/cryptopad_custom/customize/images/cover-privacy.jpg
new file mode 100644
index 0000000..ff873c9
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/cover-privacy.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/drive_screenshot.png b/custom/cryptopad_custom/customize/images/drive_screenshot.png
new file mode 100644
index 0000000..74f89c0
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/drive_screenshot.png differ
diff --git a/custom/cryptopad_custom/customize/images/email.svg b/custom/cryptopad_custom/customize/images/email.svg
new file mode 100644
index 0000000..e2e566e
--- /dev/null
+++ b/custom/cryptopad_custom/customize/images/email.svg
@@ -0,0 +1,25 @@
+
+
+
diff --git a/custom/cryptopad_custom/customize/images/github.svg b/custom/cryptopad_custom/customize/images/github.svg
new file mode 100644
index 0000000..16cd687
--- /dev/null
+++ b/custom/cryptopad_custom/customize/images/github.svg
@@ -0,0 +1,22 @@
+
+
+
diff --git a/custom/cryptopad_custom/customize/images/hash.png b/custom/cryptopad_custom/customize/images/hash.png
new file mode 100644
index 0000000..474f381
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/hash.png differ
diff --git a/custom/cryptopad_custom/customize/images/icons/folder.svg b/custom/cryptopad_custom/customize/images/icons/folder.svg
new file mode 100644
index 0000000..07d26e2
--- /dev/null
+++ b/custom/cryptopad_custom/customize/images/icons/folder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/custom/cryptopad_custom/customize/images/icons/folderOpen.svg b/custom/cryptopad_custom/customize/images/icons/folderOpen.svg
new file mode 100644
index 0000000..560582c
--- /dev/null
+++ b/custom/cryptopad_custom/customize/images/icons/folderOpen.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/custom/cryptopad_custom/customize/images/icons/ic_folder_black_24px.svg b/custom/cryptopad_custom/customize/images/icons/ic_folder_black_24px.svg
new file mode 100644
index 0000000..91729fe
--- /dev/null
+++ b/custom/cryptopad_custom/customize/images/icons/ic_folder_black_24px.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/custom/cryptopad_custom/customize/images/icons/ic_folder_open_black_24px.svg b/custom/cryptopad_custom/customize/images/icons/ic_folder_open_black_24px.svg
new file mode 100644
index 0000000..2e859c8
--- /dev/null
+++ b/custom/cryptopad_custom/customize/images/icons/ic_folder_open_black_24px.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/custom/cryptopad_custom/customize/images/irc.svg b/custom/cryptopad_custom/customize/images/irc.svg
new file mode 100644
index 0000000..58a9e70
--- /dev/null
+++ b/custom/cryptopad_custom/customize/images/irc.svg
@@ -0,0 +1,30 @@
+
+
+
diff --git a/custom/cryptopad_custom/customize/images/issue.svg b/custom/cryptopad_custom/customize/images/issue.svg
new file mode 100644
index 0000000..27d5276
--- /dev/null
+++ b/custom/cryptopad_custom/customize/images/issue.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/custom/cryptopad_custom/customize/images/key_small.png b/custom/cryptopad_custom/customize/images/key_small.png
new file mode 100644
index 0000000..bbf2673
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/key_small.png differ
diff --git a/custom/cryptopad_custom/customize/images/logo_white.png b/custom/cryptopad_custom/customize/images/logo_white.png
new file mode 100644
index 0000000..8219cc2
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/logo_white.png differ
diff --git a/custom/cryptopad_custom/customize/images/logo_white.svg b/custom/cryptopad_custom/customize/images/logo_white.svg
new file mode 100644
index 0000000..d1cd2bc
--- /dev/null
+++ b/custom/cryptopad_custom/customize/images/logo_white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/custom/cryptopad_custom/customize/images/ludovic.jpg b/custom/cryptopad_custom/customize/images/ludovic.jpg
new file mode 100644
index 0000000..02842ee
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/ludovic.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/ngi.png b/custom/cryptopad_custom/customize/images/ngi.png
new file mode 100644
index 0000000..15c6f24
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/ngi.png differ
diff --git a/custom/cryptopad_custom/customize/images/organize.png b/custom/cryptopad_custom/customize/images/organize.png
new file mode 100644
index 0000000..c741733
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/organize.png differ
diff --git a/custom/cryptopad_custom/customize/images/pad.png b/custom/cryptopad_custom/customize/images/pad.png
new file mode 100644
index 0000000..184b0e7
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/pad.png differ
diff --git a/custom/cryptopad_custom/customize/images/pad_screenshot.png b/custom/cryptopad_custom/customize/images/pad_screenshot.png
new file mode 100644
index 0000000..cbb1556
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/pad_screenshot.png differ
diff --git a/custom/cryptopad_custom/customize/images/pierre.jpg b/custom/cryptopad_custom/customize/images/pierre.jpg
new file mode 100644
index 0000000..79f5144
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/pierre.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/poll.png b/custom/cryptopad_custom/customize/images/poll.png
new file mode 100644
index 0000000..6fc6291
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/poll.png differ
diff --git a/custom/cryptopad_custom/customize/images/realtime_small.png b/custom/cryptopad_custom/customize/images/realtime_small.png
new file mode 100644
index 0000000..7cac495
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/realtime_small.png differ
diff --git a/custom/cryptopad_custom/customize/images/sayhi.svg b/custom/cryptopad_custom/customize/images/sayhi.svg
new file mode 100644
index 0000000..22c7463
--- /dev/null
+++ b/custom/cryptopad_custom/customize/images/sayhi.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/custom/cryptopad_custom/customize/images/slide.png b/custom/cryptopad_custom/customize/images/slide.png
new file mode 100644
index 0000000..2ec3066
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/slide.png differ
diff --git a/custom/cryptopad_custom/customize/images/twitter.svg b/custom/cryptopad_custom/customize/images/twitter.svg
new file mode 100644
index 0000000..bed259b
--- /dev/null
+++ b/custom/cryptopad_custom/customize/images/twitter.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/custom/cryptopad_custom/customize/images/useraccount.png b/custom/cryptopad_custom/customize/images/useraccount.png
new file mode 100644
index 0000000..22209ac
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/useraccount.png differ
diff --git a/custom/cryptopad_custom/customize/images/yann.jpg b/custom/cryptopad_custom/customize/images/yann.jpg
new file mode 100644
index 0000000..ed381e8
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/yann.jpg differ
diff --git a/custom/cryptopad_custom/customize/images/zeroknowledge_small.png b/custom/cryptopad_custom/customize/images/zeroknowledge_small.png
new file mode 100644
index 0000000..dcef74f
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/zeroknowledge_small.png differ
diff --git a/custom/cryptopad_custom/customize/images/zk.png b/custom/cryptopad_custom/customize/images/zk.png
new file mode 100644
index 0000000..a0444d7
Binary files /dev/null and b/custom/cryptopad_custom/customize/images/zk.png differ
diff --git a/custom/cryptopad_custom/customize/index.html b/custom/cryptopad_custom/customize/index.html
new file mode 100644
index 0000000..0034be5
--- /dev/null
+++ b/custom/cryptopad_custom/customize/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+ CryptPad: Zero Knowledge, Collaborative Real Time Editing
+
+
+
+
+
+
+
+
diff --git a/custom/cryptopad_custom/customize/loading-logo.png b/custom/cryptopad_custom/customize/loading-logo.png
new file mode 100644
index 0000000..2375368
Binary files /dev/null and b/custom/cryptopad_custom/customize/loading-logo.png differ
diff --git a/custom/cryptopad_custom/customize/loading.js b/custom/cryptopad_custom/customize/loading.js
new file mode 100644
index 0000000..e8f8a94
--- /dev/null
+++ b/custom/cryptopad_custom/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 = [
+ '',
+ '
',
+ '',
+ '
',
+ '
',
+ '
',
+ '',
+ '
',
+ '',
+ '
'
+ ].join('');
+ return function () {
+ var intr;
+ var append = function () {
+ if (!document.body) { return; }
+ clearInterval(intr);
+ document.body.appendChild(elem);
+ };
+ intr = setInterval(append, 100);
+ append();
+ };
+});
diff --git a/custom/cryptopad_custom/customize/login.js b/custom/cryptopad_custom/customize/login.js
new file mode 100644
index 0000000..2166814
--- /dev/null
+++ b/custom/cryptopad_custom/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;
+});
diff --git a/custom/cryptopad_custom/customize/main-favicon.png b/custom/cryptopad_custom/customize/main-favicon.png
new file mode 100644
index 0000000..212975b
Binary files /dev/null and b/custom/cryptopad_custom/customize/main-favicon.png differ
diff --git a/custom/cryptopad_custom/customize/main.js b/custom/cryptopad_custom/customize/main.js
new file mode 100644
index 0000000..44ef51d
--- /dev/null
+++ b/custom/cryptopad_custom/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();
+ });
+ });
+});
diff --git a/custom/cryptopad_custom/customize/messages.js b/custom/cryptopad_custom/customize/messages.js
new file mode 100755
index 0000000..e3db0f1
--- /dev/null
+++ b/custom/cryptopad_custom/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;
+
+});
+}());
diff --git a/custom/cryptopad_custom/customize/pages.js b/custom/cryptopad_custom/customize/pages.js
new file mode 100644
index 0000000..a7b04ba
--- /dev/null
+++ b/custom/cryptopad_custom/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;
+});
diff --git a/custom/cryptopad_custom/customize/pages/about.js b/custom/cryptopad_custom/customize/pages/about.js
new file mode 100644
index 0000000..6410deb
--- /dev/null
+++ b/custom/cryptopad_custom/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'), '
Caleb is a cryptography developer, Machine Technology graduate of the Franklin County Technical School and lifelong tinkerer. In 2011, he started the cjdns Open Source project to show that secure networking could be invisible and easily deployed. After joining XWiki SAS in 2014, he started the CryptPad project with the intent of bringing the same transparent security to collaborative editing. 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!".
Aaron transitioned into distributed systems development from a background in jazz and live stage performance. 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. 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. He spends his spare time experimenting with guitars, photography, science fiction, and spicy food.
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. 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. Yann is soft spoken but brutally efficient, he is known to say "It will take 5 minutes".
Resident CSS wizard and emoji extraordinaire, Pierre is passionate about anything related to technology. He loves to hack around computers and put parts together. He is currently studying at 42, where he learns about algorithms, networking, kernel programming and graphics. As a part of an internship, he joined XWiki SAS and worked on CryptPad to improve user experience. He also maintains the Spanish translation.
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. 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.
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. 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.
");n.customDirectionNav?n.directionNav=n.customDirectionNav:n.controlsContainer?($(n.controlsContainer).append(e),n.directionNav=$("."+i+"direction-nav li a",n.controlsContainer)):(n.append(e),n.directionNav=$("."+i+"direction-nav li a",n)),f.directionNav.update(),n.directionNav.bind(o,function(e){e.preventDefault();var t;(""===l||l===e.type)&&(t=$(this).hasClass(i+"next")?n.getTarget("next"):n.getTarget("prev"),n.flexAnimate(t,n.vars.pauseOnAction)),""===l&&(l=e.type),f.setToClearWatchedEvent()})},update:function(){var e=i+"disabled";1===n.pagingCount?n.directionNav.addClass(e).attr("tabindex","-1"):n.vars.animationLoop?n.directionNav.removeClass(e).removeAttr("tabindex"):0===n.animatingTo?n.directionNav.removeClass(e).filter("."+i+"prev").addClass(e).attr("tabindex","-1"):n.animatingTo===n.last?n.directionNav.removeClass(e).filter("."+i+"next").addClass(e).attr("tabindex","-1"):n.directionNav.removeClass(e).removeAttr("tabindex")}},pausePlay:{setup:function(){var e=$('
"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1>$2>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1>$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("")).appendTo(b.documentElement),b=qa[0].contentDocument,b.write(),b.close(),c=sa(a,b),qa.detach()),ra[a]=c),c}var ua=/^margin/,va=new RegExp("^("+Q+")(?!px)[a-z%]+$","i"),wa=function(b){return b.ownerDocument.defaultView.opener?b.ownerDocument.defaultView.getComputedStyle(b,null):a.getComputedStyle(b,null)};function xa(a,b,c){var d,e,f,g,h=a.style;return c=c||wa(a),c&&(g=c.getPropertyValue(b)||c[b]),c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),va.test(g)&&ua.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function ya(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d=l.documentElement,e=l.createElement("div"),f=l.createElement("div");if(f.style){f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===f.style.backgroundClip,e.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",e.appendChild(f);function g(){f.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",f.innerHTML="",d.appendChild(e);var g=a.getComputedStyle(f,null);b="1%"!==g.top,c="4px"===g.width,d.removeChild(e)}a.getComputedStyle&&n.extend(k,{pixelPosition:function(){return g(),b},boxSizingReliable:function(){return null==c&&g(),c},reliableMarginRight:function(){var b,c=f.appendChild(l.createElement("div"));return c.style.cssText=f.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",c.style.marginRight=c.style.width="0",f.style.width="1px",d.appendChild(e),b=!parseFloat(a.getComputedStyle(c,null).marginRight),d.removeChild(e),f.removeChild(c),b}})}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var za=/^(none|table(?!-c[ea]).+)/,Aa=new RegExp("^("+Q+")(.*)$","i"),Ba=new RegExp("^([+-])=("+Q+")","i"),Ca={position:"absolute",visibility:"hidden",display:"block"},Da={letterSpacing:"0",fontWeight:"400"},Ea=["Webkit","O","Moz","ms"];function Fa(a,b){if(b in a)return b;var c=b[0].toUpperCase()+b.slice(1),d=b,e=Ea.length;while(e--)if(b=Ea[e]+c,b in a)return b;return d}function Ga(a,b,c){var d=Aa.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Ha(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+R[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+R[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+R[f]+"Width",!0,e))):(g+=n.css(a,"padding"+R[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+R[f]+"Width",!0,e)));return g}function Ia(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=wa(a),g="border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=xa(a,b,f),(0>e||null==e)&&(e=a.style[b]),va.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Ha(a,b,c||(g?"border":"content"),d,f)+"px"}function Ja(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=L.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&S(d)&&(f[g]=L.access(d,"olddisplay",ta(d.nodeName)))):(e=S(d),"none"===c&&e||L.set(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=xa(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;return b=n.cssProps[h]||(n.cssProps[h]=Fa(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=Ba.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Fa(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=xa(a,b,d)),"normal"===e&&b in Da&&(e=Da[b]),""===c||c?(f=parseFloat(e),c===!0||n.isNumeric(f)?f||0:e):e}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?za.test(n.css(a,"display"))&&0===a.offsetWidth?n.swap(a,Ca,function(){return Ia(a,b,d)}):Ia(a,b,d):void 0},set:function(a,c,d){var e=d&&wa(a);return Ga(a,c,d?Ha(a,b,d,"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),n.cssHooks.marginRight=ya(k.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},xa,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+R[d]+b]=f[d]||f[d-2]||f[0];return e}},ua.test(a)||(n.cssHooks[a+b].set=Ga)}),n.fn.extend({css:function(a,b){return J(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=wa(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return Ja(this,!0)},hide:function(){return Ja(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){S(this)?n(this).show():n(this).hide()})}});function Ka(a,b,c,d,e){return new Ka.prototype.init(a,b,c,d,e)}n.Tween=Ka,Ka.prototype={constructor:Ka,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=Ka.propHooks[this.prop];return a&&a.get?a.get(this):Ka.propHooks._default.get(this)},run:function(a){var b,c=Ka.propHooks[this.prop];return this.options.duration?this.pos=b=n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ka.propHooks._default.set(this),this}},Ka.prototype.init.prototype=Ka.prototype,Ka.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Ka.propHooks.scrollTop=Ka.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=Ka.prototype.init,n.fx.step={};var La,Ma,Na=/^(?:toggle|show|hide)$/,Oa=new RegExp("^(?:([+-])=|)("+Q+")([a-z%]*)$","i"),Pa=/queueHooks$/,Qa=[Va],Ra={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=Oa.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&Oa.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function Sa(){return setTimeout(function(){La=void 0}),La=n.now()}function Ta(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=R[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function Ua(a,b,c){for(var d,e=(Ra[b]||[]).concat(Ra["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function Va(a,b,c){var d,e,f,g,h,i,j,k,l=this,m={},o=a.style,p=a.nodeType&&S(a),q=L.get(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,l.always(function(){l.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=n.css(a,"display"),k="none"===j?L.get(a,"olddisplay")||ta(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(o.display="inline-block")),c.overflow&&(o.overflow="hidden",l.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],Na.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}m[d]=q&&q[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(m))"inline"===("none"===j?ta(a.nodeName):j)&&(o.display=j);else{q?"hidden"in q&&(p=q.hidden):q=L.access(a,"fxshow",{}),f&&(q.hidden=!p),p?n(a).show():l.done(function(){n(a).hide()}),l.done(function(){var b;L.remove(a,"fxshow");for(b in m)n.style(a,b,m[b])});for(d in m)g=Ua(p?q[d]:0,d,l),d in q||(q[d]=g.start,p&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function Wa(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function Xa(a,b,c){var d,e,f=0,g=Qa.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=La||Sa(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:La||Sa(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(Wa(k,j.opts.specialEasing);g>f;f++)if(d=Qa[f].call(j,a,k,j.opts))return d;return n.map(k,Ua,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(Xa,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],Ra[c]=Ra[c]||[],Ra[c].unshift(b)},prefilter:function(a,b){b?Qa.unshift(a):Qa.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(S).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=Xa(this,n.extend({},a),f);(e||L.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=L.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&Pa.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=L.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(Ta(b,!0),a,d,e)}}),n.each({slideDown:Ta("show"),slideUp:Ta("hide"),slideToggle:Ta("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=0,c=n.timers;for(La=n.now();b1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===U?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?Za:Ya)),
+void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Za={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$a[b]||n.find.attr;$a[b]=function(a,b,d){var e,f;return d||(f=$a[b],$a[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$a[b]=f),e}});var _a=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_a.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ab=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ab," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ab," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ab," ").indexOf(b)>=0)return!0;return!1}});var bb=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bb,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(d.value,f)>=0)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},k.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var cb=n.now(),db=/\?/;n.parseJSON=function(a){return JSON.parse(a+"")},n.parseXML=function(a){var b,c;if(!a||"string"!=typeof a)return null;try{c=new DOMParser,b=c.parseFromString(a,"text/xml")}catch(d){b=void 0}return(!b||b.getElementsByTagName("parsererror").length)&&n.error("Invalid XML: "+a),b};var eb=/#.*$/,fb=/([?&])_=[^&]*/,gb=/^(.*?):[ \t]*([^\r\n]*)$/gm,hb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,ib=/^(?:GET|HEAD)$/,jb=/^\/\//,kb=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,lb={},mb={},nb="*/".concat("*"),ob=a.location.href,pb=kb.exec(ob.toLowerCase())||[];function qb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(n.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function rb(a,b,c,d){var e={},f=a===mb;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function sb(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&n.extend(!0,a,d),a}function tb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function ub(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:ob,type:"GET",isLocal:hb.test(pb[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":nb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?sb(sb(a,n.ajaxSettings),b):sb(n.ajaxSettings,a)},ajaxPrefilter:qb(lb),ajaxTransport:qb(mb),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!f){f={};while(b=gb.exec(e))f[b[1].toLowerCase()]=b[2]}b=f[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?e:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return c&&c.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||ob)+"").replace(eb,"").replace(jb,pb[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(h=kb.exec(k.url.toLowerCase()),k.crossDomain=!(!h||h[1]===pb[1]&&h[2]===pb[2]&&(h[3]||("http:"===h[1]?"80":"443"))===(pb[3]||("http:"===pb[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),rb(lb,k,b,v),2===t)return v;i=n.event&&k.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!ib.test(k.type),d=k.url,k.hasContent||(k.data&&(d=k.url+=(db.test(d)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=fb.test(d)?d.replace(fb,"$1_="+cb++):d+(db.test(d)?"&":"?")+"_="+cb++)),k.ifModified&&(n.lastModified[d]&&v.setRequestHeader("If-Modified-Since",n.lastModified[d]),n.etag[d]&&v.setRequestHeader("If-None-Match",n.etag[d])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+nb+"; q=0.01":""):k.accepts["*"]);for(j in k.headers)v.setRequestHeader(j,k.headers[j]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(j in{success:1,error:1,complete:1})v[j](k[j]);if(c=rb(mb,k,b,v)){v.readyState=1,i&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,c.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,f,h){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),c=void 0,e=h||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,f&&(u=tb(k,v,f)),u=ub(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[d]=w),w=v.getResponseHeader("etag"),w&&(n.etag[d]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,i&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),i&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){var b;return n.isFunction(a)?this.each(function(b){n(this).wrapAll(a.call(this,b))}):(this[0]&&(b=n(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this)},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var vb=/%20/g,wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&").replace(vb,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!T.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}}),n.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var Bb=0,Cb={},Db={0:200,1223:204},Eb=n.ajaxSettings.xhr();a.attachEvent&&a.attachEvent("onunload",function(){for(var a in Cb)Cb[a]()}),k.cors=!!Eb&&"withCredentials"in Eb,k.ajax=Eb=!!Eb,n.ajaxTransport(function(a){var b;return k.cors||Eb&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++Bb;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete Cb[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(Db[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=Cb[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=n("
+
+
+
+
diff --git a/custom/cryptopad_custom/customize/testimonial.svg b/custom/cryptopad_custom/customize/testimonial.svg
new file mode 100644
index 0000000..4c1b007
--- /dev/null
+++ b/custom/cryptopad_custom/customize/testimonial.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/custom/cryptopad_custom/customize/translations/README.md b/custom/cryptopad_custom/customize/translations/README.md
new file mode 100644
index 0000000..05ddc2b
--- /dev/null
+++ b/custom/cryptopad_custom/customize/translations/README.md
@@ -0,0 +1,29 @@
+# Translations
+
+Translations can now be made using [Weblate](https://weblate.cryptpad.fr). We may still accept PRs for the internal translation files, but we won't provide support for this.
+
+## Request a new language
+
+* New languages require some minor changes in the code which we have to apply manually. If you want to translate CryptPad into a new language, please send us an email at weblate@cryptpad.fr
+* Please note that members of the current development team are only fluent in English and French, so additional languages will require ongoing contributions in order to stay up to date.
+ * If a language becomes too outdated, we will consider dropping it from the available language until it is fixed.
+ * Please refrain from asking for a new language if you are not able to provide a sufficiently translated version and maintain it or to find other users that can do so.
+
+## Become a reviewer
+
+* We need reviewers for all the languages except English and French. Their job will be to approve/reject/discuss the translations made by other contributors to this language, using the Weblate UI.
+* If you want to become a reviewer for a language, please write us an email at weblate@cryptpad.fr
+* Please tell us if you want to stop being a reviewer (even momentarily) so that we can find a replacement as soon as posible
+
+## Translate an existing language
+
+* All translations can be done using the Weblate UI. For better help about how to use the tool, please check the [Weblate documentation](https://docs.weblate.org/en/latest/user/index.html).
+* Our Weblate instance is configured to always require approval for changes.
+
+### Update an existing translation
+
+* Existing translations that have already been approved can't be changed directly. Users can only make suggestions, and the reviewers will have to approve the changes if they agree.
+
+### Translate a new key
+
+* Untranslated strings can be changed by any user and the translation can be edited by others as long as it has not been approved by a reviewer.
diff --git a/custom/cryptopad_custom/customize/translations/messages.de.js b/custom/cryptopad_custom/customize/translations/messages.de.js
new file mode 100644
index 0000000..d7bc77b
--- /dev/null
+++ b/custom/cryptopad_custom/customize/translations/messages.de.js
@@ -0,0 +1,14 @@
+/*
+ * You can override the translation text using this file.
+ * The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
+ in a 'customize' directory (/customize/translations/messages.{LANG}.js).
+ * If you want to check all the existing translation keys, you can open the internal language file
+ but you should not change it directly (/common/translations/messages.{LANG}.js)
+*/
+define(['/common/translations/messages.de.js'], function (Messages) {
+ // Replace the existing keys in your copied file here:
+ // Messages.button_newpad = "New Rich Text Document";
+
+ return Messages;
+});
+
diff --git a/custom/cryptopad_custom/customize/translations/messages.el.js b/custom/cryptopad_custom/customize/translations/messages.el.js
new file mode 100644
index 0000000..1d27e93
--- /dev/null
+++ b/custom/cryptopad_custom/customize/translations/messages.el.js
@@ -0,0 +1,14 @@
+/*
+ * You can override the translation text using this file.
+ * The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
+ in a 'customize' directory (/customize/translations/messages.{LANG}.js).
+ * If you want to check all the existing translation keys, you can open the internal language file
+ but you should not change it directly (/common/translations/messages.{LANG}.js)
+*/
+define(['/common/translations/messages.el.js'], function (Messages) {
+ // Replace the existing keys in your copied file here:
+ // Messages.button_newpad = "New Rich Text Document";
+
+ return Messages;
+});
+
diff --git a/custom/cryptopad_custom/customize/translations/messages.es.js b/custom/cryptopad_custom/customize/translations/messages.es.js
new file mode 100644
index 0000000..2020371
--- /dev/null
+++ b/custom/cryptopad_custom/customize/translations/messages.es.js
@@ -0,0 +1,14 @@
+/*
+ * You can override the translation text using this file.
+ * The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
+ in a 'customize' directory (/customize/translations/messages.{LANG}.js).
+ * If you want to check all the existing translation keys, you can open the internal language file
+ but you should not change it directly (/common/translations/messages.{LANG}.js)
+*/
+define(['/common/translations/messages.es.js'], function (Messages) {
+ // Replace the existing keys in your copied file here:
+ // Messages.button_newpad = "New Rich Text Document";
+
+ return Messages;
+});
+
diff --git a/custom/cryptopad_custom/customize/translations/messages.fr.js b/custom/cryptopad_custom/customize/translations/messages.fr.js
new file mode 100644
index 0000000..da0e716
--- /dev/null
+++ b/custom/cryptopad_custom/customize/translations/messages.fr.js
@@ -0,0 +1,14 @@
+/*
+ * You can override the translation text using this file.
+ * The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
+ in a 'customize' directory (/customize/translations/messages.{LANG}.js).
+ * If you want to check all the existing translation keys, you can open the internal language file
+ but you should not change it directly (/common/translations/messages.{LANG}.js)
+*/
+define(['/common/translations/messages.fr.js'], function (Messages) {
+ // Replace the existing keys in your copied file here:
+ // Messages.button_newpad = "New Rich Text Document";
+
+ return Messages;
+});
+
diff --git a/custom/cryptopad_custom/customize/translations/messages.js b/custom/cryptopad_custom/customize/translations/messages.js
new file mode 100644
index 0000000..63748f0
--- /dev/null
+++ b/custom/cryptopad_custom/customize/translations/messages.js
@@ -0,0 +1,14 @@
+/*
+ * You can override the translation text using this file.
+ * The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
+ in a 'customize' directory (/customize/translations/messages.{LANG}.js).
+ * If you want to check all the existing translation keys, you can open the internal language file
+ but you should not change it directly (/common/translations/messages.{LANG}.js)
+*/
+define(['/common/translations/messages.js'], function (Messages) {
+ // Replace the existing keys in your copied file here:
+ // Messages.button_newpad = "New Rich Text Document";
+
+ return Messages;
+});
+
diff --git a/custom/cryptopad_custom/customize/translations/messages.pl.js b/custom/cryptopad_custom/customize/translations/messages.pl.js
new file mode 100644
index 0000000..69431f2
--- /dev/null
+++ b/custom/cryptopad_custom/customize/translations/messages.pl.js
@@ -0,0 +1,14 @@
+/*
+ * You can override the translation text using this file.
+ * The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
+ in a 'customize' directory (/customize/translations/messages.{LANG}.js).
+ * If you want to check all the existing translation keys, you can open the internal language file
+ but you should not change it directly (/common/translations/messages.{LANG}.js)
+*/
+define(['/common/translations/messages.pl.js'], function (Messages) {
+ // Replace the existing keys in your copied file here:
+ // Messages.button_newpad = "New Rich Text Document";
+
+ return Messages;
+});
+
diff --git a/custom/cryptopad_custom/customize/translations/messages.pt-br.js b/custom/cryptopad_custom/customize/translations/messages.pt-br.js
new file mode 100644
index 0000000..7c5ce36
--- /dev/null
+++ b/custom/cryptopad_custom/customize/translations/messages.pt-br.js
@@ -0,0 +1,14 @@
+/*
+ * You can override the translation text using this file.
+ * The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
+ in a 'customize' directory (/customize/translations/messages.{LANG}.js).
+ * If you want to check all the existing translation keys, you can open the internal language file
+ but you should not change it directly (/common/translations/messages.{LANG}.js)
+*/
+define(['/common/translations/messages.pt-br.js'], function (Messages) {
+ // Replace the existing keys in your copied file here:
+ // Messages.button_newpad = "New Rich Text Document";
+
+ return Messages;
+});
+
diff --git a/custom/cryptopad_custom/customize/translations/messages.ro.js b/custom/cryptopad_custom/customize/translations/messages.ro.js
new file mode 100644
index 0000000..ad87a80
--- /dev/null
+++ b/custom/cryptopad_custom/customize/translations/messages.ro.js
@@ -0,0 +1,14 @@
+/*
+ * You can override the translation text using this file.
+ * The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
+ in a 'customize' directory (/customize/translations/messages.{LANG}.js).
+ * If you want to check all the existing translation keys, you can open the internal language file
+ but you should not change it directly (/common/translations/messages.{LANG}.js)
+*/
+define(['/common/translations/messages.ro.js'], function (Messages) {
+ // Replace the existing keys in your copied file here:
+ // Messages.button_newpad = "New Rich Text Document";
+
+ return Messages;
+});
+
diff --git a/custom/cryptopad_custom/customize/translations/messages.zh.js b/custom/cryptopad_custom/customize/translations/messages.zh.js
new file mode 100644
index 0000000..fceb731
--- /dev/null
+++ b/custom/cryptopad_custom/customize/translations/messages.zh.js
@@ -0,0 +1,14 @@
+/*
+ * You can override the translation text using this file.
+ * The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
+ in a 'customize' directory (/customize/translations/messages.{LANG}.js).
+ * If you want to check all the existing translation keys, you can open the internal language file
+ but you should not change it directly (/common/translations/messages.{LANG}.js)
+*/
+define(['/common/translations/messages.zh.js'], function (Messages) {
+ // Replace the existing keys in your copied file here:
+ // Messages.button_newpad = "New Rich Text Document";
+
+ return Messages;
+});
+
diff --git a/custom/cryptopad_custom/customize/what-is-cryptpad.html b/custom/cryptopad_custom/customize/what-is-cryptpad.html
new file mode 100644
index 0000000..31d4c99
--- /dev/null
+++ b/custom/cryptopad_custom/customize/what-is-cryptpad.html
@@ -0,0 +1,16 @@
+
+
+
+
+ CryptPad: Zero Knowledge, Collaborative Real Time Editing
+
+
+
+
+
+
+
+