Merge branch 'master' into master

pull/1/head
ansuz 7 years ago committed by GitHub
commit 411c64df85

@ -1,15 +1,10 @@
node_modules/
www/bower_components/
www/code/codemirror*
www/common/chainpad.js
storage/kad.js
www/common/otaml.js
www/common/pdfjs/
server.js
NetFluxWebsocketSrv.js
NetFluxWebsocketServer.js
WebRTCSrv.js
www/common/media-tag.js
www/scratch
www/common/toolbar.js
www/common/hyperscript.js

@ -19,12 +19,12 @@
],
"dependencies": {
"jquery": "~2.1.3",
"tweetnacl": "~0.12.2",
"tweetnacl": "0.12.2",
"components-font-awesome": "^4.6.3",
"ckeditor": "~4.5.6",
"ckeditor": "~4.7",
"codemirror": "^5.19.0",
"requirejs": "~2.1.15",
"marked": "~0.3.5",
"requirejs": "2.1.15",
"marked": "0.3.5",
"rangy": "rangy-release#~1.3.0",
"json.sortify": "~2.1.0",
"secure-fabric.js": "secure-v1.7.9",
@ -33,10 +33,12 @@
"chainpad-json-validator": "^0.2.0",
"chainpad-crypto": "^0.1.3",
"chainpad-listmap": "^0.3.0",
"file-saver": "^1.3.1",
"diff-dom": "^2.1.1",
"alertifyjs": "^1.0.11",
"scrypt-async": "^1.2.0",
"bootstrap": "#v4.0.0-alpha.6"
"file-saver": "1.3.1",
"alertifyjs": "1.0.11",
"scrypt-async": "1.2.0",
"require-css": "0.1.10",
"less": "^2.7.2",
"bootstrap": "#v4.0.0-alpha.6",
"diff-dom": "2.1.1"
}
}

@ -25,7 +25,7 @@ module.exports = {
"default-src 'none'",
"style-src 'unsafe-inline' 'self'",
"script-src 'self'",
"font-src 'self'",
"font-src 'self' data:",
/* 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.
@ -67,7 +67,7 @@ module.exports = {
"connect-src 'self' ws: wss:",
// (insecure remote) images are included by users of the wysiwyg who embed photos in their pads
"img-src *",
"img-src * blob:",
].join('; '),
httpPort: 3000,
@ -277,6 +277,11 @@ module.exports = {
*/
//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
*/

@ -2,123 +2,15 @@
<html class="cp">
<!-- If this file is not called customize.dist/src/template.html, it is generated -->
<head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/customize/main.css" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<script data-bootload="/customize/main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script>
</head>
<body class="html">
<div id="cryptpadTopBar">
<span>
<a class="gotoMain" href="/">
<img src="/customize/cryptofist_mini.png" class="cryptpad-logo" alt="" /> CryptPad
</a>
</span>
<!--<span class="slogan" data-localization="main_slogan"></span>-->
<span id="user-menu" class="right dropdown-bar"></span>
<span id="language-selector" class="right dropdown-bar"></span>
<span class="link right">
<a href="/about.html" data-localization="about">About</a>
</span>
<span class="link right">
<a href="/privacy.html" data-localization="privacy">Privacy</a>
</span>
<span class="link right">
<a href="/terms.html" data-localization="terms">ToS</a>
</span>
<span class="link right">
<a href="/contact.html" data-localization="contact">Contact</a>
</span>
<span class="link right">
<a href="https://blog.cryptpad.fr/" data-localization="blog">Blog</a>
</span>
<span class="link right">
<button id="upgrade" class="upgrade btn buttonSuccess" style="display: none;"></button>
</span>
</div>
<noscript>
<div id="noscriptContainer">
<div class="mainOverlay"></div>
<div id="noscript">
<p>
<strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.
</p>
<hr>
<p>
<strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.
</p>
</div>
</div>
</noscript>
<div id="mainBlock" class="hidden">
<div id="main_other">
<center>
<h1 data-localization="about">About</h1>
</center>
<p data-localization="main_p2"><!-- CkEditor, CodeMirror, Chainpad --></p>
<h2 id="howitworks" data-localization="main_howitworks"></h2>
<p data-localization="main_howitworks_p1"><!-- Operational transform, Nakamoto blockchain, server kept unaware of the content--></p>
</div>
</div>
<footer>
<div class="container">
<div class="row">
<div class="col">
<ul class="list-unstyled">
<li class="title">CryptPad</li>
<li><a href="/about.html" data-localization="about"></a></li>
<li><a href="/terms.html" data-localization="terms"></a></li>
<li><a href="/privacy.html" data-localization="privacy"></a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_applications"><li>
<li><a href="/pad/" data-localization="main_richText"></a></li>
<li><a href="/code/" data-localization="main_code"></a></li>
<li><a href="/slide/" data-localization="main_slide"></a></li>
<li><a href="/poll/" data-localization="main_poll"></a></li>
<li><a href="/drive/" data-localization="main_drive"></a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_aboutUs"><li>
<li><a href="https://blog.cryptpad.fr" target="_blank" data-localization="blog" rel="noopener noreferrer">Blog</a></li>
<li><a href="https://labs.xwiki.com" target="_blank" rel="noopener noreferrer">XWiki Labs</a></li>
<li><a href="http://www.xwiki.com" target="_blank" rel="noopener noreferrer">XWiki SAS</a></li>
<li><a href="https://www.open-paas.org/" target="_blank" rel="noopener noreferrer">OpenPaaS</a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_contact"><li>
<li><a href="https://riot.im/app/#/room/#cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
<li><a href="/contact.html">Email</a></li>
</ul>
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.9.0 (Jackelope)</div>
</footer>
</body>
<p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
<p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
</noscript>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

@ -4,8 +4,8 @@ define(function() {
/* Select the buttons displayed on the main page to create new collaborative sessions
* Existing types : pad, code, poll, slide
*/
config.availablePadTypes = ['drive', 'pad', 'code', 'slide', 'poll', 'whiteboard', 'file'];
config.registeredOnlyTypes = ['file'];
config.availablePadTypes = ['drive', 'pad', 'code', 'slide', 'poll', 'whiteboard', 'file', 'contacts'];
config.registeredOnlyTypes = ['file', 'contacts'];
/* Cryptpad apps use a common API to display notifications to users
* by default, notifications are hidden after 5 seconds

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 121 KiB

@ -8,7 +8,8 @@ CKEDITOR.editorConfig = function( config ) {
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';
config.removePlugins= 'resize,elementspath';
config.resize_enabled= false; //bottom-bar
config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock,justify';
config.toolbarGroups= [
// {"name":"clipboard","groups":["clipboard","undo"]},
@ -50,7 +51,7 @@ CKEDITOR.editorConfig = function( config ) {
//skin: 'moono-cryptpad,/pad/themes/moono-cryptpad/'
//skin: 'flat,/pad/themes/flat/'
//skin: 'moono-lisa,/pad/themes/moono-lisa/'
//config.skin= 'moono-lisa,/pad/themes/moono-lisa/'
//skin: 'moono-dark,/pad/themes/moono-dark/'
//skin: 'office2013,/pad/themes/office2013/'
};

@ -2,120 +2,15 @@
<html class="cp">
<!-- If this file is not called customize.dist/src/template.html, it is generated -->
<head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/customize/main.css" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<script data-bootload="/customize/main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script>
</head>
<body class="html">
<div id="cryptpadTopBar">
<span>
<a class="gotoMain" href="/">
<img src="/customize/cryptofist_mini.png" class="cryptpad-logo" alt="" /> CryptPad
</a>
</span>
<!--<span class="slogan" data-localization="main_slogan"></span>-->
<span id="user-menu" class="right dropdown-bar"></span>
<span id="language-selector" class="right dropdown-bar"></span>
<span class="link right">
<a href="/about.html" data-localization="about">About</a>
</span>
<span class="link right">
<a href="/privacy.html" data-localization="privacy">Privacy</a>
</span>
<span class="link right">
<a href="/terms.html" data-localization="terms">ToS</a>
</span>
<span class="link right">
<a href="/contact.html" data-localization="contact">Contact</a>
</span>
<span class="link right">
<a href="https://blog.cryptpad.fr/" data-localization="blog">Blog</a>
</span>
<span class="link right">
<button id="upgrade" class="upgrade btn buttonSuccess" style="display: none;"></button>
</span>
</div>
<noscript>
<div id="noscriptContainer">
<div class="mainOverlay"></div>
<div id="noscript">
<p>
<strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.
</p>
<hr>
<p>
<strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.
</p>
</div>
</div>
</noscript>
<div id="mainBlock" class="hidden">
<div id="main_other">
<center>
<h1 data-localization="contact">Contact</h1>
</center>
<p data-localization="main_about_p2"><!-- Contact us--></p>
</div>
</div>
<footer>
<div class="container">
<div class="row">
<div class="col">
<ul class="list-unstyled">
<li class="title">CryptPad</li>
<li><a href="/about.html" data-localization="about"></a></li>
<li><a href="/terms.html" data-localization="terms"></a></li>
<li><a href="/privacy.html" data-localization="privacy"></a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_applications"><li>
<li><a href="/pad/" data-localization="main_richText"></a></li>
<li><a href="/code/" data-localization="main_code"></a></li>
<li><a href="/slide/" data-localization="main_slide"></a></li>
<li><a href="/poll/" data-localization="main_poll"></a></li>
<li><a href="/drive/" data-localization="main_drive"></a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_aboutUs"><li>
<li><a href="https://blog.cryptpad.fr" target="_blank" data-localization="blog" rel="noopener noreferrer">Blog</a></li>
<li><a href="https://labs.xwiki.com" target="_blank" rel="noopener noreferrer">XWiki Labs</a></li>
<li><a href="http://www.xwiki.com" target="_blank" rel="noopener noreferrer">XWiki SAS</a></li>
<li><a href="https://www.open-paas.org/" target="_blank" rel="noopener noreferrer">OpenPaaS</a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_contact"><li>
<li><a href="https://riot.im/app/#/room/#cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
<li><a href="/contact.html">Email</a></li>
</ul>
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.9.0 (Jackelope)</div>
</footer>
</body>
<p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
<p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
</noscript>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 89 KiB

@ -34,7 +34,8 @@ define([
// User admin menu
var $userMenu = $('#user-menu');
var userMenuCfg = {
$initBlock: $userMenu
$initBlock: $userMenu,
'static': true
};
var $userAdmin = Cryptpad.createUserAdminMenu(userMenuCfg);
$userAdmin.find('button').addClass('btn').addClass('btn-secondary');

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 10 KiB

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

After

Width:  |  Height:  |  Size: 263 B

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

After

Width:  |  Height:  |  Size: 298 B

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

After

Width:  |  Height:  |  Size: 255 B

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

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

File diff suppressed because it is too large Load Diff

@ -2,242 +2,15 @@
<html class="cp">
<!-- If this file is not called customize.dist/src/template.html, it is generated -->
<head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/customize/main.css" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<script data-bootload="/customize/main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script>
</head>
<body class="html">
<div id="cryptpadTopBar">
<span>
<a class="gotoMain" href="/">
<img src="/customize/cryptofist_mini.png" class="cryptpad-logo" alt="" /> CryptPad
</a>
</span>
<!--<span class="slogan" data-localization="main_slogan"></span>-->
<span id="user-menu" class="right dropdown-bar"></span>
<span id="language-selector" class="right dropdown-bar"></span>
<span class="link right">
<a href="/about.html" data-localization="about">About</a>
</span>
<span class="link right">
<a href="/privacy.html" data-localization="privacy">Privacy</a>
</span>
<span class="link right">
<a href="/terms.html" data-localization="terms">ToS</a>
</span>
<span class="link right">
<a href="/contact.html" data-localization="contact">Contact</a>
</span>
<span class="link right">
<a href="https://blog.cryptpad.fr/" data-localization="blog">Blog</a>
</span>
<span class="link right">
<button id="upgrade" class="upgrade btn buttonSuccess" style="display: none;"></button>
</span>
</div>
<noscript>
<div id="noscriptContainer">
<div class="mainOverlay"></div>
<div id="noscript">
<p>
<strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.
</p>
<hr>
<p>
<strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.
</p>
</div>
</div>
</noscript>
<div id="mainBlock" class="hidden">
<a data-localization-title="github_ribbon" href="https://github.com/xwiki-labs/cryptpad" class="github-corner" aria-label="View source on Github"><svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 50px; border: 0; left: 0; transform: scale(-1, 1);z-index:2;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<!-- Thanks! http://tholman.com/github-corners/ -->
<div id="main">
<div class="mainOverlay"></div>
<div id="align-container">
<div id="main-container">
<div id="data" class="hidden">
<p class="left" data-localization="main_info"><!-- Collaborate in Confidence. --></p>
</div>
<div id="userForm" class="form-group hidden">
<input type="text" id="name" name="name" class="form-control" data-localization-placeholder="login_username">
<input type="password" id="password" name="password" class="form-control" data-localization-placeholder="login_password">
<div style="display: none;"><span class="remember form-check"><label for="rememberme" class="form-check-label" data-localization-append="login_remember"><input type="checkbox" id="rememberme" class="form-check-input" checked="checked"></label></span><br></div>
<button class="btn btn-secondary login half first" data-localization="login_login"></button> <button class="btn btn-success register half" data-localization="login_register"></button>
<p class="separator" data-localization="login_orNoLogin"></p>
<p id="buttons" class="buttons"></p>
<p class="driveLink"><a class="gotodrive" href="/drive/" data-localization="login_nologin"></a></p>
</div>
<div id="loggedIn" class="hidden">
<p id="loggedInHello"></p>
<p><button class="btn btn-primary gotodrive" data-localization="login_accessDrive"></button></p>
<p><button id="loggedInLogOut" class="btn btn-secondary" data-localization="logoutButton"></button></p>
</div>
</div>
</div>
</div>
<div class="page category first" id="knowmore">
<center>
<h1 data-localization="main_howitworks"></h1>
</center>
</div>
<div class="page">
<div class="info-container">
<div class="left image">
<img src="customize/images/zeroknowledge_small.png" alt="Zero Knowledge" />
</div>
<div class="right">
<h2 data-localization="main_zeroKnowledge"></h2>
<p data-localization="main_zeroKnowledge_p"></p>
</div>
</div>
</div>
<div class="page even">
<div class="info-container">
<div class="left">
<h2 data-localization="main_writeItDown"></h2>
<p data-localization="main_writeItDown_p"></p>
</div>
<div class="right image">
<img src="customize/images/realtime_small.png" alt="User account" />
</div>
</div>
</div>
<div class="page">
<div class="info-container">
<div class="left image">
<img src="customize/images/key_small.png" alt="User account" />
</div>
<div class="right">
<h2 data-localization="main_share"></h2>
<p data-localization="main_share_p"></p>
</div>
</div>
</div>
<div class="page even">
<div class="info-container">
<div class="left">
<h2 data-localization="main_organize"></h2>
<p data-localization="main_organize_p"></p>
</div>
<div class="right image">
<img src="customize/images/organize.png" alt="User account" />
</div>
</div>
</div>
<div class="page category" id="tryit">
<center>
<h1 data-localization="tryIt">Try it out!</h1>
</center>
</div>
<div class="page">
<div class="app-container">
<div class="app-row">
<div class="app">
<center>
<h2 data-localization="main_richText"></h2>
<img src="customize/images/pad.png" alt="Rich Text application" />
</center>
<p data-localization="main_richText_p"></p>
<p class="buttons">
<a id="create-pad" href="/pad/"><button class="btn btn-secondary" data-localization="button_newpad"></button></a>
</p>
</div><div class="app">
<center>
<h2 data-localization="main_code"></h2>
<img src="customize/images/code.png" alt="Code application" />
</center>
<p data-localization="main_code_p"></p>
<p class="buttons">
<a id="create-code" href="/code/"><button class="btn btn-secondary" data-localization="button_newcode"></button></a>
</p>
</div><!--
--></div><!--
--><div class="app-row"><!--
--><div class="app">
<center>
<h2 data-localization="main_slide"></h2>
<img src="customize/images/slide.png" alt="Slide applcation" />
</center>
<p data-localization="main_slide_p"></p>
<p class="buttons">
<a id="create-slide" href="/slide/"><button class="btn btn-secondary" data-localization="button_newslide"></button></a>
</p>
</div><div class="app">
<center>
<h2 data-localization="main_poll"></h2>
<img src="customize/images/poll.png" alt="Poll application" />
</center>
<p data-localization="main_poll_p"></p>
<p class="buttons">
<a id="create-poll" href="/poll/"><button class="btn btn-secondary" data-localization="button_newpoll"></button></a>
</p>
</div>
</div>
</div>
</div>
</div>
<footer>
<div class="container">
<div class="row">
<div class="col">
<ul class="list-unstyled">
<li class="title">CryptPad</li>
<li><a href="/about.html" data-localization="about"></a></li>
<li><a href="/terms.html" data-localization="terms"></a></li>
<li><a href="/privacy.html" data-localization="privacy"></a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_applications"><li>
<li><a href="/pad/" data-localization="main_richText"></a></li>
<li><a href="/code/" data-localization="main_code"></a></li>
<li><a href="/slide/" data-localization="main_slide"></a></li>
<li><a href="/poll/" data-localization="main_poll"></a></li>
<li><a href="/drive/" data-localization="main_drive"></a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_aboutUs"><li>
<li><a href="https://blog.cryptpad.fr" target="_blank" data-localization="blog" rel="noopener noreferrer">Blog</a></li>
<li><a href="https://labs.xwiki.com" target="_blank" rel="noopener noreferrer">XWiki Labs</a></li>
<li><a href="http://www.xwiki.com" target="_blank" rel="noopener noreferrer">XWiki SAS</a></li>
<li><a href="https://www.open-paas.org/" target="_blank" rel="noopener noreferrer">OpenPaaS</a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_contact"><li>
<li><a href="https://riot.im/app/#/room/#cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
<li><a href="/contact.html">Email</a></li>
</ul>
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.9.0 (Jackelope)</div>
</footer>
</body>
<p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
<p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
</noscript>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because it is too large Load Diff

@ -171,6 +171,10 @@ define([
$('button.gotodrive').click(function () {
document.location.href = '/drive/';
});
$('button#loggedInLogout').click(function () {
$('#user-menu .logout').click();
});
};
displayCreateButtons();

@ -46,6 +46,7 @@ define(req, function($, Default, Language) {
}
messages._languages = map;
messages._languageUsed = language;
messages._checkTranslationState = function (cb) {
if (typeof(cb) !== "function") { return; }

@ -0,0 +1,521 @@
define([
'/api/config',
'/common/hyperscript.js',
'/common/cryptpad-common.js',
], function (Config, h, Cryptpad) {
var Pages = {};
var Msg = Cryptpad.Messages;
var urlArgs = Config.requireConf.urlArgs;
var setHTML = function (e, html) {
e.innerHTML = html;
return e;
};
var indexContent = function () {
return [
h('div.page.category.first#knowmore', [
h('center', [
h('h1', Msg.main_howitworks)
])
]),
h('div.page', [
h('div.info-container', [
h('div.left.image', [
h('img', {
src: '/customize/images/zeroknowledge_small.png?' + urlArgs ,
alt: 'Zero Knowledge'
})
]),
h('div.right', [
h('h2', Msg.main_zeroKnowledge),
setHTML(h('p'), Msg.main_zeroKnowledge_p)
])
])
]),
h('div.page.even', [
h('div.info-container', [
h('div.left', [
h('h2', Msg.main_writeItDown),
h('p', Msg.main_writeItDown_p)
]),
h('div.right.image', [
h('img', {
alt: "User account",
src: '/customize/images/realtime_small.png?' + urlArgs,
})
])
])
]),
h('div.page', [
h('div.info-container', [
h('div.left.image', [
h('img', {
src: '/customize/images/key_small.png?' + urlArgs,
alt: 'User account'
})
]),
h('div.right', [
h('h2', Msg.main_share),
h('p', Msg.main_share_p)
])
])
]),
h('div.page.even', [
h('div.info-container', [
h('div.left', [
h('h2', Msg.main_organize),
h('p', Msg.main_organize_p)
]),
h('div.right.image', [
h('img', {
src: '/customize/images/organize.png?' + urlArgs,
alt: 'User account'
})
])
])
])
];
};
Pages['/about.html'] = function () {
return h('div#main_other', [
h('center', [
h('h1', Msg.about)
]),
setHTML(h('p'), Msg.main_p2),
h('h2', Msg.main_howitworks),
setHTML(h('p'), Msg.main_howitworks_p1)
].concat(indexContent()));
};
Pages['/privacy.html'] = function () {
return h('div#main_other', [
h('center', h('h1', Msg.policy_title)),
h('h2', Msg.policy_whatweknow),
h('p', Msg.policywhatweknow_p1),
h('h2', Msg.policy_howweuse),
h('p', Msg.policy_howweuse_p1),
h('p', Msg.policy_howweuse_p2),
h('h2', Msg.policy_whatwetell),
h('p', Msg.policy_whatwetell_p1),
h('h2', Msg.policy_links),
h('p', Msg.policy_links_p1),
h('h2', Msg.policy_ads),
h('p', Msg.policy_ads_p1),
h('h2', Msg.policy_choices),
h('p', Msg.policy_choices_open),
setHTML(h('p'), Msg.policy_choices_vpn),
h('br')
]);
};
Pages['/terms.html'] = function () {
return h('div#main_other', [
h('center', h('h1', Msg.tos_title)),
h('p', Msg.tos_legal),
h('p', Msg.tos_availability),
h('p', Msg.tos_e2ee),
h('p', Msg.tos_logs),
h('p', Msg.tos_3rdparties),
]);
};
Pages['/contact.html'] = function () {
return h('div#main_other', [
h('center', h('h1', Msg.contact)),
setHTML(h('p'), Msg.main_about_p2)
]);
};
var userForm = function () {
return h('div#userForm.form-group.hidden', [
h('input#name.form-control', {
name: 'name',
type: 'text',
placeholder: Msg.login_username
}),
h('input#password.form-control', {
name: 'password',
type: 'password',
placeholder: Msg.login_password
}),
h('div', {
style: { display: 'none' }
}, [
h('span.remember.form-check', [
h('label.form-check-label', {
'for': 'rememberme',
placeholder: Msg.login_remember,
}, [
h('input#rememberme.form-check-input', {
type: 'checkbox',
checked: true
})
])
])
]),
h('button.btn.btn-secondary.login.half.first', Msg.login_login),
h('button.btn.btn-success.register.half', Msg.login_register),
h('p.separator', Msg.login_orNoLogin),
h('p#buttons.buttons'),
h('p.driveLink', [
h('a.gotodrive', {
href: '/drive/'
}, Msg.login_nologin)
])
]);
};
var appButton = function (alt, h2, img, p, url, btn, id) {
return h('div.app', [
h('center', [
h('h2', h2),
h('img', {
alt: 'Rich Text application',
src: img,
})
]),
setHTML(h('p'), p),
h('p.buttons', [
h('a#' + id, {
href: url,
}, [
h('button.btn.btn-secondary', btn),
])
])
]);
};
var tryIt = function () {
return [
h('div.class.category#tryit', [
h('center', [
h('h1', Msg.tryIt)
])
]),
h('div.page', [
h('div.app-container', [
h('div.app-row', [
appButton("Rich Text application",
Msg.main_richText,
'/customize/images/pad.png?' + urlArgs,
Msg.main_richText_p,
'/pad/',
Msg.button_newpad,
'create-pad'),
appButton('Code application',
Msg.main_code,
'/customize/images/code.png?' + urlArgs,
Msg.main_code_p,
'/code/',
Msg.button_newcode,
'create-code'),
appButton('Slide application',
Msg.main_slide,
'/customize/images/slide.png?' + urlArgs,
Msg.main_slide_p,
'/slide/',
Msg.button_newslide,
'create-slide'),
appButton('Poll application',
Msg.main_poll,
'/customize/images/poll.png?' + urlArgs,
Msg.main_poll_p,
'/poll/',
Msg.button_newpoll,
'create-poll')
])
])
])
];
};
Pages['/'] = Pages['/index.html'] = function () {
return [
h('div#main', [
h('div.mainOverlay'),
h('div#align-container', [
h('div#main-container', [
h('div#data.hidden', [
setHTML(h('p.left'), Msg.main_info),
]),
userForm(),
h('div#loggedIn.hidden', [
h('p#loggedInHello'),
h('p', [
h('button.btn.btn-primary.gotodrive', Msg.login_accessDrive),
]),
h('p', [
h('button#loggedInLogout.btn.btn-secondary', Msg.logoutButton)
])
])
])
]),
])
]
.concat(tryIt());
};
var loadingScreen = function () {
return h('div#loading',
h('div.loadingContainer', [
h('img.cryptofist', {
src: '/customize/cryptofist_small.png?' + urlArgs
}),
h('div.spinnerContainer',
h('span.fa.fa-spinner.fa-pulse.fa-4x.fa-fw')),
h('p', Msg.loading)
])
);
};
loadingScreen = loadingScreen; // TODO use this
Pages['/user/'] = Pages['/user/index.html'] = function () {
return h('div#container');
};
Pages['/register/'] = Pages['/register/index.html'] = function () {
return [h('div#main', [
h('div.mainOverlay'),
h('div#align-container', [
h('div#data.hidden', [
h('h1', Msg.register_header),
h('br'),
setHTML(h('p.left.register-explanation'), Msg.register_explanation)
]),
h('div#userForm.form-group.hidden', [
h('input.form-control#username', {
type: 'text',
autocomplete: 'off',
autocorrect: 'off',
autocapitalize: 'off',
spellcheck: false,
placeholder: Msg.login_username,
autofocus: true,
}),
h('input.form-control#password', {
type: 'password',
placeholder: Msg.login_password,
}),
h('input.form-control#password-confirm', {
type: 'password',
placeholder: Msg.login_confirm,
}),
h('input#import-recent', {
type: 'checkbox',
checked: true
}),
h('label', {
'for': 'import-recent',
}, Msg.register_importRecent),
h('br'),
h('input#accept-terms', {
type: 'checkbox'
}),
setHTML(h('label', {
'for': 'accept-terms',
}), Msg.register_acceptTerms),
h('br'),
h('button#register.btn.btn-primary', Msg.login_register)
])
])
])];
};
Pages['/login/'] = Pages['/login/index.html'] = function () {
return [h('div#main', [
h('div.mainOverlay'),
h('div#align-container',
h('div#main-container', [
h('div#data.hidden', setHTML(h('p.left'), Msg.main_info)),
h('div#userForm.form-group.hidden', [
h('input.form-control#name', {
name: 'name',
type: 'text',
autocomplete: 'off',
autocorrect: 'off',
autocapitalize: 'off',
spellcheck: false,
placeholder: Msg.login_username,
autofocus: true,
}),
h('input.form-control#password', {
type: 'password',
'name': 'password',
placeholder: Msg.login_password,
}),
h('button.btn.btn-primary.login.first', Msg.login_login),
h('div.extra', [
h('p', Msg.login_notRegistered),
h('button#register.btn.btn-success.register', Msg.login_register)
])
])
])
)
])];
};
var appToolbar = function () {
return h('div#toolbar.toolbar-container');
};
Pages['/whiteboard/'] = Pages['/whiteboard/index.html'] = function () {
return [
appToolbar(),
h('div#canvas-area', h('canvas#canvas', {
width: 600,
height: 600
})),
h('div#controls', {
style: {
display: 'block',
}
}, [
h('button#clear', Msg.canvas_clear), ' ',
h('button#toggleDraw', Msg.canvas_disable),
h('button#delete', {
style: {
display: 'none',
}
}),
h('input#width', {
type: 'range',
value: "5",
min: "1",
max: "100"
}),
h('label', {
'for': 'width'
}, Msg.canvas_width),
h('input#opacity', {
type: 'range',
value: "1",
min: "0.1",
max: "1",
step: "0.1"
}),
h('label', {
'for': 'width',
}),
h('span.selected')
]),
setHTML(h('div#colors'), '&nbsp;'),
loadingScreen(),
h('div#cursors', {
style: {
display: 'none',
background: 'white',
'text-align': 'center',
}
}),
h('div#pickers'),
];
};
Pages['/poll/'] = Pages['/poll/index.html'] = function () {
return [
appToolbar(),
h('div#content', [
h('div#poll', [
h('div#howItWorks', [
h('h1', 'CryptPoll'),
setHTML(h('h2'), Msg.poll_subtitle),
h('p', Msg.poll_p_save),
h('p', Msg.poll_p_encryption)
]),
h('div.upper', [
h('button#publish', {
style: { display: 'none' }
}, Msg.poll_publish_button),
h('button#admin', {
style: { display: 'none' },
title: Msg.poll_admin_button
}, Msg.poll_admin_button),
h('button#help', {
title: Msg.poll_show_help_button,
style: { display: 'none' }
}, Msg.poll_show_help_button)
]),
h('div.realtime', [
h('br'),
h('center', [
h('textarea#description', {
rows: "5",
cols: "50",
disabled: true
}),
h('br')
]),
h('div#tableContainer', [
h('div#tableScroll'),
h('button#create-user', {
title: Msg.poll_create_user
}, h('span.fa.fa-plus')),
h('button#create-option', {
title: Msg.poll_create_option
}, h('span.fa.fa-plus')),
h('button#commit', {
title: Msg.poll_commit
}, h('span.fa.fa-check'))
])
])
])
]),
loadingScreen()
];
};
Pages['/drive/'] = Pages['/drive/index.html'] = function () {
return loadingScreen();
};
Pages['/file/'] = Pages['/file/index.html'] = function () {
return loadingScreen();
};
Pages['/contacts/'] = Pages['/contacts/index.html'] = function () {
return loadingScreen();
};
Pages['/pad/'] = Pages['/pad/index.html'] = function () {
return loadingScreen();
};
Pages['/code/'] = Pages['/code/index.html'] = function () {
return loadingScreen();
};
Pages['/slide/'] = Pages['/slide/index.html'] = function () {
return loadingScreen();
};
Pages['/invite/'] = Pages['/invite/index.html'] = function () {
return loadingScreen();
};
Pages['/settings/'] = Pages['/settings/index.html'] = function () {
return [
h('div#toolbar'),
h('div#container'),
loadingScreen()
];
};
Pages['/profile/'] = Pages['/profile/index.html'] = function () {
return [
h('div#toolbar'),
h('div#container'),
loadingScreen()
];
};
return Pages;
});

@ -2,141 +2,15 @@
<html class="cp">
<!-- If this file is not called customize.dist/src/template.html, it is generated -->
<head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/customize/main.css" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<script data-bootload="/customize/main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script>
</head>
<body class="html">
<div id="cryptpadTopBar">
<span>
<a class="gotoMain" href="/">
<img src="/customize/cryptofist_mini.png" class="cryptpad-logo" alt="" /> CryptPad
</a>
</span>
<!--<span class="slogan" data-localization="main_slogan"></span>-->
<span id="user-menu" class="right dropdown-bar"></span>
<span id="language-selector" class="right dropdown-bar"></span>
<span class="link right">
<a href="/about.html" data-localization="about">About</a>
</span>
<span class="link right">
<a href="/privacy.html" data-localization="privacy">Privacy</a>
</span>
<span class="link right">
<a href="/terms.html" data-localization="terms">ToS</a>
</span>
<span class="link right">
<a href="/contact.html" data-localization="contact">Contact</a>
</span>
<span class="link right">
<a href="https://blog.cryptpad.fr/" data-localization="blog">Blog</a>
</span>
<span class="link right">
<button id="upgrade" class="upgrade btn buttonSuccess" style="display: none;"></button>
</span>
</div>
<noscript>
<div id="noscriptContainer">
<div class="mainOverlay"></div>
<div id="noscript">
<p>
<strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.
</p>
<hr>
<p>
<strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.
</p>
</div>
</div>
</noscript>
<div id="mainBlock" class="hidden">
<div id="main_other">
<center>
<h1 data-localization="policy_title"></h1>
</center>
<h2 data-localization="policy_whatweknow"></h2>
<p data-localization="policy_whatweknow_p1"><!-- HTTP headers, IP address--></p>
<h2 data-localization="policy_howweuse"></h2>
<p data-localization="policy_howweuse_p1"><!-- Referrer : promoting--></p>
<p data-localization="policy_howweuse_p2"><!-- Browser : prioritizing new features--></p>
<h2 data-localization="policy_whatwetell"></h2>
<p data-localization="policy_whatwetell_p1"></p>
<h2 data-localization="policy_links"></h2>
<p data-localization="policy_links_p1"></p>
<h2 data-localization="policy_ads"></h2>
<p data-localization="policy_ads_p1"></p>
<h2 data-localization="policy_choices"></h2>
<p data-localization="policy_choices_open"></p>
<p data-localization="policy_choices_vpn"></p>
<br />
</div>
</div>
<footer>
<div class="container">
<div class="row">
<div class="col">
<ul class="list-unstyled">
<li class="title">CryptPad</li>
<li><a href="/about.html" data-localization="about"></a></li>
<li><a href="/terms.html" data-localization="terms"></a></li>
<li><a href="/privacy.html" data-localization="privacy"></a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_applications"><li>
<li><a href="/pad/" data-localization="main_richText"></a></li>
<li><a href="/code/" data-localization="main_code"></a></li>
<li><a href="/slide/" data-localization="main_slide"></a></li>
<li><a href="/poll/" data-localization="main_poll"></a></li>
<li><a href="/drive/" data-localization="main_drive"></a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_aboutUs"><li>
<li><a href="https://blog.cryptpad.fr" target="_blank" data-localization="blog" rel="noopener noreferrer">Blog</a></li>
<li><a href="https://labs.xwiki.com" target="_blank" rel="noopener noreferrer">XWiki Labs</a></li>
<li><a href="http://www.xwiki.com" target="_blank" rel="noopener noreferrer">XWiki SAS</a></li>
<li><a href="https://www.open-paas.org/" target="_blank" rel="noopener noreferrer">OpenPaaS</a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_contact"><li>
<li><a href="https://riot.im/app/#/room/#cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
<li><a href="/contact.html">Email</a></li>
</ul>
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.9.0 (Jackelope)</div>
</footer>
</body>
<p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
<p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
</noscript>
</html>

@ -1,85 +0,0 @@
var Fs = require("fs");
// read a file
var read = function (path) {
return Fs.readFileSync(path, 'utf-8');
};
// write a file
var write = function (path, src) {
return Fs.writeFileSync(path, src);
};
// basic templating
var swap = function (src, dict) {
return src.replace(/\{\{(.*?)\}\}/g, function (a, b) {
return dict[b] || b;
});
};
// read the template file
var template = read('./template.html');
// read page fragments
var fragments = {};
[ 'index',
'fork',
'topbar',
'terms',
'privacy',
'about',
'contact',
'logo',
'noscript',
'footer',
'empty',
'script',
'appscript'
].forEach(function (name) {
fragments[name] = read('./fragments/' + name + '.html');
});
// build static pages
['index', 'privacy', 'terms', 'about', 'contact'].forEach(function (page) {
var source = swap(template, {
topbar: fragments.topbar,
fork: fragments.fork,
main: swap(fragments[page] || fragments.empty, {
topbar: fragments.topbar,
fork: fragments.fork,
logo: fragments.logo,
noscript: fragments.noscript,
footer: fragments.footer,
}),
logo: fragments.logo,
noscript: fragments.noscript,
footer: fragments.footer,
script: fragments.script
});
write('../' + page + '.html', source);
});
// build static pages
[
'../www/settings/index',
'../www/user/index'
].forEach(function (page) {
var source = swap(template, {
topbar: fragments.topbar,
fork: fragments.fork,
main: swap(fragments[page] || fragments.empty, {
topbar: fragments.topbar,
fork: fragments.fork,
logo: fragments.logo,
noscript: fragments.noscript,
footer: fragments.footer,
}),
logo: fragments.logo,
noscript: fragments.noscript,
footer: fragments.footer,
script: fragments.appscript
});
write('../' + page + '.html', source);
});

@ -1,11 +0,0 @@
<div id="main_other">
<center>
<h1 data-localization="about">About</h1>
</center>
<p data-localization="main_p2"><!-- CkEditor, CodeMirror, Chainpad --></p>
<h2 id="howitworks" data-localization="main_howitworks"></h2>
<p data-localization="main_howitworks_p1"><!-- Operational transform, Nakamoto blockchain, server kept unaware of the content--></p>
</div>

@ -1,2 +0,0 @@
<link rel="stylesheet" type="text/css" href="main.css" />
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>

@ -1,8 +0,0 @@
<div id="main_other">
<center>
<h1 data-localization="contact">Contact</h1>
</center>
<p data-localization="main_about_p2"><!-- Contact us--></p>
</div>

@ -1 +0,0 @@
<div id="container"></div>

@ -1,43 +0,0 @@
<footer>
<div class="container">
<div class="row">
<div class="col">
<ul class="list-unstyled">
<li class="title">CryptPad</li>
<li><a href="/about.html" data-localization="about"></a></li>
<li><a href="/terms.html" data-localization="terms"></a></li>
<li><a href="/privacy.html" data-localization="privacy"></a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_applications"><li>
<li><a href="/pad/" data-localization="main_richText"></a></li>
<li><a href="/code/" data-localization="main_code"></a></li>
<li><a href="/slide/" data-localization="main_slide"></a></li>
<li><a href="/poll/" data-localization="main_poll"></a></li>
<li><a href="/drive/" data-localization="main_drive"></a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_aboutUs"><li>
<li><a href="https://blog.cryptpad.fr" target="_blank" data-localization="blog" rel="noopener noreferrer">Blog</a></li>
<li><a href="https://labs.xwiki.com" target="_blank" rel="noopener noreferrer">XWiki Labs</a></li>
<li><a href="http://www.xwiki.com" target="_blank" rel="noopener noreferrer">XWiki SAS</a></li>
<li><a href="https://www.open-paas.org/" target="_blank" rel="noopener noreferrer">OpenPaaS</a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_contact"><li>
<li><a href="https://riot.im/app/#/room/#cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
<li><a href="/contact.html">Email</a></li>
</ul>
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.9.0 (Jackelope)</div>
</footer>

@ -1,3 +0,0 @@
<a data-localization-title="github_ribbon" href="https://github.com/xwiki-labs/cryptpad" class="github-corner" aria-label="View source on Github"><svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 50px; border: 0; left: 0; transform: scale(-1, 1);z-index:2;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<!-- Thanks! http://tholman.com/github-corners/ -->

@ -1,127 +0,0 @@
{{fork}}
<div id="main">
<div class="mainOverlay"></div>
<div id="align-container">
<div id="main-container">
<div id="data" class="hidden">
<p class="left" data-localization="main_info"><!-- Collaborate in Confidence. --></p>
</div>
<div id="userForm" class="form-group hidden">
<input type="text" id="name" name="name" class="form-control" data-localization-placeholder="login_username">
<input type="password" id="password" name="password" class="form-control" data-localization-placeholder="login_password">
<div style="display: none;"><span class="remember form-check"><label for="rememberme" class="form-check-label" data-localization-append="login_remember"><input type="checkbox" id="rememberme" class="form-check-input" checked="checked"></label></span><br></div>
<button class="btn btn-secondary login half first" data-localization="login_login"></button> <button class="btn btn-success register half" data-localization="login_register"></button>
<p class="separator" data-localization="login_orNoLogin"></p>
<p id="buttons" class="buttons"></p>
<p class="driveLink"><a class="gotodrive" href="/drive/" data-localization="login_nologin"></a></p>
</div>
<div id="loggedIn" class="hidden">
<p id="loggedInHello"></p>
<p><button class="btn btn-primary gotodrive" data-localization="login_accessDrive"></button></p>
<p><button id="loggedInLogOut" class="btn btn-secondary" data-localization="logoutButton"></button></p>
</div>
</div>
</div>
</div>
<div class="page category first" id="knowmore">
<center>
<h1 data-localization="main_howitworks"></h1>
</center>
</div>
<div class="page">
<div class="info-container">
<div class="left image">
<img src="customize/images/zeroknowledge_small.png" alt="Zero Knowledge" />
</div>
<div class="right">
<h2 data-localization="main_zeroKnowledge"></h2>
<p data-localization="main_zeroKnowledge_p"></p>
</div>
</div>
</div>
<div class="page even">
<div class="info-container">
<div class="left">
<h2 data-localization="main_writeItDown"></h2>
<p data-localization="main_writeItDown_p"></p>
</div>
<div class="right image">
<img src="customize/images/realtime_small.png" alt="User account" />
</div>
</div>
</div>
<div class="page">
<div class="info-container">
<div class="left image">
<img src="customize/images/key_small.png" alt="User account" />
</div>
<div class="right">
<h2 data-localization="main_share"></h2>
<p data-localization="main_share_p"></p>
</div>
</div>
</div>
<div class="page even">
<div class="info-container">
<div class="left">
<h2 data-localization="main_organize"></h2>
<p data-localization="main_organize_p"></p>
</div>
<div class="right image">
<img src="customize/images/organize.png" alt="User account" />
</div>
</div>
</div>
<div class="page category" id="tryit">
<center>
<h1 data-localization="tryIt">Try it out!</h1>
</center>
</div>
<div class="page">
<div class="app-container">
<div class="app-row">
<div class="app">
<center>
<h2 data-localization="main_richText"></h2>
<img src="customize/images/pad.png" alt="Rich Text application" />
</center>
<p data-localization="main_richText_p"></p>
<p class="buttons">
<a id="create-pad" href="/pad/"><button class="btn btn-secondary" data-localization="button_newpad"></button></a>
</p>
</div><div class="app">
<center>
<h2 data-localization="main_code"></h2>
<img src="customize/images/code.png" alt="Code application" />
</center>
<p data-localization="main_code_p"></p>
<p class="buttons">
<a id="create-code" href="/code/"><button class="btn btn-secondary" data-localization="button_newcode"></button></a>
</p>
</div><!--
--></div><!--
--><div class="app-row"><!--
--><div class="app">
<center>
<h2 data-localization="main_slide"></h2>
<img src="customize/images/slide.png" alt="Slide applcation" />
</center>
<p data-localization="main_slide_p"></p>
<p class="buttons">
<a id="create-slide" href="/slide/"><button class="btn btn-secondary" data-localization="button_newslide"></button></a>
</p>
</div><div class="app">
<center>
<h2 data-localization="main_poll"></h2>
<img src="customize/images/poll.png" alt="Poll application" />
</center>
<p data-localization="main_poll_p"></p>
<p class="buttons">
<a id="create-poll" href="/poll/"><button class="btn btn-secondary" data-localization="button_newpoll"></button></a>
</p>
</div>
</div>
</div>
</div>

@ -1,3 +0,0 @@
<center>
<a href="/"><img class="imgcenter cryptofist" src="/customize/cryptofist_small.png" /></a>
</center>

@ -1,14 +0,0 @@
<noscript>
<div id="noscriptContainer">
<div class="mainOverlay"></div>
<div id="noscript">
<p>
<strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.
</p>
<hr>
<p>
<strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.
</p>
</div>
</div>
</noscript>

@ -1,29 +0,0 @@
<div id="main_other">
<center>
<h1 data-localization="policy_title"></h1>
</center>
<h2 data-localization="policy_whatweknow"></h2>
<p data-localization="policy_whatweknow_p1"><!-- HTTP headers, IP address--></p>
<h2 data-localization="policy_howweuse"></h2>
<p data-localization="policy_howweuse_p1"><!-- Referrer : promoting--></p>
<p data-localization="policy_howweuse_p2"><!-- Browser : prioritizing new features--></p>
<h2 data-localization="policy_whatwetell"></h2>
<p data-localization="policy_whatwetell_p1"></p>
<h2 data-localization="policy_links"></h2>
<p data-localization="policy_links_p1"></p>
<h2 data-localization="policy_ads"></h2>
<p data-localization="policy_ads_p1"></p>
<h2 data-localization="policy_choices"></h2>
<p data-localization="policy_choices_open"></p>
<p data-localization="policy_choices_vpn"></p>
<br />
</div>

@ -1 +0,0 @@
<script data-bootload="/customize/main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>

@ -1,14 +0,0 @@
<!--<table class="recent scroll" style="display:none">
<tbody>
<tr>
<th data-localization="table_type"></th>
<th data-localization="table_link"></th>
<th data-localization="table_created"></th>
<th data-localization="table_last"></th>
<th></th>
</tr>
</tbody>
</table>-->

@ -1,12 +0,0 @@
<div id="main_other">
<center>
<h1 data-localization="tos_title"></h1>
</center>
<p data-localization="tos_legal"></p>
<p data-localization="tos_availability"></p>
<p data-localization="tos_e2ee"></p>
<p data-localization="tos_logs"></p>
<p data-localization="tos_3rdparties"></p>
</div>

@ -1,30 +0,0 @@
<div id="cryptpadTopBar">
<span>
<a class="gotoMain" href="/">
<img src="/customize/cryptofist_mini.png" class="cryptpad-logo" alt="" /> CryptPad
</a>
</span>
<!--<span class="slogan" data-localization="main_slogan"></span>-->
<span id="user-menu" class="right dropdown-bar"></span>
<span id="language-selector" class="right dropdown-bar"></span>
<span class="link right">
<a href="/about.html" data-localization="about">About</a>
</span>
<span class="link right">
<a href="/privacy.html" data-localization="privacy">Privacy</a>
</span>
<span class="link right">
<a href="/terms.html" data-localization="terms">ToS</a>
</span>
<span class="link right">
<a href="/contact.html" data-localization="contact">Contact</a>
</span>
<span class="link right">
<a href="https://blog.cryptpad.fr/" data-localization="blog">Blog</a>
</span>
<span class="link right">
<button id="upgrade" class="upgrade btn buttonSuccess" style="display: none;"></button>
</span>
</div>

@ -15,15 +15,22 @@ html.cp, .cp body {
background-color: @page-white; //@base;
color: @fore;
font-family: Ubuntu,Georgia,Cambria,serif;
height: 100%;
}
.fa {
cursor: default; // Fix for Edge displaying the text cursor on every icon
}
.cp {
// override bootstrap colors
.btn-primary {
background-color: @cp-blue;
&:hover {
color: #fff;
background-color: #025aa5;
border-color: #01549b;
}
}
body {
@ -105,19 +112,24 @@ h6 {
padding-top: .65001rem;
}
a {
cursor: pointer;
color: @cp-link;
p {
a:not(.btn) {
cursor: pointer;
color: @cp-link;
text-decoration: none;
text-decoration: none;
&:hover {
color: @cp-link-hover;
}
&:visited {
color: @cp-link-visited;
&:hover {
color: @cp-link-hover;
}
&:visited {
color: @cp-link-visited;
}
}
}
a.btn {
font-family: sans-serif;
}
img {
height: auto;
@ -211,13 +223,13 @@ body.html {
margin: 0 auto;
}
.app-row {
display: inline-block;
white-space: nowrap;
width: 700px;
display: flex;
justify-content: center;
flex-flow: row wrap;
max-width: 100%;
margin: 0 auto;
@media screen and (max-width: 1399px) {
display: block;
display: flex;
}
img {
@media screen and (max-width: @media-not-big) {
@ -413,6 +425,8 @@ noscript {
label {
margin-bottom: 0;
margin-left: 5px;
vertical-align: middle;
}
button {
@ -420,7 +434,7 @@ noscript {
width: 100%;
cursor: pointer;
&.half {
width: ~"calc(50% - 2px)";
width: ~"calc(50% - 10px)";
&:not(.first) {
float: right;
}
@ -557,20 +571,25 @@ noscript {
/* Pin limit */
.limit-container {
display: inline-flex;
flex-flow: column-reverse;
width: 100%;
margin-top: 20px;
.cryptpad-limit-bar {
display: inline-block;
height: 26px;
width: 200px;
margin: 2px;
max-width: 40vw;
margin: 3px;
box-sizing: border-box;
border: 1px solid #999;
background: white;
position: relative;
text-align: center;
line-height: 24px;
vertical-align: middle;
width: ~"calc(100% - 6px)";
height: 25px;
line-height: 25px;
.usage {
height: 24px;
height: 100%;
display: inline-block;
background: blue;
position: absolute;
@ -591,12 +610,16 @@ noscript {
color: black;
text-shadow: 1px 0 2px white, 0 1px 2px white, -1px 0 2px white, 0 -1px 2px white;
z-index: 2;
font-size: 16px;
font-size: @main-font-size;
font-weight: bold;
}
}
.upgrade {
margin-left: 10px;
padding: 0;
line-height: 25px;
height: 25px;
margin: 0 3px;
border-radius: 0;
}
}
@ -608,10 +631,10 @@ noscript {
background-color: rgba(0,0,0,0.5);
color: white;
opacity: 0.7;
font-family: Ubuntu,Georgia,Cambria,serif;
box-sizing: border-box;
z-index:10;
z-index:10000;
display: none;
font-family: -apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
#uploadStatus {
width: 80vw;
border: 1px solid black;

@ -21,17 +21,24 @@
margin-right: 0px;
margin-left: 5px;
}
* {
.unselectable();
cursor: default;
}
}
.dropdown-bar-content {
display: none;
position: absolute;
background-color: #f9f9f9;
background-color: @dropdown-bg;
min-width: 250px;
box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2);
z-index: 1000;
max-height: 300px;
overflow-y: auto;
font-family: -apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
font-size: 16px;
line-height: 1em;
&.left {
right: 0;
@ -42,10 +49,10 @@
}
a {
color: black !important;
color: @dropdown-color;
padding: 5px 16px;
text-decoration: none;
display: block;
display: flex;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
@ -53,15 +60,24 @@
user-select: none;
float: none;
text-align: left;
font: @dropdown-font;
line-height: 1em;
.fa {
width: 20px;
text-align: center;
margin-right: 5px !important;
}
&:hover {
background-color: #f1f1f1;
color: black !important;
background-color: @dropdown-bg-hover;
color: @dropdown-color;
}
&.active {
background-color: #e8e8e8;
color: black !important;
background-color: @dropdown-bg-active;
color: @dropdown-color;
}
}
@ -77,13 +93,13 @@
margin: 0;
white-space: normal;
text-align: left;
&.cryptpad-dropdown-users {
text-align:baseline;
.yourself, .anonymous, .viewer {
font-style: italic;
}
color: black;
font-size: 14px;
* {
font-size: 14px;
}
h2 {
color: black;
font-weight: bold;
text-align: center;
background-color: #EEEEEE;

@ -1,3 +1,5 @@
@import "./variables.less";
.cp #loading {
position: fixed;
z-index: 9999;

@ -1,3 +1,5 @@
@import "/customize/src/less/variables.less";
.fontface(@family, @src, @style: normal, @weight: 400, @fmt: 'truetype'){
@font-face {
font-family: @family;
@ -37,3 +39,102 @@
background: linear-gradient(@start, @end); /* Standard syntax */
}
.placeholderColor (@color) {
&::-webkit-input-placeholder { /* WebKit, Blink, Edge */
color: @color;;
}
&:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
color: @color;
opacity: 1;
}
&::-moz-placeholder { /* Mozilla Firefox 19+ */
color: @color;
opacity: 1;
}
&:-ms-input-placeholder { /* Internet Explorer 10-11 */
color: @color;
}
&::-ms-input-placeholder { /* Microsoft Edge */
color: @color;
}
}
.avatar (@width) {
&.avatar {
overflow: hidden;
text-overflow: ellipsis;
font-size: 16px;
display: flex;
align-items: center;
&.clickable {
cursor: pointer;
&:hover {
background-color: rgba(0,0,0,0.3);
}
}
.default, media-tag {
display: inline-flex;
width: @width;
height: @width;
justify-content: center;
align-items: center;
border-radius: 4px;
overflow: hidden;
box-sizing: content-box;
}
.default {
.unselectable();
background: white;
color: black;
font-size: @width/1.2;
}
.right-col {
order: 10;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: flex;
flex-flow: column;
.name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.friend {
padding: 0;
}
}
media-tag {
min-height: @width;
min-width: @width;
max-height: @width;
max-width: @width;
img {
min-width: 100%;
min-height: 100%;
max-width: none;
max-height: none; // To override 'media-tag img' in slide.less
}
}
}
}
.leftsideCategory {
.unselectable();
padding: 5px 20px;
margin: 15px 0;
cursor: pointer;
height: @toolbar-line-height;
line-height: @toolbar-line-height - 10px;
.fa {
width: 25px;
}
&:hover {
background: rgba(0,0,0,0.05);
}
&.active {
background: white;
}
}

@ -0,0 +1,97 @@
@import '/customize/src/less/variables.less';
@import '/customize/src/less/mixins.less';
@leftside-bg: #eee;
@leftside-color: #000;
@rightside-color: #000;
@description-color: #777;
@button-width: 400px;
@button-bg: #3066e5;
@button-alt-bg: #fff;
@button-red-bg: #e54e4e;
.cp {
input[type="text"] {
padding-left: 10px;
}
#container {
font-size: 16px;
display: flex;
flex: 1;
min-height: 0;
#leftSide {
color: @leftside-color;
width: 250px;
background: @leftside-bg;
display: flex;
flex-flow: column;
.categories {
flex: 1;
.category {
.leftsideCategory();
}
}
}
#rightSide {
flex: 1;
padding: 5px 20px;
color: @rightside-color;
overflow: auto;
.element {
label:not(.noTitle), .label {
display: block;
font-weight: bold;
margin-bottom: 0;
}
.description {
display: block;
color: @description-color;
margin-bottom: 5px;
}
margin-bottom: 20px;
}
[type="text"], button {
vertical-align: middle;
height: 40px;
box-sizing: border-box;
}
.inputBlock {
display: inline-flex;
width: @button-width;
input {
flex: 1;
border-radius: 0.25em 0 0 0.25em;
border: 1px solid #adadad;
border-right: 0px;
}
button {
border-radius: 0 0.25em 0.25em 0;
//border: 1px solid #adadad;
border-left: 0px;
}
}
button.btn {
background-color: @button-bg;
border-color: darken(@button-bg, 10%);
color: white;
&:hover {
background-color: darken(@button-bg, 10%);
}
&.btn-danger {
background-color: @button-red-bg;
border-color: darken(@button-red-bg, 10%);
color: white;
&:hover {
background-color: darken(@button-red-bg, 10%);
}
}
}
&>div {
margin: 10px 0;
}
}
}
}

File diff suppressed because it is too large Load Diff

@ -72,7 +72,7 @@
}
}
}
a {
&.link a {
font-weight: 500;
font-size: 0.75em;
color: @cp-link;

@ -7,6 +7,8 @@
@old-base: #302B28;
@old-fore: #fafafa;
@main-font-size: 16px;
@cp-green: #46E981;
@cp-accent: lighten(@cp-green, 20%);
@ -70,9 +72,42 @@
@media-narrow-screen: 400px;
@media-medium-screen: 600px;
@toolbar-gradient-start: #f5f5f5;
@toolbar-gradient-end: #DDDDDD;
@toolbar-button-font: 12px Ubuntu, Arial, sans-serif;
// Dropdown
@dropdown-font: @main-font-size -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
@dropdown-bg: #f9f9f9;
@dropdown-color: black;
@dropdown-bg-hover: #f1f1f1;
@dropdown-bg-active: #e8e8e8;
// Toolbar
@toolbar-button-font: @dropdown-font;
@toolbar-pad-bg: #1c4fa0;
@toolbar-pad-color: #fff;
@toolbar-slide-bg: #e57614;
@toolbar-slide-color: #fff;
@toolbar-code-bg: #ffae00;
@toolbar-code-color: #000;
@toolbar-poll-bg: #006304;
@toolbar-poll-color: #fff;
@toolbar-whiteboard-bg: #800080;
@toolbar-whiteboard-color: #fff;
@toolbar-drive-bg: #0087ff;
@toolbar-drive-color: #fff;
@toolbar-file-bg: #cd2532;
@toolbar-file-color: #fff;
@toolbar-friends-bg: #607B8D;
@toolbar-friends-color: #fff;
@toolbar-default-bg: #ddd;
@toolbar-default-color: #000;
@toolbar-settings-bg: #0087ff;
@toolbar-settings-color: #fff;
@toolbar-profile-bg: #0087ff;
@toolbar-profile-color: #fff;
@topbar-back: #fff;
@topbar-color: #000;
@ -80,6 +115,8 @@
@topbar-button-color: #fff;
@topbar-height: 50px;
@toolbar-top-height: 64px;
@main-border-width: 15vw;
@cp-darkblue: #3333ff;
@cp-accent2: darken(@cp-darkblue, 20%);
@ -89,3 +126,14 @@
@main-bg: url('/customize/bg3.jpg') no-repeat center center;
@category-bg: #f4f4f4;
.unselectable () {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
@toolbar-line-height: 32px;

@ -2,24 +2,15 @@
<html class="cp">
<!-- If this file is not called customize.dist/src/template.html, it is generated -->
<head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/customize/main.css" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
{{script}}
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script>
</head>
<body class="html">
{{topbar}}
{{noscript}}
<div id="mainBlock" class="hidden">
{{main}}
</div>
{{footer}}
</body>
<noscript>
<p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
<p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
</noscript>
</html>

@ -0,0 +1,198 @@
define([
'jquery',
'/common/hyperscript.js',
'/common/cryptpad-common.js',
'/customize/pages.js',
'/api/config',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
], function ($, h, Cryptpad, Pages, Config) {
$(function () {
var urlArgs = Config.requireConf.urlArgs;
var Messages = Cryptpad.Messages;
var $body = $('body');
var isMainApp = function () {
return /^\/(pad|code|slide|poll|whiteboard|file|media|contacts|drive|settings|profile)\/$/.test(location.pathname);
};
var rightLink = function (ref, loc, txt) {
return h('span.link.right', [
h('a', { href: ref, 'data-localization': loc}, txt)
]);
};
var $topbar = $(h('div#cryptpadTopBar', [
h('span', [
h('a.gotoMain', {href: '/'}, [
h('img.cryptpad-logo', {
src: '/customize/cryptofist_mini.png?' + urlArgs,
alt: '',
}),
'CryptPad'
])
]),
h('span#user-menu.right.dropdown-bar'),
h('span#language-selector.right.dropdown-bar'),
rightLink('/about.html', 'about', 'About'),
rightLink('/privacy.html', 'privacy', 'Privacy'),
rightLink('/terms.html', 'terms', 'ToS'),
rightLink('/contact.html', 'contact', 'Contact'),
rightLink('https://blog.cryptpad.fr/', 'blog', 'Blog'),
h('span.link.right', [
h('button#upgrade.upgrade.btn.buttonSuccess', {
style: { display: 'none' }
})
])
]
));
var infoPage = function () {
return h('div#mainBlock.hidden', typeof(Pages[location.pathname]) === 'function'?
Pages[location.pathname](): [h('div#container')]);
};
var $main = $(infoPage());
var footerCol = function (title, L, literal) {
return h('div.col', [
h('ul.list-unstyled', [
h('li.title', {
'data-localization': title,
}, title? Messages[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 = Messages[loc];
}
return h('a', attrs, text);
};
var $footer = $(h('footer', [
h('div.container', [
h('div.row', [
footerCol(null, [
footLink('/about.html', 'about'),
footLink('/terms.html', 'terms'),
footLink('/privacy.html', 'privacy'),
], 'CryptPad'),
footerCol('footer_applications', [
footLink('/drive/', 'main_drive'),
footLink('/pad/', 'main_richText'),
footLink('/code/', 'main_code'),
footLink('/slide/', 'main_slide'),
footLink('/poll/', 'main_poll'),
footLink('/whiteboard/', null, Messages.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.version-footer', "CryptPad v1.11.0 (Lutin)")
]));
var pathname = location.pathname;
if (isMainApp()) {
if (typeof(Pages[pathname]) === 'function') {
var $flash = $('body, #iframe-container, #pad-iframe, textarea');
$flash.css({
display: 'none',
opacity: 0,
overflow: 'hidden',
});
var ready = function () {
$flash.css({
display: '',
opacity: '',
overflow: '',
});
};
require([
'less!/customize/src/less/loading.less'
], function () {
if (/whiteboard/.test(pathname)) {
$('body').html(h('body', Pages[pathname]()).innerHTML);
require(['/whiteboard/main.js'], ready);
} else if (/poll/.test(pathname)) {
$('body').html(h('body', Pages[pathname]()).innerHTML);
require(['/poll/main.js'], ready);
} else if (/drive/.test(pathname)) {
$('body').append(h('body', Pages[pathname]()).innerHTML);
require(['/drive/main.js'], ready);
} else if (/\/file\//.test(pathname)) {
$('body').append(h('body', Pages[pathname]()).innerHTML);
require([ '/file/main.js' ], ready);
} else if (/contacts/.test(pathname)) {
$('body').append(h('body', Pages[pathname]()).innerHTML);
require([ '/contacts/main.js' ], ready);
} else if (/pad/.test(pathname)) {
$('body').append(h('body', Pages[pathname]()).innerHTML);
require([ '/pad/main.js' ], ready);
} else if (/code/.test(pathname)) {
$('body').append(h('body', Pages[pathname]()).innerHTML);
require([ '/code/main.js' ], ready);
} else if (/slide/.test(pathname)) {
$('body').append(h('body', Pages[pathname]()).innerHTML);
require([ '/slide/main.js' ], ready);
} else if (/^\/settings\//.test(pathname)) {
$('body').append(h('body', Pages[pathname]()).innerHTML);
require([ '/settings/main.js', ], ready);
} else if (/^\/profile\//.test(pathname)) {
$('body').append(h('body', Pages[pathname]()).innerHTML);
require([ '/profile/main.js', ], ready);
}
});
return;
}
}
require([
'less!/customize/src/less/cryptpad.less',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
], function () {
$body.append($topbar).append($main).append($footer);
if (/^\/user\//.test(pathname)) {
require([ '/user/main.js'], function () {});
} else if (/^\/register\//.test(pathname)) {
require([ '/register/main.js' ], function () {});
} else if (/^\/login\//.test(pathname)) {
require([ '/login/main.js' ], function () {});
} else if (/^\/($|^\/index\.html$)/.test(pathname)) {
// TODO use different top bar
require([ '/customize/main.js', ], function () {});
} else if (/invite/.test(pathname)) {
require([ '/invite/main.js'], function () {});
} else {
require([ '/customize/main.js', ], function () {});
}
});
});
});

@ -2,124 +2,15 @@
<html class="cp">
<!-- If this file is not called customize.dist/src/template.html, it is generated -->
<head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/customize/main.css" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<script data-bootload="/customize/main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script>
</head>
<body class="html">
<div id="cryptpadTopBar">
<span>
<a class="gotoMain" href="/">
<img src="/customize/cryptofist_mini.png" class="cryptpad-logo" alt="" /> CryptPad
</a>
</span>
<!--<span class="slogan" data-localization="main_slogan"></span>-->
<span id="user-menu" class="right dropdown-bar"></span>
<span id="language-selector" class="right dropdown-bar"></span>
<span class="link right">
<a href="/about.html" data-localization="about">About</a>
</span>
<span class="link right">
<a href="/privacy.html" data-localization="privacy">Privacy</a>
</span>
<span class="link right">
<a href="/terms.html" data-localization="terms">ToS</a>
</span>
<span class="link right">
<a href="/contact.html" data-localization="contact">Contact</a>
</span>
<span class="link right">
<a href="https://blog.cryptpad.fr/" data-localization="blog">Blog</a>
</span>
<span class="link right">
<button id="upgrade" class="upgrade btn buttonSuccess" style="display: none;"></button>
</span>
</div>
<noscript>
<div id="noscriptContainer">
<div class="mainOverlay"></div>
<div id="noscript">
<p>
<strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.
</p>
<hr>
<p>
<strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.
</p>
</div>
</div>
</noscript>
<div id="mainBlock" class="hidden">
<div id="main_other">
<center>
<h1 data-localization="tos_title"></h1>
</center>
<p data-localization="tos_legal"></p>
<p data-localization="tos_availability"></p>
<p data-localization="tos_e2ee"></p>
<p data-localization="tos_logs"></p>
<p data-localization="tos_3rdparties"></p>
</div>
</div>
<footer>
<div class="container">
<div class="row">
<div class="col">
<ul class="list-unstyled">
<li class="title">CryptPad</li>
<li><a href="/about.html" data-localization="about"></a></li>
<li><a href="/terms.html" data-localization="terms"></a></li>
<li><a href="/privacy.html" data-localization="privacy"></a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_applications"><li>
<li><a href="/pad/" data-localization="main_richText"></a></li>
<li><a href="/code/" data-localization="main_code"></a></li>
<li><a href="/slide/" data-localization="main_slide"></a></li>
<li><a href="/poll/" data-localization="main_poll"></a></li>
<li><a href="/drive/" data-localization="main_drive"></a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_aboutUs"><li>
<li><a href="https://blog.cryptpad.fr" target="_blank" data-localization="blog" rel="noopener noreferrer">Blog</a></li>
<li><a href="https://labs.xwiki.com" target="_blank" rel="noopener noreferrer">XWiki Labs</a></li>
<li><a href="http://www.xwiki.com" target="_blank" rel="noopener noreferrer">XWiki SAS</a></li>
<li><a href="https://www.open-paas.org/" target="_blank" rel="noopener noreferrer">OpenPaaS</a></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled">
<li class="title" data-localization="footer_contact"><li>
<li><a href="https://riot.im/app/#/room/#cryptpad:matrix.org" target="_blank" rel="noopener noreferrer">Chat</a></li>
<li><a href="https://twitter.com/cryptpad" target="_blank" rel="noopener noreferrer">Twitter</a></li>
<li><a href="https://github.com/xwiki-labs/cryptpad" target="_blank" rel="noopener noreferrer">GitHub</a></li>
<li><a href="/contact.html">Email</a></li>
</ul>
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.9.0 (Jackelope)</div>
</footer>
</body>
<p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
<p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
</noscript>
</html>

@ -1,581 +0,0 @@
/* The container <div> - needed to position the dropdown content */
.dropdown-bar {
position: relative;
display: inline-block;
}
.dropdown-bar .fa {
font-family: FontAwesome;
}
.dropdown-bar button .fa-caret-down {
margin-right: 0px;
margin-left: 5px;
}
.dropdown-bar .dropdown-bar-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 250px;
box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2);
z-index: 1000;
max-height: 300px;
overflow-y: auto;
}
.dropdown-bar .dropdown-bar-content.left {
right: 0;
}
.dropdown-bar .dropdown-bar-content:hover {
display: block;
}
.dropdown-bar .dropdown-bar-content a {
color: black !important;
padding: 5px 16px;
text-decoration: none;
display: block;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
float: none;
text-align: left;
}
.dropdown-bar .dropdown-bar-content a:hover {
background-color: #f1f1f1;
color: black !important;
}
.dropdown-bar .dropdown-bar-content a.active {
background-color: #e8e8e8;
color: black !important;
}
.dropdown-bar .dropdown-bar-content hr {
margin: 5px 0px;
height: 1px;
background: #bbb;
}
.dropdown-bar .dropdown-bar-content p {
min-width: 160px;
padding: 5px;
margin: 0;
white-space: normal;
text-align: left;
}
.dropdown-bar .dropdown-bar-content p.cryptpad-dropdown-users {
text-align: baseline;
}
.dropdown-bar .dropdown-bar-content p.cryptpad-dropdown-users .yourself,
.dropdown-bar .dropdown-bar-content p.cryptpad-dropdown-users .anonymous,
.dropdown-bar .dropdown-bar-content p.cryptpad-dropdown-users .viewer {
font-style: italic;
}
.dropdown-bar .dropdown-bar-content p h2 {
font-weight: bold;
text-align: center;
background-color: #EEEEEE;
padding: 5px 0px;
margin: 5px 0px;
font-size: 16px;
white-space: normal;
}
.unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.toolbar-container {
font: 12px Arial, Helvetica, Tahoma, Verdana, sans-serif;
background: -webkit-linear-gradient(#f5f5f5, #DDDDDD);
/* For Safari 5.1 to 6.0 */
background: -o-linear-gradient(#f5f5f5, #DDDDDD);
/* For Opera 11.1 to 12.0 */
background: -moz-linear-gradient(#f5f5f5, #DDDDDD);
/* For Firefox 3.6 to 15 */
background: linear-gradient(#f5f5f5, #DDDDDD);
/* Standard syntax */
}
.toolbar-container .cryptpad-toolbar select {
box-sizing: border-box;
}
.cryptpad-toolbar {
box-sizing: border-box;
padding: 0px 6px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
font: normal normal normal 12px Arial, Helvetica, Tahoma, Verdana, Sans-Serif;
color: #000;
width: 100%;
z-index: 9001;
}
.cryptpad-toolbar .fa {
font: normal normal normal 14px/1 FontAwesome;
font-family: FontAwesome;
}
.cryptpad-toolbar a {
float: right;
}
.cryptpad-toolbar button {
font: 12px Ubuntu, Arial, sans-serif;
border: 1px solid transparent;
border-radius: .25rem;
color: #000;
background-color: #fff;
border-color: #ccc;
}
.cryptpad-toolbar button * {
font: 12px Ubuntu, Arial, sans-serif;
}
.cryptpad-toolbar button#shareButton,
.cryptpad-toolbar button.buttonSuccess {
color: #fff;
background: #5cb85c;
border-color: #5cb85c;
}
.cryptpad-toolbar button#shareButton:hover,
.cryptpad-toolbar button.buttonSuccess:hover {
color: #fff;
background: #449d44;
border: 1px solid #419641;
}
.cryptpad-toolbar button#shareButton span,
.cryptpad-toolbar button.buttonSuccess span {
color: #fff;
}
.cryptpad-toolbar button#shareButton .large,
.cryptpad-toolbar button.buttonSuccess .large {
margin-left: 5px;
}
.cryptpad-toolbar button#newdoc,
.cryptpad-toolbar button.buttonPrimary {
color: #fff;
background: #0275d8;
border-color: #0275d8;
}
.cryptpad-toolbar button#newdoc:hover,
.cryptpad-toolbar button.buttonPrimary:hover {
color: #fff;
background: #025aa5;
border: 1px solid #01549b;
}
.cryptpad-toolbar button#newdoc span,
.cryptpad-toolbar button.buttonPrimary span {
color: #fff;
}
.cryptpad-toolbar button#newdoc .large,
.cryptpad-toolbar button.buttonPrimary .large {
margin-left: 5px;
}
.cryptpad-toolbar button.hidden {
display: none;
}
.cryptpad-toolbar button:hover {
color: #292b2c;
background-color: #e6e6e6;
border-color: #adadad;
}
.cryptpad-toolbar button.upgrade {
font-size: 14px;
vertical-align: top;
margin-left: 10px;
}
.cryptpad-toolbar .cryptpad-limit {
box-sizing: border-box;
height: 26px;
width: 26px;
display: inline-block;
padding: 3px;
margin: 0px;
margin-right: 3px;
vertical-align: middle;
}
.cryptpad-toolbar .cryptpad-limit span {
color: red;
cursor: pointer;
margin: auto;
font-size: 20px;
}
.cryptpad-toolbar .cryptpad-lag {
height: 20px;
width: 23px;
background: transparent;
display: inline-block;
padding: 3px;
margin: 3px;
vertical-align: top;
box-sizing: content-box;
text-align: center;
}
.cryptpad-toolbar .cryptpad-lag span {
display: inline-block;
width: 4px;
margin: 0;
margin-right: 1px;
background: black;
vertical-align: bottom;
box-sizing: border-box;
border: 1px solid black;
transition: background 1s, border 1s;
}
.cryptpad-toolbar .cryptpad-lag span:last-child {
margin-right: 0;
}
.cryptpad-toolbar .cryptpad-lag span.bar1 {
height: 5px;
}
.cryptpad-toolbar .cryptpad-lag span.bar2 {
height: 10px;
}
.cryptpad-toolbar .cryptpad-lag span.bar3 {
height: 15px;
}
.cryptpad-toolbar .cryptpad-lag span.bar4 {
height: 20px;
}
.cryptpad-toolbar .cryptpad-lag.lag0 span {
background: transparent;
border-color: red;
}
.cryptpad-toolbar .cryptpad-lag.lag1 .bar2,
.cryptpad-toolbar .cryptpad-lag.lag1 .bar3,
.cryptpad-toolbar .cryptpad-lag.lag1 .bar4 {
background: transparent;
}
.cryptpad-toolbar .cryptpad-lag.lag1 span {
background-color: orange;
border-color: orange;
}
.cryptpad-toolbar .cryptpad-lag.lag2 .bar3,
.cryptpad-toolbar .cryptpad-lag.lag2 .bar4 {
background: transparent;
}
.cryptpad-toolbar .cryptpad-lag.lag2 span {
background-color: orange;
border-color: orange;
}
.cryptpad-toolbar .cryptpad-lag.lag3 .bar4 {
background: transparent;
}
.cryptpad-toolbar .cryptpad-lag.lag3 span {
background-color: #5cb85c;
border-color: #5cb85c;
}
.cryptpad-toolbar .cryptpad-lag.lag4 span {
background-color: #5cb85c;
border-color: #5cb85c;
}
.cryptpad-toolbar div {
white-space: normal;
}
.cryptpad-toolbar div.cryptpad-back {
padding: 0;
font-weight: bold;
cursor: pointer;
color: #000;
}
.cryptpad-toolbar button,
.cryptpad-toolbar select,
.cryptpad-toolbar .rightside-element {
height: 26px;
padding-right: 5px;
padding-left: 5px;
margin: 3px 2px;
box-sizing: border-box;
}
.cryptpad-toolbar .dropdown-bar-content {
margin-top: -3px;
margin-right: 2px;
}
.cryptpad-toolbar .cryptpad-state {
line-height: 32px;
/* equivalent to 26px + 2*2px margin used for buttons */
}
.cryptpad-toolbar .rightside-button {
float: right;
cursor: pointer;
}
.cryptpad-toolbar .leftside-button {
cursor: pointer;
float: left;
}
.cryptpad-toolbar .rightside-element {
vertical-align: middle;
white-space: nowrap;
}
.cryptpad-toolbar .rightside-element.float {
float: right;
}
.cryptpad-toolbar select {
border: 0px;
margin-left: 5px;
margin-right: 5px;
padding-left: 5px;
border: 1px solid #A6A6A6;
border-bottom-color: #979797;
vertical-align: top;
box-sizing: content-box;
}
.cryptpad-toolbar select option {
height: 24px;
}
@media screen and (max-width: 800px) {
.cryptpad-toolbar .big {
display: none;
}
}
@media screen and (min-width: 801px) {
.cryptpad-toolbar .big {
display: inline-block;
}
}
@media screen and (max-width: 800px) {
.cryptpad-toolbar .small {
display: inline-block;
}
}
@media screen and (min-width: 801px) {
.cryptpad-toolbar .small {
display: none;
}
}
@media screen and (max-width: 600px) {
.cryptpad-toolbar .med-big {
display: none;
}
}
@media screen and (min-width: 601px) {
.cryptpad-toolbar .med-big {
display: inline-block;
}
}
@media screen and (max-width: 600px) {
.cryptpad-toolbar .med-small {
display: inline-block;
}
}
@media screen and (min-width: 601px) {
.cryptpad-toolbar .med-small {
display: none;
}
}
@media screen and (max-width: 400px) {
.cryptpad-toolbar .large {
display: none;
}
}
@media screen and (min-width: 401px) {
.cryptpad-toolbar .large {
display: inline-block;
}
}
@media screen and (max-width: 400px) {
.cryptpad-toolbar .narrow {
display: inline-block;
}
}
@media screen and (min-width: 401px) {
.cryptpad-toolbar .narrow {
display: none;
}
}
@media screen and (max-width: 600px) {
.cryptpad-toolbar:not(.notitle) .cryptpad-toolbar-top {
height: 67px;
}
}
@media screen and (max-width: 600px) {
.cryptpad-toolbar:not(.notitle) .cryptpad-toolbar-top .cryptpad-link,
.cryptpad-toolbar:not(.notitle) .cryptpad-toolbar-top .cryptpad-user {
top: 35px;
}
}
@media screen and (min-width: 601px) {
.cryptpad-toolbar:not(.notitle) .cryptpad-toolbar-top .cryptpad-link,
.cryptpad-toolbar:not(.notitle) .cryptpad-toolbar-top .cryptpad-user {
top: 0px;
}
}
.cryptpad-toolbar-top {
display: block;
text-align: center;
height: 32px;
position: relative;
}
.cryptpad-toolbar-top .cryptpad-title .title,
.cryptpad-toolbar-top .cryptpad-title .pencilIcon {
font-size: 1.5em;
vertical-align: middle;
line-height: 32px;
}
.cryptpad-toolbar-top .cryptpad-title .pencilIcon {
margin-left: 11px;
}
.cryptpad-toolbar-top .cryptpad-title .pencilIcon:hover {
color: #999;
margin-left: 0px;
}
.cryptpad-toolbar-top .cryptpad-title .pencilIcon span {
cursor: pointer;
}
.cryptpad-toolbar-top .cryptpad-title:not(input):hover .editable {
border: 1px solid #888;
border-radius: 2px 0px 0px 2px;
background: white;
padding: 5px;
border-collapse: collapse;
}
.cryptpad-toolbar-top .cryptpad-title:not(input):hover .pencilIcon {
cursor: pointer;
border: 1px solid #888;
border-radius: 0px 2px 2px 0px;
background: white;
padding: 5px;
display: inline;
margin-left: -1px;
border-collapse: collapse;
}
.cryptpad-toolbar-top .cryptpad-title input {
font-size: 1.5em;
vertical-align: middle;
box-sizing: border-box;
border: 1px solid black;
background: #fff;
cursor: auto;
width: 300px;
padding: 5px 5px;
}
.cryptpad-toolbar-top .cryptpad-link {
position: absolute;
left: 0px;
height: 32px;
}
.cryptpad-toolbar-top .cryptpad-link a.cryptpad-logo {
cursor: pointer;
height: 32px;
padding: 0px 5px;
text-decoration: none;
color: inherit;
}
.cryptpad-toolbar-top .cryptpad-link a.cryptpad-logo:hover span {
text-decoration: underline;
}
.cryptpad-toolbar-top .cryptpad-link a.cryptpad-logo img {
vertical-align: middle;
height: 32px;
cursor: pointer;
}
.cryptpad-toolbar-top .cryptpad-link a.cryptpad-logo span {
font-size: 1.5em;
margin-left: 5px;
vertical-align: middle;
cursor: pointer;
}
.cryptpad-toolbar-top .cryptpad-user {
position: absolute;
right: 0;
}
.cryptpad-toolbar-top .cryptpad-user :not(.cryptpad-lag) span {
vertical-align: top;
}
.cryptpad-toolbar-top .cryptpad-user button span.fa {
vertical-align: baseline;
}
.cryptpad-toolbar-leftside {
float: left;
margin-bottom: -1px;
}
.cryptpad-toolbar-leftside .cryptpad-dropdown-users pre {
/* needed for ckeditor */
white-space: pre;
margin: 5px 0px;
}
.cryptpad-toolbar-leftside button {
margin: 2px 4px 2px 0px;
}
.cryptpad-toolbar-leftside .dropdown-bar-content {
margin-top: -1px;
}
.cryptpad-toolbar-rightside {
text-align: right;
}
.cryptpad-toolbar-history {
display: none;
text-align: center;
}
.cryptpad-toolbar-history .next {
display: inline-block;
vertical-align: middle;
margin: 20px;
}
.cryptpad-toolbar-history .previous {
display: inline-block;
vertical-align: middle;
margin: 20px;
}
.cryptpad-toolbar-history .goto {
display: inline-block;
vertical-align: middle;
text-align: center;
}
.cryptpad-toolbar-history .goto input {
width: 75px;
}
.cryptpad-toolbar-history .gotoInput {
vertical-align: middle;
}
.cke_toolbox .cryptpad-toolbar-history input.gotoInput {
background: white;
height: 20px;
padding: 3px 3px;
border-radius: 5px;
}
.cryptpad-spinner > span {
height: 16px;
width: 16px;
margin: 8px;
line-height: 16px;
font-size: 16px;
text-align: center;
}
.cryptpad-readonly {
margin-right: 5px;
font-weight: bold;
text-transform: uppercase;
}
.cryptpad-user p.accountData {
background: #f0f0f0;
}
.cryptpad-user p.accountData > span {
font-weight: bold;
}
.cryptpad-user p.accountData > span span {
font-weight: normal;
}
.cryptpad-user .buttonTitle .fa:not(.fa-caret-down) {
margin-right: 5px;
}
.cryptpad-user .buttonTitle .account-name {
margin-left: 5px;
}
.cryptpad-dropdown-share a .fa {
margin-right: 5px;
}
.lag {
height: 15px !important;
width: 15px !important;
border-radius: 50%;
border: 1px solid #444;
}
.lag-green {
background-color: #46E981;
}
.lag-red {
background-color: #FA5858;
}
.lag-orange {
background-color: #FE9A2E;
}

@ -53,7 +53,7 @@
out.shareSuccess = 'Die URL wurde in die Zwischenablage kopiert';
out.presentButtonTitle = "Präsentationsmodus starten";
out.presentSuccess = 'Drücke ESC um den Präsentationsmodus zu beenden';
out.presentSuccess = 'Drücke ESC um den Präsentationsmodus zu verlassen!';
out.backgroundButtonTitle = 'Die Hintergrundfarbe der Präsentation ändern';
out.colorButtonTitle = 'Die Textfarbe im Präsentationsmodus ändern';

@ -12,12 +12,13 @@ define(function () {
out.type.poll = 'Encuesta';
out.type.slide = 'Presentación';
out.type.whiteboard = 'Pizarra';
out.type.contacts = 'Contactos';
out.disconnected = "Desconectado";
out.synchronizing = "Sincronización";
out.reconnecting = "Reconectando...";
out.lag = "Retraso";
out.readonly = 'Solo lectura';
out.readonly = 'Sólo lectura';
out.anonymous = 'Anónimo';
out.yourself = "Tú mismo";
out.anonymousUsers = "usuarios anónimos";
@ -29,8 +30,8 @@ define(function () {
out.editor = "editor";
out.editors = "editores";
out.greenLight = "Todo funciona bién";
out.orangeLight = "La conexión es lenta y podria impactar la experiencia";
out.greenLight = "Todo funciona bien";
out.orangeLight = "La conexión es lenta y podría afectar la experiencia";
out.redLight = "Has sido desconectado de la sesión";
out.importButtonTitle = 'Importar un documento de tus archivos locales';
@ -43,7 +44,7 @@ define(function () {
out.clickToEdit = "Haz clic para cambiar";
out.forgetButtonTitle = 'Eliminar este documento de la lista en la pagina de inicio';
out.forgetPrompt = 'Pulser OK eliminará este documento del almacenamiento local (localStorage), ¿estás seguro?';
out.forgetPrompt = 'Pulsar OK eliminará este documento del almacenamiento local (localStorage), ¿estás seguro?';
out.shareButton = 'Compartir';
out.shareSuccess = 'URL copiada al portapapeles';
@ -56,10 +57,10 @@ define(function () {
out.editShare = "URL de edición compartida";
out.editShareTitle = "Copiar la URL de edición al portapapeles";
out.viewShare = "Compartir URL de solo lectura";
out.viewShareTitle = "Copiar la URL de solo lectura al portapapeles";
out.viewShare = "Compartir URL de sólo lectura";
out.viewShareTitle = "Copiar la URL de sólo lectura al portapapeles";
out.viewOpen = "Ver en pestaña nueva";
out.viewOpenTitle = "Abrir el documento en solo lectura en una pestaña nueva";
out.viewOpenTitle = "Abrir el documento en sólo lectura en una pestaña nueva";
out.notifyJoined = "{0} se ha unido a la sesión de colaboración";
out.notifyRenamed = "{0} ahora se conoce como {1}";
@ -75,10 +76,10 @@ define(function () {
out.poll_title = "Selector de fecha Zero Knowledge";
out.poll_subtitle = "Agenda en <em>tiempo real</em> Zero Knowledge";
out.poll_p_save = "Tus configuraciones se actualizan instantaneamente, no es necesario guardar cambios.";
out.poll_p_encryption = "Todos los datos entrados son cifrados, solo las personas que poseen el enlace tienen acceso. Incluso el servidor no puede ver el contenido.";
out.poll_p_save = "Tus configuraciones se actualizan instantáneamente, no es necesario guardar cambios.";
out.poll_p_encryption = "Todos los datos entrados son cifrados, sólo las personas que poseen el enlace tienen acceso. Incluso el servidor no puede ver el contenido.";
out.wizardLog = "Presiona el boton en la parte superior izquierda para volver a la encuesta";
out.wizardLog = "Presiona el botón en la parte superior izquierda para volver a la encuesta";
out.wizardTitle = "Utiliza el asistente para crear tu encuesta";
out.wizardConfirm = "¿Estás realmente seguro de agregar estas opciones a tu encuesta?";
@ -86,14 +87,14 @@ define(function () {
out.poll_closeWizardButtonTitle = "Cerrar el asistente";
out.poll_wizardComputeButton = "Generar opciones";
out.poll_wizardClearButton = "Limpiar tabla";
out.poll_wizardDescription = "Automaticamente crear opciones ingresando cualquier cantidad de fechas y horas";
out.poll_wizardDescription = "Crear opciones automáticamente ingresando cualquier cantidad de fechas y horas";
out.poll_wizardAddDateButton = "+ Fechas";
out.poll_wizardAddTimeButton = "+ Horas";
out.poll_optionPlaceholder = "Opción";
out.poll_userPlaceholder = "Tu nombre";
out.poll_removeOption = "¿Estás seguro que quieres eliminar esta opción?";
out.poll_removeUser = "¿Estás seguro que quieres eliminar este usuario?";
out.poll_removeOption = "¿Estás seguro de que quieres eliminar esta opción?";
out.poll_removeUser = "¿Estás seguro de que quieres eliminar este usuario?";
out.poll_titleHint = "Título";
out.poll_descriptionHint = "Descripción";
@ -103,9 +104,9 @@ define(function () {
out.main_p2 = 'Este proyecto utiliza el editor de texto visual <a href="http://ckeditor.com/">CKEditor</a>, <a href="https://codemirror.net/">CodeMirror</a>, y el motor en tiempo real <a href="https://github.com/xwiki-contrib/chainpad">ChainPad</a>.';
out.main_howitworks = '¿Cómo funciona?';
out.main_howitworks_p1 = "CryptPad utiliza una variante del algoritmo de <a href='https://en.wikipedia.org/wiki/Operational_transformation'>transformación operacional</a> (página en inglés) que es capaz de encontrar un consenso distribuido usando un <a href='https://bitcoin.org/bitcoin.pdf'>Blockchain Nakamoto</a> (página en inglés), popularizado por <a href='https://es.wikipedia.org/wiki/Bitcoin'>Bitcoin</a>. De esta manera el algoritmo puede evitar la necesidad de un servidor central para resolver conflictos de edición de la transformación operacional y sin necesidad de resolver conflictos, el servidor puede mantenerse inconsciente del contenido que se está editando en el pad.";
out.main_about_p2 = 'Si tienes preguntas o comentarios, puedes <a href="https://twitter.com/cryptpad">enviarnos un tweet</a>, abrir un issue <a href="https://github.com/xwiki-labs/cryptpad/issues/" title="nuestro issue tracker">en GitHub</a>, saludarnos en nuestro canal IRC (<a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" title="freenode webchat">irc.freenode.net</a>), o <a href="mailto:research@xwiki.com">envianos un email</a>.';
out.main_about_p2 = 'Si tienes preguntas o comentarios, puedes <a href="https://twitter.com/cryptpad">enviarnos un tweet</a>, abrir un issue <a href="https://github.com/xwiki-labs/cryptpad/issues/" title="nuestro issue tracker">en GitHub</a>, saludarnos en nuestro canal IRC (<a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" title="freenode webchat">irc.freenode.net</a>), o <a href="mailto:research@xwiki.com">envíanos un email</a>.';
out.button_newpad = 'Crear nuevo pad de texto enriquezido';
out.button_newpad = 'Crear nuevo pad de texto enriquecido';
out.button_newcode = 'Crear nuevo pad de código';
out.button_newpoll = 'Crear nueva encuesta';
out.button_newslide = 'Crear nueva presentación';
@ -114,15 +115,15 @@ define(function () {
out.policy_title = 'Política de privacidad Cryptpad';
out.policy_whatweknow = 'Qué sabemos sobre tí';
out.policy_whatweknow_p1 = 'Como cualquier aplicación que está en la red, Cryptpad tiene acceso a los metadatos expuestos por el protócolo HTTP. Esto incluye tu dirección IP, y otros headers HTTP que pueden ser utilizados para identificar a tu navegador propio. Puedes ver la información que comparte tu navegador visitando <a target="_blank" rel="noopener noreferrer" href="https://www.whatismybrowser.com/detect/what-http-headers-is-my-browser-sending" title="Que headers HTTP esta compartiendo mi navegador">WhatIsMyBrowser.com</a> (página en inglés).';
out.policy_whatweknow_p2 = 'Nosotros usamos <a href="https://piwik.org/" target="_blank" rel="noopener noreferrer" title="open source analytics platform">Piwik</a>, una plataforma de analítica de datos abierta, para mejor conocer a nuestros usuarios. Piwik nos dice como encontráste Cryptpad, en entrada manual, por un motor de busquéda, or por referal de otra página como Reddit o Twitter. También aprendemos cuándo visitas, que páginas vees en nuestra web, y cuánto tiempo te quedas en cada una.';
out.policy_whatweknow_p1 = 'Como cualquier aplicación que está en la red, Cryptpad tiene acceso a los metadatos expuestos por el protocolo HTTP. Esto incluye tu dirección IP, y otros headers HTTP que pueden ser utilizados para identificar a tu navegador propio. Puedes ver la información que comparte tu navegador visitando <a target="_blank" rel="noopener noreferrer" href="https://www.whatismybrowser.com/detect/what-http-headers-is-my-browser-sending" title="Qué headers HTTP esta compartiendo mi navegador">WhatIsMyBrowser.com</a> (página en inglés).';
out.policy_whatweknow_p2 = 'Nosotros usamos <a href="https://piwik.org/" target="_blank" rel="noopener noreferrer" title="open source analytics platform">Piwik</a>, una plataforma de analítica de datos abierta, para conocer mejor a nuestros usuarios. Piwik nos dice como encontraste Cryptpad, en entrada manual, por un motor de búsqueda, or por referal de otra página como Reddit o Twitter. También aprendemos cuándo visitas, qué páginas ves en nuestra web, y cuánto tiempo te quedas en cada una.';
out.policy_howweuse = 'Cómo usamos lo que aprendemos';
out.policy_howweuse_p1 = 'Usamos esta información para tomar mejores decisiones para promocionar Cryptpad, para evaluar cuáles de nuestros esfuerzos han sido exitosos. La información sobre tu ubicación nos permite saber si deberíamos considerar mejor soporte para idiomas diferentes al inglés.';
out.policy_howweuse_p2 = "La información sobre tu navegador (en escritorio u movil) nos ayuda a saber qué caracteristicas que debemos mejorar. Nuestro equipo de desarrollo es pequeño, e intentamos tomar decisiones que beneficien a la experiencia de la mayoria de nuestros usuarios.";
out.policy_howweuse_p2 = "La información sobre tu navegador (en escritorio o móvil) nos ayuda a saber qué características que debemos mejorar. Nuestro equipo de desarrollo es pequeño, e intentamos tomar decisiones que beneficien a la experiencia de la mayoría de nuestros usuarios.";
out.policy_whatwetell = 'Lo que decimos a otros sobre tí';
out.policy_whatwetell_p1 = 'No suministramos la información que colectamos a terceros a menos de ser legalmente obligados a hacerlo.';
out.policy_whatwetell_p1 = 'No suministramos la información que recolectamos a terceros a menos de estar legalmente obligados a hacerlo.';
out.policy_links = 'Enlaces a otras páginas';
out.policy_links_p1 = 'Esta web contiene enlaces a otros sitios, incluyendo algunos producidos por otras organizaciones. No somos responsables por el tratamiento de la privacidad de los datos y el contenido de páginas externas. Como regla general, los enlaces externos se abren en una nueva pestaña del navegador, para clarificar que estás abandonando a Cryptpad.fr.';
out.policy_links_p1 = 'Esta web contiene enlaces a otros sitios, incluyendo algunos producidos por otras organizaciones. No somos responsables del tratamiento de la privacidad de los datos ni el contenido de páginas externas. Como regla general, los enlaces externos se abren en una nueva pestaña del navegador, para clarificar que estás abandonando a Cryptpad.fr.';
out.policy_ads = 'Anuncios';
out.policy_ads_p1 = 'Nosotros no mostramos anuncios, pero podemos poner enlaces a las organizaciones que financian nuestro trabajo de investigación.';
out.policy_choices = 'Lo que puedes hacer';
@ -134,8 +135,8 @@ define(function () {
out.tos_title = "Condiciones de servicio Cryptpad";
out.tos_legal = "Por favor, no seas malicioso, abusivo o hagas algo ilegal.";
out.tos_availability = "Esperamos que este servicio te parezca util, pero nuestra disponibilidad o rendimiento no pueden ser garantizados. Por favor, exporta tus datos regularmente.";
out.tos_e2ee = "Los documentos Cryptpad pueden ser leidos o modificados por cualquiera que pueda adivinar o que pueda tener el enlace. Recomendamos que utilizes mensajes cifrados de punto a punto (e2ee) para compartir URLs, no asumimos ninguna responsabilidad en el evento de alguna fuga.";
out.tos_availability = "Esperamos que este servicio te parezca útil, pero nuestra disponibilidad o rendimiento no pueden ser garantizados. Por favor, exporta tus datos regularmente.";
out.tos_e2ee = "Los documentos Cryptpad pueden ser leídos o modificados por cualquiera que pueda adivinar o que pueda tener el enlace. Recomendamos que utilices mensajes cifrados de punto a punto (e2ee) para compartir URLs, no asumimos ninguna responsabilidad en el evento de alguna fuga.";
out.tos_logs = "Los metadatos entregados por el navegador al servidor pueden ser almacenados para la mantenencia del servicio.";
out.tos_3rdparties = "No proveemos datos individualizados a terceros a menos de ser obligados por la ley.";
@ -151,8 +152,8 @@ define(function () {
out.header_logoTitle = 'Ir a la página principal';
out.websocketError = "Error al conectarse al servidor WebSocket";
out.typeError = "Este documento no es compatible con la applicación seleccionada";
out.onLogout = "Tu sesión está cerrada, <a href=\"/\" target=\"_blank\">haz clic aquí</a> para iniciar sesión<br>o apreta sobre <em>Escape</em> para acceder al documento en modo solo lectura.";
out.typeError = "Este documento no es compatible con la aplicación seleccionada";
out.onLogout = "Tu sesión está cerrada, <a href=\"/\" target=\"_blank\">haz clic aquí</a> para iniciar sesión<br>o pulsa <em>Escape</em> para acceder al documento en modo sólo lectura.";
out.loading = "Cargando...";
out.error = "Error";
out.language = "Idioma";
@ -184,37 +185,37 @@ define(function () {
out.fm_creation = "Creación";
out.fm_forbidden = "Acción prohibida";
out.fm_originalPath = "Enlace original";
out.fm_noname = "Documento sín título";
out.fm_emptyTrashDialog = "¿Seguro qué quieres vaciar la papelera?";
out.fm_removeSeveralPermanentlyDialog = "¿Seguro qué quieres eliminar estos {0} elementos de la papelera para siempre?";
out.fm_removePermanentlyDialog = "¿Seguro qué quieres eliminar este elemento para siempre?";
out.fm_removeSeveralDialog = "¿Seguro qué quieres mover estos {0} elementos a la papelera?";
out.fm_removeDialog = "¿Seguro qué quieres mover {0} a la papelera?";
out.fm_noname = "Documento sin título";
out.fm_emptyTrashDialog = "¿Seguro que quieres vaciar la papelera?";
out.fm_removeSeveralPermanentlyDialog = "¿Seguro que quieres eliminar estos {0} elementos de la papelera para siempre?";
out.fm_removePermanentlyDialog = "¿Seguro que quieres eliminar este elemento para siempre?";
out.fm_removeSeveralDialog = "¿Seguro que quieres mover estos {0} elementos a la papelera?";
out.fm_removeDialog = "¿Seguro que quieres mover {0} a la papelera?";
out.fm_restoreDialog = "¿Seguro que quieres recuperar {0}?";
out.fm_unknownFolderError = "La carpeta seleccionada ya no existe. Abriendo la carpeta anterior...";
out.fm_contextMenuError = "No se puedo abrir el menú para este elemento. Si persiste el problema, recarga la página.";
out.fm_selectError = "No se puedo abrir el elemento. Si persiste el problema, recarga la página.";
out.fm_contextMenuError = "No se pudo abrir el menú para este elemento. Si persiste el problema, recarga la página.";
out.fm_selectError = "No se pudo abrir el elemento. Si persiste el problema, recarga la página.";
out.fm_info_root = "Crea carpetas aquí para organizar tus documentos.";
out.fm_info_unsorted = "Contiene todos los documentos que has visitado que no estan organizados en \"Documentos\" o movidos a la \"Papelera\".";
out.fm_info_unsorted = "Contiene todos los documentos que has visitado que no están organizados en \"Documentos\" o movidos a la \"Papelera\".";
out.fm_info_template = "Contiene todas las plantillas que puedes volver a usar para crear nuevos documentos.";
out.fm_info_allFiles = "Contiene todos los archivos de \"Documentos\", \"Sin organizar\" y \"Papelera\". No puedes mover o eliminar archivos aquí.";
out.fm_alert_backupUrl = "Enlace de copia de seguridad para este drive. Te recomendamos <strong>muy fuertemente</strong> que lo guardes secreto.<br>Lo puedes usar para recuparar todos tus archivos en el caso que la memoria de tu navegador se borre.<br>Cualquiera con este enlace puede editar o eliminar todos los archivos en el explorador.<br>";
out.fm_alert_backupUrl = "Enlace de copia de seguridad para este drive. Te recomendamos <strong>encarecidamente</strong> que lo guardes secreto.<br>Lo puedes usar para recuperar todos tus archivos en el caso que la memoria de tu navegador se borre.<br>Cualquiera con este enlace puede editar o eliminar todos los archivos en el explorador.<br>";
out.fm_backup_title = "Enlace de copia de seguridad";
out.fm_nameFile = "¿Cómo quieres nombrar este archivo?";
out.fc_newfolder = "Nueva carpeta";
out.fc_rename = "Cambiar nombre";
out.fc_open = "Abrir";
out.fc_open_ro = "Abrir (solo lectura)";
out.fc_open_ro = "Abrir (sólo lectura)";
out.fc_delete = "Eliminar";
out.fc_restore = "Recuperar";
out.fc_remove = "Eliminar para siempre";
out.fc_empty = "Vaciar la papelera";
out.fc_prop = "Propriedades";
out.fc_prop = "Propiedades";
out.fo_moveUnsortedError = "No puedes mover una carpeta en la lista de documentos no organizados";
out.fo_existingNameError = "Nombre ya utilizado en esta carpeta. Por favor elige otro.";
out.fo_moveFolderToChildError = "No puedes mover una carpeta en una de sus subcarpetas";
out.fo_unableToRestore = "No se pudo restaurar este archivo a la localización de orígen. Puedes intentar moverlo a otra localización.";
out.fo_unavailableName = "Un archivo o carpeta ya tiene este nombre. Cambiálo y vuelve a intentarlo.";
out.fo_unableToRestore = "No se pudo restaurar este archivo a la localización de origen. Puedes intentar moverlo a otra localización.";
out.fo_unavailableName = "Un archivo o carpeta ya tiene este nombre. Cámbialo y vuelve a intentarlo.";
out.login_login = "Iniciar sesión";
out.login_makeAPad = "Crear documento anónimo";
out.login_nologin = "Ver documentos locales";
@ -225,53 +226,52 @@ define(function () {
out.login_password = "Contraseña";
out.login_confirm = "Confirmar contraseña";
out.login_remember = "Recuérdame";
out.login_hashing = "Tratamiento de datos, esto puede tardar un poco.";
out.login_hashing = "Generando hash de tu contraseña, esto puede tardar un poco.";
out.login_hello = "Hola {0},";
out.login_helloNoName = "Hola,";
out.login_accessDrive = "Acceder a tu drive";
out.login_orNoLogin = "o";
out.login_noSuchUser = "Credenciales invalidos. Inténtalo de nuevo, o registrate";
out.login_invalUser = "Nombre de usuario requirido";
out.login_invalPass = "Contraseña requirida";
out.login_unhandledError = "Un error inesperado se produjo :(";
out.login_noSuchUser = "Credenciales inválidos. Inténtalo de nuevo, o regístrate";
out.login_invalUser = "Nombre de usuario requerido";
out.login_invalPass = "Contraseña requerida";
out.login_unhandledError = "Ha ocurrido un error inesperado :(";
out.register_importRecent = "Importar historial (recomendado)";
out.register_acceptTerms = "Accepto los <a href='/terms.html' tabindex='-1'>términos de servicio</a>";
out.register_acceptTerms = "Acepto los <a href='/terms.html' tabindex='-1'>términos de servicio</a>";
out.register_passwordsDontMatch = "Las contraseñas no corresponden";
out.register_mustAcceptTerms = "Tienes que acceptar los términos de servicio";
out.register_mustAcceptTerms = "Tienes que aceptar los términos de servicio";
out.register_mustRememberPass = "No podemos reiniciar tu contraseña si la olvidas. ¡Es muy importante que la recuerdes! Marca la casilla para confirmarlo.";
out.register_header = "Bienvenido a CryptPad";
out.register_explanation = ["<p>Vamos a ver algunas cosas antes</p>", "<ul>", "<li>Tu contraseña es tu clave secreta que cifra todos tus documentos. Si la pierdes no podremos recuparar tus datos.</li>", "<li>Puedes importar documentos que has visto recientemente en tu navegador para tenerlos en tu cuenta.</li>", "<li>Si estás usando un ordenador compartido, tienes que cerrar sesión cuando terminas, cerrar la pestaña no es suficiente.</li>", "</ul>"].join('');
out.register_explanation = ["<p>Vamos a ver algunas cosas antes</p>", "<ul>", "<li>Tu contraseña es tu clave secreta que cifra todos tus documentos. Si la pierdes no podremos recuperar tus datos.</li>", "<li>Puedes importar documentos que has visto recientemente en tu navegador para tenerlos en tu cuenta.</li>", "<li>Si estás usando un ordenador compartido, tienes que cerrar sesión cuando terminas, cerrar la pestaña no es suficiente.</li>", "</ul>"].join('');
out.settings_title = "Preferencias";
out.settings_save = "Guardar";
out.settings_backupTitle = "Copia de seguridad";
out.settings_backup = "Copia de seguridad";
out.settings_restore = "Recuparar datos";
out.settings_resetTitle = "Limpiar tu drive";
out.settings_restore = "Recuperar datos";
out.settings_reset = "Quita todos los documentos de tu CryptDrive";
out.settings_resetPrompt = "Esta acción eliminará todos tus documentos.<br>¿Seguro que quieres continuar?<br>Introduce “<em>I love CryptPad</em>” para confirmar.";
out.settings_resetDone = "¡Tu drive ahora está vacio!";
out.settings_resetTips = "Consejos en CryptDrive";
out.settings_resetTipsButton = "Restaurar consejos";
out.settings_resetTipsDone = "Todos los consejos ahora están visibles";
out.main_info = "<h1>Collabora en Confidencia</h1><br>Cultiva ideas juntos con documentos compartidos con tecnología <strong>Zero Knowledge</strong> que protege tu privacidad.";
out.main_info = "<h1>Colabora con Confianza</h1><br>Cultiva ideas juntos con documentos compartidos con tecnología <strong>Zero Knowledge</strong> que protege tu privacidad.";
out.main_zeroKnowledge = "Zero Knowledge";
out.main_zeroKnowledge_p = "No tienes que confiar que <em>no</em> veremos tus documentos, con la tecnología Zero Knowledge de CryptPad <em>no podemos</em>. Aprende más sobre como protegemos tu <a href=\"/privacy.html\" title='Privacidad'>Privacidad y Seguridad</a>.";
out.main_zeroKnowledge_p = "No tienes que confiar en que <em>no</em> veremos tus documentos, con la tecnología Zero Knowledge de CryptPad <em>no podemos</em>. Aprende más sobre cómo protegemos tu <a href=\"/privacy.html\" title='Privacidad'>Privacidad y Seguridad</a>.";
out.main_writeItDown = "Escríbelo";
out.main_writeItDown_p = "Los mejores proyectos vienen de las más pequeñas ideas. Escribe tus momentos de inspiración y ideas inesperadas porque nunca sabrás cual será tu próximo descubrimiento.";
out.main_writeItDown_p = "Los mejores proyectos vienen de las más pequeñas ideas. Escribe tus momentos de inspiración e ideas inesperadas porque nunca sabrás cuál será tu próximo descubrimiento.";
out.main_share = "Comparte el enlace, comparte el pad";
out.main_share_p = "Cultiva ideas juntos: ten reuniones eficaces, collabora en listas y haz presentaciones rápidas en todos tus dispositivos.";
out.main_organize = "Organizate";
out.main_organize_p = "Con CryptPad Drive, porta tu atención en lo más importante. Carpetas te permiten organizar tus proyectos y tener una visión global de donde van las cosas.";
out.main_richText = "Editor de Texto Enriquezido";
out.main_richText_p = "Collabora en texto enriquezido con nuestro editor Zero Knowledge en tiempo real <a href=\"http://ckeditor.com\" target=\"_blank\">CkEditor</a>.";
out.main_share_p = "Cultiva ideas juntos: ten reuniones eficaces, colabora en listas y haz presentaciones rápidas en todos tus dispositivos.";
out.main_organize = "Organízate";
out.main_organize_p = "Con CryptPad Drive, mantén tu atención en lo más importante. Las carpetas te permiten organizar tus proyectos y tener una visión global de dónde van las cosas.";
out.main_richText = "Editor de Texto Enriquecido";
out.main_richText_p = "Colabora en texto enriquecido con nuestro editor Zero Knowledge en tiempo real <a href=\"http://ckeditor.com\" target=\"_blank\">CkEditor</a>.";
out.main_code = "Editor de código";
out.main_code_p = "Edita código fuente para tus programas con nuestro editor Zero Knowledge en tiempo real <a href=\"https://www.codemirror.net\" target=\"_blank\">CodeMirror</a>.";
out.main_slide = "Editor de presentación";
out.main_slide_p = "Crea presentaciones utilizando Markdown, y visualizalos en tu navegador";
out.main_slide_p = "Crea presentaciones utilizando Markdown, y visualízalos en tu navegador";
out.main_poll = "Encuestas";
out.main_poll_p = "Planifica tus reuniones y eventos, o vota para la mejor solución a un problema.";
out.main_drive = "CryptDrive";
out.footer_applications = "Applicaciones";
out.footer_applications = "Aplicaciones";
out.footer_contact = "Contacto";
out.footer_aboutUs = "Acerca de nosotros";
out.about = "Acerca de nosotros";
@ -282,49 +282,49 @@ define(function () {
// 1.1.0 - Bunyip
out.movedToTrash = "Este pad fue movido a la papelera.<br><a href\"/drive/\">Acceder a mi Drive</a>";
out.fm_newFile = "Nuevo pad";
out.fm_type = "Típo";
out.fm_type = "Tipo";
out.fm_categoryError = "No se pudo abrir la categoría seleccionada, mostrando la raíz.";
out.settings_userFeedbackHint1 = "CryptPad suministra informaciones muy básicas al servidor, para ayudarnos a mejorar vuestra experiencia.";
out.settings_userFeedbackHint2 = "El contenido de tu pad nunca será compartido con el servidor.";
out.settings_userFeedback = "Activar feedback";
out.settings_anonymous = "No has iniciado sesión. Tus ajustes se aplicarán solo a este navegador.";
out.settings_anonymous = "No has iniciado sesión. Tus ajustes se aplicarán sólo a este navegador.";
out.blog = "Blog";
out.initialState = [
'<span style="font-size:18px;"><p>',
'Esto es&nbsp;<strong>CryptPad</strong>, el editor collaborativo en tiempo real Zero Knowledge. Todo está guardado cuando escribes.',
'Esto es&nbsp;<strong>CryptPad</strong>, el editor colaborativo en tiempo real Zero Knowledge. Todo está guardado cuando escribes.',
'<br>',
'Comparte el enlace a este pad para editar con amigos o utiliza el botón <span style="background-color:#5cb85c;color:#ffffff;">&nbsp;Compartir&nbsp;</span> para obtener un <em>enlace solo lectura</em>&nbsp;que permite leer pero no escribir.',
'Comparte el enlace a este pad para editar con amigos o utiliza el botón <span class="fa fa-share-alt" style="border: 1px solid black;color:#000;">&nbsp;Compartir&nbsp;</span> para obtener un <em>enlace sólo lectura</em>&nbsp;que permite leer pero no escribir.',
'</p>',
'<p><span style="color:#808080;"><em>',
'Vamos, solo empezia a escribir...',
'</em></span></p></span>',
'<p><em>',
'Vamos, empieza a escribir...',
'</em></p></span>',
'<p>&nbsp;<br></p>'
].join('');
out.codeInitialState = "/*\n Esto es CryptPad, el editor collaborativo en tiempo real zero knowledge.\n Lo que escribes aquí es cifrado, con lo cual solo las personas con el enlace pueden accederlo.\n Incluso el servidor no puede ver lo que escribes.\n Lo que ves aquí, lo que escuchas aquí, cuando sales, se queda aquí\n*/";
out.slideInitialState = "# CryptSlide\n* Esto es CryptPad, el editor collaborativo en tiempo real zero knowledge.\n* Lo que escribes aquí es cifrado, con lo cual solo las personas con el enlace pueden accederlo.\n* Incluso el servidor no puede ver lo que escribes.\n* Lo que ves aquí, lo que escuchas aquí, cuando sales, se queda aquí\n\n---\n# Como utilizarlo\n1. Escribe tu contenido en Markdown\n - Puedes aprender más sobre Markdown [aquí](http://www.markdowntutorial.com/)\n2. Separa tus slides con ---\n3. Haz clic en \"Presentar\" para ver el resultado - Tus slides se actualizan en tiempo real";
out.codeInitialState = "/*\n Esto es CryptPad, el editor colaborativo en tiempo real zero knowledge.\n Lo que escribes aquí está cifrado de manera que sólo las personas con el enlace pueden acceder a ello.\n Incluso el servidor no puede ver lo que escribes.\n Lo que ves aquí, lo que escuchas aquí, cuando sales, se queda aquí\n*/";
out.slideInitialState = "# CryptSlide\n* Esto es CryptPad, el editor colaborativo en tiempo real zero knowledge.\n* Lo que escribes aquí está cifrado de manera que sólo las personas con el enlace pueden acceder a ello.\n* Incluso el servidor no puede ver lo que escribes.\n* Lo que ves aquí, lo que escuchas aquí, cuando sales, se queda aquí\n\n---\n# Cómo utilizarlo\n1. Escribe tu contenido en Markdown\n - Puedes aprender más sobre Markdown [aquí](http://www.markdowntutorial.com/)\n2. Separa tus diapositivas con ---\n3. Haz clic en \"Presentar\" para ver el resultado - Tus diapositivas se actualizan en tiempo real";
out.driveReadmeTitle = "¿Qué es CryptDrive?";
out.readme_welcome = "¡Bienvenido a CryptPad!";
out.readme_p1 = "Bienvenido a CryptPad, aquí podrás tomar nota de cosas sólo u con otra gente.";
out.readme_p2 = "Este pad es un guía rapida para aprender a usar a CryptPad para tomar notas, organizarlas y trabajar con más personas.";
out.readme_cat1 = "Aprende a conocer a tu CryptDrive";
out.readme_p1 = "Bienvenido a CryptPad, aquí podrás anotar cosas solo o con otra gente.";
out.readme_p2 = "Este pad es una guía rápida para aprender a usar a CryptPad para tomar notas, organizarlas y trabajar con más personas.";
out.readme_cat1 = "Conoce tu CryptDrive";
out.readme_cat1_l1 = "Crea un pad: En CryptDrive, haz clic en {0} y luego en {1} para crear un pad.";
out.readme_cat1_l2 = "Abrir pads desde CryptDrive: haz doble clic en un icono para abrirlo.";
out.readme_cat1_l3 = "Organiza tus pads: Cuando has iniciado sesión, cada pad que accedes se quedaran en tu drive en {0}.";
out.readme_cat1_l3 = "Organiza tus pads: Cuando has iniciado sesión, cada pad al que accedes se quedará en tu drive en {0}.";
out.readme_cat1_l3_l1 = "Puedes hacer clic y arrastrar archivos en carpetas desde {0}, y crear nuevas carpetas.";
out.readme_cat1_l3_l2 = "Recuerda hacer clic derecho en varios iconos, ya que hay menús addicionales.";
out.readme_cat1_l3_l2 = "Recuerda hacer clic derecho en los iconos, ya que suele haber menús adicionales.";
out.readme_cat1_l4 = "Elimina tus viejos pads: Haz clic y arrastra tus pads en la {0} de la misma manera que lo harías con carpetas.";
out.readme_cat2 = "Haz pads como un pro";
out.edit = "editar";
out.view = "ver";
out.readme_cat2_l1 = "El botón {0} en tu pad te permite dar acceso a collaboradores para {1} o {2} el pad.";
out.readme_cat2_l1 = "El botón {0} en tu pad te permite dar acceso a colaboradores para {1} o {2} el pad.";
out.readme_cat2_l2 = "Cambia el título del pad haciendo clic en el lápiz";
out.readme_cat3 = "Descubre las apps CryptPad";
out.readme_cat3_l1 = "Con el editor de código CryptPad, puedes collaborar en código fuente, como por ejemplo JavaScript y Markdown";
out.readme_cat3_l1 = "Con el editor de código CryptPad, puedes colaborar en código fuente, como por ejemplo JavaScript y Markdown";
out.readme_cat3_l2 = "Con los slides CryptPad, puedes hacer presentaciones rápidas con Markdown";
out.readme_cat3_l3 = "Con CryptPoll puedes tomar votos rápidos, especialmente utíl para programar un horario que conviene a todo el mundo";
out.readme_cat3_l3 = "Con CryptPoll puedes hacer una encuesta rápida, especialmente útil para programar un horario que conviene a todo el mundo";
// 1.2.0 - Chupacabra
@ -337,25 +337,25 @@ define(function () {
out.printDate = "Mostrar la fecha";
out.printTitle = "Mostrar el título";
out.printCSS = "CSS personalizado:";
out.editOpen = "Abrir enlances de edición en pestaña nueva";
out.editOpen = "Abrir enlaces de edición en pestaña nueva";
out.editOpenTitle = "Abrir en modo edición en pestaña nueva";
out.settings_importTitle = "Importar pads recientes locales en CryptDrive";
out.settings_import = "Importar";
out.settings_importConfirm = "¿Seguro qué quieres importar tus pads recientes a tu cuenta CryptDrive?";
out.settings_importConfirm = "¿Seguro que quieres importar tus pads recientes a tu cuenta CryptDrive?";
out.settings_importDone = "Importación terminada";
out.tips = {};
out.tips.lag = "El icono verde en la parte superior derecha muestra la calidad de tu connexión a CryptPad.";
out.tips.shortcuts = "`ctrl+b`, `ctrl+i`, y `ctrl+u` son accesos rápidos para negrita, itálica y subrayado.";
out.tips.indent = "Cuando editas listas, puedes usar tab o shift+tab para icrementar o decrementar indentación.";
out.tips.indent = "Cuando editas listas, puedes usar tab o shift+tab para incrementar o decrementar la sangría.";
out.tips.title = "Puedes cambiar el título de tus pads en la parte superior de la pantalla.";
out.tips.store = "Cada vez que visitas un pad con una sesión iniciada se guardará a tu CryptDrive.";
out.tips.store = "Cada vez que visitas un pad con una sesión iniciada se guardará en tu CryptDrive.";
out.tips.marker = "Puedes resaltar texto en un pad utilizando el \"marcador\" en el menú de estílo.";
out.tips.driveUpload = "Usuarios registrados pueden subir archivos cifrados arrastrandolos hacia CryptDrive.";
out.tips.driveUpload = "Los usuarios registrados pueden subir archivos cifrados arrastrándolos hacia CryptDrive.";
out.feedback_about = "Si estas leyendo esto, quizas estés curioso de saber porqué CryptPad solicita esta página cuando haces algunas acciones";
out.feedback_privacy = "Nos importa tu privacidad, y al mismo tiempo queremos que CryptPad sea muy fácil de usar. Utilizamos esta página para conocer las funcionalidades que importan a nuestros usuarios, pidiendolo con un parametro que nos dice que accion fue realizada.";
out.feedback_optout = "Si quieres darte de baja, visita <a href='/settings/'>tus preferencias</a>, donde podrás activar o desactivar feedback";
out.feedback_about = "Si estas leyendo esto, quizás sientas curiosidad por saber por qué CryptPad solicita páginas cuando realizas algunas acciones";
out.feedback_privacy = "Nos importa tu privacidad, y al mismo tiempo queremos que CryptPad sea muy fácil de usar. Utilizamos este archivo para conocer las funcionalidades que importan a nuestros usuarios, pidiéndolo con un parametro que nos dice qué acción fue realizada.";
out.feedback_optout = "Si quieres darte de baja, visita <a href='/settings/'>tus preferencias</a>, donde podrás activar o desactivar el feedback";
out.fm_searchName = "Buscar";
out.fm_searchPlaceholder = "Buscar...";
@ -369,12 +369,12 @@ define(function () {
// 1.4.0 - Easter Bunny
out.button_newwhiteboard = "Nueva Pizarra";
out.wrongApp = "No se pudo mostrar el contenido de la sessión en tiempo real en tu navigador. Por favor, actualiza la página.";
out.wrongApp = "No se pudo mostrar el contenido de la sesión en tiempo real en tu navegador. Por favor, actualiza la página.";
out.synced = "Todo está guardado.";
out.saveTemplateButton = "Guardar como plantilla";
out.saveTemplatePrompt = "Élige un título para la plantilla";
out.saveTemplatePrompt = "Elige un título para la plantilla";
out.templateSaved = "¡Plantilla guardada!";
out.selectTemplate = "Élige una plantilla o pulsa ESC";
out.selectTemplate = "Elige una plantilla o pulsa ESC";
out.slideOptionsTitle = "Personaliza tus diapositivas";
out.slideOptionsButton = "Guardar (enter)";
out.canvas_clear = "Limpiar";
@ -385,8 +385,8 @@ define(function () {
out.canvas_opacity = "Opacidad";
out.settings_publicSigningKey = "Clave de Firma Pública";
out.settings_usage = "Utilización";
out.settings_usageTitle = "Vee el uso total de tus pads en MB";
out.settings_pinningNotAvailable = "Los pads pegados solo están disponibles para usuarios registrados.";
out.settings_usageTitle = "Ve el uso total de tus pads en MB";
out.settings_pinningNotAvailable = "Los pads pegados sólo están disponibles para usuarios registrados.";
out.settings_pinningError = "Algo salió mal";
out.settings_usageAmount = "Tus pads pegados utilizan {0}MB";
out.historyButton = "Mostrar el historial del documento";
@ -397,9 +397,9 @@ define(function () {
out.history_closeTitle = "Cerrar el historial";
out.history_restore = "Restaurar";
out.history_restoreTitle = "Restaurar la versión seleccionada del documento";
out.history_restorePrompt = "¿Estás seguro que quieres cambiar la versión actual del documento por esta?";
out.history_restorePrompt = "¿Estás seguro de que quieres cambiar la versión actual del documento por ésta?";
out.history_restoreDone = "Documento restaurado";
out.fc_sizeInKilobytes = "Talla en Kilobytes";
out.fc_sizeInKilobytes = "Tamaño en Kilobytes";
// 1.5.0/1.6.0 - Fenrir/Grootslang
@ -415,15 +415,15 @@ define(function () {
out.formattedGB = "{0} GB";
out.formattedKB = "{0} KB";
out.pinLimitReached = "Has llegado al limite de espacio";
out.pinLimitNotPinned = "Has llegado al limite de espacio.<br>Este pad no estará presente en tu CryptDrive.";
out.pinLimitDrive = "Has llegado al limite de espacio.<br>No puedes crear nuevos pads.";
out.pinLimitReached = "Has llegado al límite de espacio";
out.pinLimitNotPinned = "Has llegado al límite de espacio.<br>Este pad no estará presente en tu CryptDrive.";
out.pinLimitDrive = "Has llegado al límite de espacio.<br>No puedes crear nuevos pads.";
out.printTransition = "Activar transiciones";
out.history_version = "Versión: ";
out.settings_logoutEverywhereTitle = "Cerrar sessión en todas partes";
out.settings_logoutEverywhere = "Cerrar todas las otras sessiones";
out.settings_logoutEverywhereConfirm = "¿Estás seguro? Tendrás que volver a iniciar sessión con todos tus dispositivos.";
out.upload_serverError = "Error: no pudimos subir tu archivo.";
out.settings_logoutEverywhereTitle = "Cerrar sesión en todas partes";
out.settings_logoutEverywhere = "Cerrar todas las otras sesiones";
out.settings_logoutEverywhereConfirm = "¿Estás seguro? Tendrás que volver a iniciar sesión con todos tus dispositivos.";
out.upload_serverError = "Error: no se pudo subir tu archivo en este momento.";
out.upload_uploadPending = "Ya tienes una subida en progreso. ¿Cancelar y subir el nuevo archivo?";
out.upload_success = "Tu archivo ({0}) ha sido subido con éxito y fue añadido a tu drive.";
@ -432,11 +432,11 @@ define(function () {
out.newVersion = ["<b>CryptPad ha sido actualizado!</b>",
"Puedes ver lo que ha cambiado aquí (en inglés):",
"<a href=\"https://github.com/xwiki-labs/cryptpad/releases/tag/{0}\" target=\"_blank\">Notas de versión para CryptPad {0}</a>"].join("<br>");
out.pinLimitReachedAlertNoAccounts = "Has llegado a tu limite de espacio";
out.pinLimitReachedAlertNoAccounts = "Has llegado a tu límite de espacio";
out.previewButtonTitle = "Mostrar/esconder la vista previa Markdown";
out.fm_info_anonymous = "No estás conectado, así que estos pads pueden ser borrados (<a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">¿por qué?</a>). <a href=\"/register/\">Registrate</a> o <a href=\"/login/\">Inicia sesión</a> para asegurarlos.";
out.fm_alert_anonymous = "Hola, estás usando CryptPad anónimamente. Está bien, pero tus pads pueden ser borrados después de un périodo de inactividad. Hemos desactivado funciones avanzadas de CryptDrive para usuarios anónimos porque queremos ser claros que no es un lugar seguro para almacenar cosas. Puedes <a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">leer este articulo</a> (en inglés) sobre por qué hacemos esto y por qué deberías <a href=\"/register/\">Registrarte</a> e <a href=\"/login/\">Iniciar sesión</a>.";
out.fm_error_cantPin = "Error del servidor. Por favor, recarga la página e intentalo de nuevo.";
out.fm_info_anonymous = "No estás conectado, así que estos pads pueden ser borrados (<a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">¿por qué?</a>). <a href=\"/register/\">Regístrate</a> o <a href=\"/login/\">Inicia sesión</a> para asegurarlos.";
out.fm_alert_anonymous = "Hola, estás usando CryptPad anónimamente. Está bien, pero tus pads pueden ser borrados después de un périodo de inactividad. Hemos desactivado funciones avanzadas de CryptDrive para usuarios anónimos porque queremos dejar claro que no es un lugar seguro para almacenar cosas. Puedes <a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">leer este articulo</a> (en inglés) acerca de por qué hacemos esto y por qué deberías <a href=\"/register/\">Registrarte</a> e <a href=\"/login/\">Iniciar sesión</a>.";
out.fm_error_cantPin = "Error del servidor. Por favor, recarga la página e inténtalo de nuevo.";
out.upload_notEnoughSpace = "No tienes suficiente espacio para este archivo en tu CryptDrive";
out.upload_tooLarge = "Este archivo supera el límite de carga.";
out.upload_choose = "Escoge un archivo";
@ -446,7 +446,7 @@ define(function () {
out.upload_size = "Tamaño";
out.upload_progress = "Progreso";
out.download_button = "Descifrar y descargar";
out.warn_notPinned = "Este pad no está en ningun CryptDrive. Expirará después de 3 meses. <a href='/about.html#pinning'>Acerca de...</a>";
out.warn_notPinned = "Este pad no está en ningún CryptDrive. Expirará después de 3 meses. <a href='/about.html#pinning'>Acerca de...</a>";
out.poll_remove = "Quitar";
out.poll_edit = "Editar";
@ -458,13 +458,13 @@ define(function () {
// 1.8.0 - Idopogo
out.common_connectionLost = "<b>Connexión perdida</b><br>El documento está ahora en modo solo lectura hasta que la conexión vuelva.";
out.common_connectionLost = "<b>Conexión perdida</b><br>El documento está ahora en modo sólo lectura hasta que la conexión vuelva.";
out.updated_0_common_connectionLost = out.common_connectionLost;
out.supportCryptpad = "Ayudar a CryptPad";
out.pinLimitReachedAlert = ["Has llegado a tu limite de espacio. Nuevos pads no serán guardados en tu CryptDrive.",
out.pinLimitReachedAlert = ["Has llegado a tu límite de espacio. Los nuevos pads no serán guardados en tu CryptDrive.",
"Puedes eliminar pads de tu CryptDrive o <a href=\"https://accounts.cryptpad.fr/#!on={0}\" target=\"_blank\">suscribirte a una oferta premium</a> para obtener más espacio."].join("<br>");
out.updated_0_pinLimitReachedAlert = out.pinLimitReachedAlert;
out.fm_info_trash = "Vacía tu papelera para liberar espaci en tu CryptDrive.";
out.fm_info_trash = "Vacía tu papelera para liberar espacio en tu CryptDrive.";
out.updated_0_fm_info_trash = out.fm_info_trash;
out.fs_migration = "Tu CryptDrive fue actualizado a una nueva versión.<br><strong>Por favor, recarga la página.</strong>";
@ -485,5 +485,62 @@ define(function () {
out.canvas_opacityLabel = "Opacidad: {0}";
out.canvas_widthLabel = "Talla: {0}";
// 1.10.0 - Kraken
out.moreActions = "Más acciones";
out.importButton = "Importar";
out.exportButton = "Exportar";
out.saveTitle = "Guardar título (enter)";
out.forgetButton = "Eliminar";
out.printText = "Imprimir";
out.slideOptionsText = "Opciones";
out.historyText = "Historial";
out.openLinkInNewTab = "Abrir enlace en pestaña nueva";
out.profileButton = "Perfil";
out.profile_urlPlaceholder = "URL";
out.profile_namePlaceholder = "Nombre mostrado en su perfil";
out.profile_avatar = "Imagen";
out.profile_upload = "Subir una imagen";
out.profile_error = "Error al crear tu perfil: {0}";
out.profile_register = "Tienes que registrarte para crear un perfil";
out.profile_create = "Crear perfil";
out.profile_description = "Descripción";
out.profile_fieldSaved = "Guardado: {0}";
out.download_mt_button = "Descargar";
out.updated_0_header_logoTitle = "Volver a tu CryptDrive";
out.header_logoTitle = out.updated_0_header_logoTitle;
// 1.11.0 - Lutin
out.realtime_unrecoverableError = "El motor de tiempo real ha encontrado un error. Haga clic en OK para recargar la página.";
out.typing = "Escribiendo";
out.profile_inviteButton = "Conectar";
out.profile_inviteButtonTitle = "Crear un enlace de invitación para este usuario.";
out.profile_inviteExplanation = "Hacer clic en <strong>OK</strong> creará un enlace de mensaje seguro que <em>sólo {0} podrá ver.</em><br><br>El enlace será copiado a tu portapapeles y puede ser compartido públicamente.";
out.profile_viewMyProfile = "Ver mi perfil";
out.userlist_addAsFriendTitle = 'Agregar "{0}" como contacto';
out.userlist_thisIsYou = 'Tú mismo ("{0}")';
out.contacts_title = "Contactos";
out.contacts_addError = "Error al agregar este contacto a la lista";
out.contacts_added = "Invitación aceptada";
out.contacts_rejected = "Invitación denegada";
out.contacts_request = "<em>{0}</em> quiere agregarte como contacto. ¿<b>Aceptar</b>?";
out.contacts_send = "Enviar";
out.contacts_remove = "Eliminar este contacto";
out.contacts_confirmRemove = "¿Estás seguro de que quieres eliminar <em>{0}</em> de tus contactos?";
out.contacts_info1 = "Estos son tus contactos. Desde aquí, puedes:";
out.contacts_info2 = "Hacer clic en el icono de tu contacto para chatear";
out.contacts_info3 = "Hacer doble-clic para ver su perfil";
out.contacts_info4 = "Cualquier participante puede eliminar definitivamente el historial de chat";
out.settings_cat_account = "Cuenta";
out.settings_cat_drive = "CryptDrive";
out.settings_backupCategory = "Copia de seguridad";
out.settings_resetNewTitle = "Limpiar CryptDrive";
out.settings_resetButton = "Eliminar";
out.settings_resetTipsAction = "Reiniciar";
out.settings_userFeedbackTitle = "Feedback";
out.settings_logoutEverywhereButton = "Cerrar sesión";
out.upload_title = "Subir archivo";
return out;
});

@ -9,10 +9,11 @@ define(function () {
out.type.code = 'Code';
out.type.poll = 'Sondage';
out.type.slide = 'Présentation';
out.type.drive = 'Drive';
out.type.drive = 'CryptDrive';
out.type.whiteboard = "Tableau Blanc";
out.type.file = "Fichier";
out.type.media = "Média";
out.type.contacts = "Contacts";
out.button_newpad = 'Nouveau document texte';
out.button_newcode = 'Nouvelle page de code';
@ -34,9 +35,12 @@ define(function () {
out.synced = "Tout est enregistré";
out.deleted = "Pad supprimé de votre CryptDrive";
out.realtime_unrecoverableError = "Le moteur temps-réel a rencontré une erreur critique. Cliquez sur OK pour recharger la page.";
out.disconnected = 'Déconnecté';
out.synchronizing = 'Synchronisation';
out.reconnecting = 'Reconnexion...';
out.typing = "Édition";
out.lag = 'Latence';
out.readonly = 'Lecture seule';
out.anonymous = "Anonyme";
@ -74,7 +78,7 @@ define(function () {
out.greenLight = "Tout fonctionne bien";
out.orangeLight = "Votre connexion est lente, ce qui réduit la qualité de l'éditeur";
out.redLight = "Vous êtes déconnectés de la session";
out.redLight = "Vous êtes déconnecté de la session";
out.pinLimitReached = "Vous avez atteint votre limite de stockage";
out.updated_0_pinLimitReachedAlert = "Vous avez atteint votre limite de stockage. Les nouveaux pads ne seront pas enregistrés dans votre CryptDrive.<br>" +
@ -86,8 +90,12 @@ define(function () {
out.pinLimitDrive = out.pinLimitReached+ ".<br>" +
"Vous ne pouvez pas créer de nouveaux pads.";
out.moreActions = "Autres actions";
out.importButton = "Importer";
out.importButtonTitle = 'Importer un pad depuis un fichier local';
out.exportButton = "Exporter";
out.exportButtonTitle = 'Exporter ce pad vers un fichier local';
out.exportPrompt = 'Comment souhaitez-vous nommer ce fichier ?';
@ -97,7 +105,9 @@ define(function () {
out.user_accountName = "Nom d'utilisateur";
out.clickToEdit = 'Cliquer pour modifier';
out.saveTitle = "Enregistrer le titre (Entrée)";
out.forgetButton = "Supprimer";
out.forgetButtonTitle = 'Déplacer ce pad vers la corbeille';
out.forgetPrompt = 'Cliquer sur OK déplacera ce pad vers la corbeille de votre CryptDrive, êtes-vous sûr ?';
out.movedToTrash = 'Ce pad a été déplacé vers la corbeille.<br><a href="/drive/">Accéder à mon Drive</a>';
@ -107,8 +117,8 @@ define(function () {
out.newButton = 'Nouveau';
out.newButtonTitle = 'Créer un nouveau pad';
out.uploadButton = 'Upload';
out.uploadButtonTitle = 'Uploader un nouveau fichier dans le dossier actuel';
out.uploadButton = 'Importer des fichiers';
out.uploadButtonTitle = 'Importer un nouveau fichier dans le dossier actuel';
out.saveTemplateButton = "Sauver en tant que modèle";
out.saveTemplatePrompt = "Choisir un titre pour ce modèle";
@ -123,6 +133,7 @@ define(function () {
out.backgroundButtonTitle = 'Changer la couleur de fond de la présentation';
out.colorButtonTitle = 'Changer la couleur du texte en mode présentation';
out.printText = "Imprimer";
out.printButton = "Imprimer (Entrée)";
out.printButtonTitle = "Imprimer votre présentation ou l'enregistrer au format PDF";
out.printOptions = "Options de mise en page";
@ -138,6 +149,7 @@ define(function () {
out.filePicker_filter = "Filtrez les fichiers par leur nom";
out.or = 'ou';
out.slideOptionsText = "Options";
out.slideOptionsTitle = "Personnaliser la présentation";
out.slideOptionsButton = "Enregistrer (Entrée)";
@ -164,6 +176,7 @@ define(function () {
out.cancel = "Annuler";
out.cancelButton = 'Annuler (Echap)';
out.historyText = "Historique";
out.historyButton = "Afficher l'historique du document";
out.history_next = "Voir la version suivante";
out.history_prev = "Voir la version précédente";
@ -176,6 +189,9 @@ define(function () {
out.history_restoreDone = "Document restauré";
out.history_version = "Version :";
// Ckeditor links
out.openLinkInNewTab = "Ouvrir le lien dans un nouvel onglet";
// Polls
out.poll_title = "Sélecteur de date Zero Knowledge";
@ -228,6 +244,38 @@ define(function () {
out.canvas_opacityLabel = "opacité: {0}";
out.canvas_widthLabel = "taille: {0}";
// Profile
out.profileButton = "Profil"; // dropdown menu
out.profile_urlPlaceholder = 'URL';
out.profile_namePlaceholder = 'Nom ou pseudo pour le profil';
out.profile_avatar = "Avatar";
out.profile_upload = " Importer un nouvel avatar";
out.profile_error = "Erreur lors de la création du profil : {0}";
out.profile_register = "Vous devez vous inscrire pour pouvoir créer un profil !";
out.profile_create = "Créer un profil";
out.profile_description = "Description";
out.profile_fieldSaved = 'Nouvelle valeur enregistrée: {0}';
out.profile_viewMyProfile = "Voir mon profil";
// contacts/userlist
out.userlist_addAsFriendTitle = 'Ajouter "{0}" comme contact';
out.userlist_thisIsYou = 'Vous ("{0}")';
out.contacts_title = "Contacts";
out.contacts_addError = "Erreur lors de l'ajout de ce contact dans votre liste";
out.contacts_added = 'Invitation de contact acceptée';
out.contacts_rejected = 'Invitation d econtact rejetée';
out.contacts_request = '<em>{0}</em> souhaite vous ajouter en tant que contact. <b>Accepter<b> ?';
out.contacts_send = 'Envoyer';
out.contacts_remove = 'Supprimer ce contact';
out.contacts_confirmRemove = 'Êtes-vous sûr de voulour supprimer <em>{0}</em> de vos contacts ?';
out.contacts_info1 = "Voici vos contacts. Ici, vous pouvez :";
out.contacts_info2 = "Cliquer sur le nom d'un contact pour discuter avec lui";
out.contacts_info3 = "Double-cliquer sur son nom pour voir son profil";
out.contacts_info4 = "Chaque participant peut nettoyer définitivement l'historique d'une discussion";
// File manager
out.fm_rootName = "Documents";
@ -238,7 +286,7 @@ define(function () {
out.fm_searchName = "Recherche";
out.fm_searchPlaceholder = "Rechercher...";
out.fm_newButton = "Nouveau";
out.fm_newButtonTitle = "Créer un nouveau pad ou un dossier";
out.fm_newButtonTitle = "Créer un nouveau pad ou un dossier, importer un fichier dans le dossier courant";
out.fm_newFolder = "Nouveau dossier";
out.fm_newFile = "Nouveau pad";
out.fm_folder = "Dossier";
@ -270,12 +318,12 @@ define(function () {
out.updated_0_fm_info_trash = "Vider la corbeille permet de libérer de l'espace dans votre CryptDrive";
out.fm_info_trash = out.updated_0_fm_info_trash;
out.fm_info_allFiles = 'Contient tous les fichiers de "Documents", "Fichiers non triés" et "Corbeille". Vous ne pouvez pas supprimer ou déplacer des fichiers depuis cet endroit.'; // Same here
out.fm_info_anonymous = 'Vous n\'êtes pas connectés, ces pads risquent donc d\'être supprimés (<a href="https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/" target="_blank">découvrez pourquoi</a>). ' +
out.fm_info_anonymous = 'Vous n\'êtes pas connecté, ces pads risquent donc d\'être supprimés (<a href="https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/" target="_blank">découvrez pourquoi</a>). ' +
'<a href="/register/">Inscrivez-vous</a> ou <a href="/login/">connectez-vous</a> pour les maintenir en vie.';
out.fm_alert_backupUrl = "Lien de secours pour ce disque.<br>" +
out.fm_alert_backupUrl = "Lien de secours pour ce CryptDrive.<br>" +
"Il est <strong>fortement recommandé</strong> de garder ce lien pour vous-même.<br>" +
"Elle vous servira en cas de perte des données de votre navigateur afin de retrouver vos fichiers.<br>" +
"Quiconque se trouve en possession de celle-ci peut modifier ou supprimer tous les fichiers de ce gestionnaire.<br>";
"Il vous servira en cas de perte des données de votre navigateur afin de retrouver vos fichiers.<br>" +
"Quiconque se trouve en possession de celui-ci peut modifier ou supprimer tous les fichiers de ce gestionnaire.<br>";
out.fm_alert_anonymous = "Bonjour ! Vous utilisez actuellement Cryptpad de manière anonyme, ce qui ne pose pas de problème mais vos pads peuvent être supprimés après un certain temps " +
"d'inactivité. Nous avons désactivé certaines fonctionnalités avancées de CryptDrive pour les utilisateurs anonymes afin de rendre clair le fait que ce n'est pas " +
'un endroit sûr pour le stockage des documents. Vous pouvez <a href="https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/" target="_blank">en lire plus</a> concernant ' +
@ -351,28 +399,37 @@ define(function () {
];
// Settings
out.settings_cat_account = "Compte";
out.settings_cat_drive = "CryptDrive";
out.settings_title = "Préférences";
out.settings_save = "Sauver";
out.settings_backupCategory = "Sauvegarde";
out.settings_backupTitle = "Créer ou restaurer une sauvegarde de vos données";
out.settings_backup = "Créer une sauvegarde";
out.settings_restore = "Restaurer une sauvegarde";
out.settings_resetTitle = "Vider votre drive";
out.settings_backup = "Sauvegarder";
out.settings_restore = "Restaurer";
out.settings_resetNewTitle = "Vider CryptDrive";
out.settings_resetButton = "Supprimer";
out.settings_reset = "Supprimer tous les fichiers et dossiers de votre CryptDrive";
out.settings_resetPrompt = "Cette action va supprimer tous les pads de votre drive.<br>"+
"Êtes-vous sûr de vouloir continuer ?<br>" +
"Tapez “<em>I love CryptPad</em>” pour confirmer.";
out.settings_resetDone = "Votre drive est désormais vide!";
out.settings_resetError = "Texte de vérification incorrect. Votre CryptDrive n'a pas été modifié.";
out.settings_resetTips = "Astuces et informations dans CryptDrive";
out.settings_resetTipsAction ="Réinitialiser";
out.settings_resetTips = "Astuces";
out.settings_resetTipsButton = "Réinitialiser les astuces visibles dans CryptDrive";
out.settings_resetTipsDone = "Toutes les astuces sont de nouveau visibles.";
out.settings_importTitle = "Importer les pads récents de ce navigateur dans mon CryptDrive";
out.settings_importTitle = "Importer les pads récents de ce navigateur dans votre CryptDrive";
out.settings_import = "Importer";
out.settings_importConfirm = "Êtes-vous sûr de vouloir importer les pads récents de ce navigateur dans le CryptDrive de votre compte utilisateur ?";
out.settings_importDone = "Importation terminée";
out.settings_userFeedbackHint1 = "CryptPad peut envoyer des retours d'expérience très limités vers le serveur, de manière à nous permettre d'améliorer l'expérience des utilisateurs.";
out.settings_userFeedbackTitle = "Retour d'expérience";
out.settings_userFeedbackHint1 = "CryptPad peut envoyer des retours d'expérience très limités vers le serveur, de manière à nous permettre d'améliorer l'expérience des utilisateurs. ";
out.settings_userFeedbackHint2 = "Le contenu de vos pads et les clés de déchiffrement ne seront jamais partagés avec le serveur.";
out.settings_userFeedback = "Activer l'envoi de retours d'expérience";
@ -385,13 +442,15 @@ define(function () {
out.settings_pinningError = "Un problème est survenu";
out.settings_usageAmount = "Vos pads épinglés occupent {0} Mo";
out.settings_logoutEverywhereButton = "Se déconnecter";
out.settings_logoutEverywhereTitle = "Se déconnecter partout";
out.settings_logoutEverywhere = "Se déconnecter de toutes les autres sessions.";
out.settings_logoutEverywhere = "Se déconnecter de force de toutes les autres sessions.";
out.settings_logoutEverywhereConfirm = "Êtes-vous sûr ? Vous devrez vous reconnecter sur tous vos autres appareils.";
out.upload_serverError = "Erreur interne: impossible d'uploader le fichier pour l'instant.";
out.upload_uploadPending = "Vous avez déjà un fichier en cours d'upload. Souhaitez-vous l'annuler et uploader ce nouveau fichier ?";
out.upload_success = "Votre fichier ({0}) a été uploadé avec succès et ajouté à votre CryptDrive.";
out.upload_title = "Hébergement de fichiers";
out.upload_serverError = "Erreur interne: impossible d'importer le fichier pour l'instant.";
out.upload_uploadPending = "Vous avez déjà un fichier en cours d'importation. Souhaitez-vous l'annuler et importer ce nouveau fichier ?";
out.upload_success = "Votre fichier ({0}) a été importé avec succès et ajouté à votre CryptDrive.";
out.upload_notEnoughSpace = "Il n'y a pas assez d'espace libre dans votre CryptDrive pour ce fichier.";
out.upload_tooLarge = "Ce fichier dépasse la taille maximale autorisée.";
out.upload_choose = "Choisir un fichier";
@ -400,8 +459,9 @@ define(function () {
out.upload_name = "Nom du fichier";
out.upload_size = "Taille";
out.upload_progress = "État";
out.upload_mustLogin = "Vous devez vous connecter pour uploader un fichier";
out.upload_mustLogin = "Vous devez vous connecter pour importer un fichier";
out.download_button = "Déchiffrer et télécharger";
out.download_mt_button = "Télécharger";
// general warnings
out.warn_notPinned = "Ce pad n'est stocké dans aucun CryptDrive. Il va expirer après 3 mois d'inactivité. <a href='/about.html#pinning'>En savoir plus...</a>";
@ -484,7 +544,8 @@ define(function () {
out.header_france = '<a href="http://www.xwiki.com/fr" target="_blank" rel="noopener noreferrer">Fait avec <img class="bottom-bar-heart" src="/customize/heart.png" alt="amour" /> en <img class="bottom-bar-fr" title="France" alt="France" src="/customize/fr.png" /> par <img src="/customize/logo-xwiki.png" alt="XWiki SAS" class="bottom-bar-xwiki"/></a>';
out.header_support = '<a href="http://ng.open-paas.org/" title="OpenPaaS::ng" target="_blank" rel="noopener noreferrer"> <img src="/customize/openpaasng.png" alt="OpenPaaS-ng" class="bottom-bar-openpaas" /></a>';
out.header_logoTitle = "Aller vers la page d'accueil";
out.updated_0_header_logoTitle = 'Retourner vers votre CryptDrive';
out.header_logoTitle = out.updated_0_header_logoTitle;
// Initial states
@ -492,12 +553,12 @@ define(function () {
'<span style="font-size:16px;"><p>',
'Voici <strong>CryptPad</strong>, l\'éditeur collaboratif en temps-réel Zero Knowledge. Tout est sauvegardé dés que vous le tapez.',
'<br>',
'Partagez le lien vers ce pad avec des amis ou utilisez le bouton <span style="background-color:#5cb85c;color:#ffffff;">&nbsp;Partager&nbsp;</span> pour obtenir le <em>lien de lecture-seule</em>, qui permet la lecture mais non la modification.',
'Partagez le lien vers ce pad avec des amis ou utilisez le bouton <span class="fa fa-share-alt" style="border: 1px solid black;color:#000;">&nbsp;Partager&nbsp;</span> pour obtenir le <em>lien de lecture-seule</em>, qui permet la lecture mais non la modification.',
'</p>',
'<p><span style="color:#808080; font-size: 16px;">',
'<p>',
'<em>',
'Lancez-vous, commencez à taper...',
'</em></span></p></span>',
'</em></p></span>',
'<p>&nbsp;<br></p>'
].join('');

@ -9,10 +9,11 @@ define(function () {
out.type.code = 'Code';
out.type.poll = 'Poll';
out.type.slide = 'Presentation';
out.type.drive = 'Drive';
out.type.drive = 'CryptDrive';
out.type.whiteboard = 'Whiteboard';
out.type.file = 'File';
out.type.media = 'Media';
out.type.contacts = 'Contacts';
out.button_newpad = 'New Rich Text pad';
out.button_newcode = 'New Code pad';
@ -36,9 +37,12 @@ define(function () {
out.synced = "Everything is saved";
out.deleted = "Pad deleted from your CryptDrive";
out.realtime_unrecoverableError = "The realtime engine has encountered an unrecoverable error. Click OK to reload.";
out.disconnected = 'Disconnected';
out.synchronizing = 'Synchronizing';
out.reconnecting = 'Reconnecting...';
out.typing = "Typing";
out.lag = 'Lag';
out.readonly = 'Read only';
out.anonymous = "Anonymous";
@ -88,8 +92,12 @@ define(function () {
out.pinLimitDrive = "You've reached your storage limit.<br>" +
"You can't create new pads.";
out.moreActions = "More actions";
out.importButton = "Import";
out.importButtonTitle = 'Import a pad from a local file';
out.exportButton = "Export";
out.exportButtonTitle = 'Export this pad to a local file';
out.exportPrompt = 'What would you like to name your file?';
@ -99,7 +107,9 @@ define(function () {
out.user_accountName = "Account name";
out.clickToEdit = "Click to edit";
out.saveTitle = "Save the title (enter)";
out.forgetButton = "Delete";
out.forgetButtonTitle = 'Move this pad to the trash';
out.forgetPrompt = 'Clicking OK will move this pad to your trash. Are you sure?';
out.movedToTrash = 'That pad has been moved to the trash.<br><a href="/drive/">Access my Drive</a>';
@ -109,7 +119,7 @@ define(function () {
out.newButton = 'New';
out.newButtonTitle = 'Create a new pad';
out.uploadButton = 'Upload';
out.uploadButton = 'Upload files';
out.uploadButtonTitle = 'Upload a new file to the current folder';
out.saveTemplateButton = "Save as template";
@ -125,6 +135,7 @@ define(function () {
out.backgroundButtonTitle = 'Change the background color in the presentation';
out.colorButtonTitle = 'Change the text color in presentation mode';
out.printText = "Print";
out.printButton = "Print (enter)";
out.printButtonTitle = "Print your slides or export them as a PDF file";
out.printOptions = "Layout options";
@ -140,6 +151,7 @@ define(function () {
out.filePicker_filter = "Filter files by name";
out.or = 'or';
out.slideOptionsText = "Options";
out.slideOptionsTitle = "Customize your slides";
out.slideOptionsButton = "Save (enter)";
@ -166,6 +178,7 @@ define(function () {
out.cancel = "Cancel";
out.cancelButton = 'Cancel (esc)';
out.historyText = "History";
out.historyButton = "Display the document history";
out.history_next = "Go to the next version";
out.history_prev = "Go to the previous version";
@ -178,6 +191,9 @@ define(function () {
out.history_restoreDone = "Document restored";
out.history_version = "Version:";
// Ckeditor links
out.openLinkInNewTab = "Open Link in New Tab";
// Polls
out.poll_title = "Zero Knowledge Date Picker";
@ -229,7 +245,41 @@ define(function () {
out.canvas_opacity = "Opacity";
out.canvas_opacityLabel = "opacity: {0}";
out.canvas_widthLabel = "Width: {0}";
// Profile
out.profileButton = "Profile"; // dropdown menu
out.profile_urlPlaceholder = 'URL';
out.profile_namePlaceholder = 'Name displayed in your profile';
out.profile_avatar = "Avatar";
out.profile_upload = " Upload a new avatar";
out.profile_error = "Error while creating your profile: {0}";
out.profile_register = "You have to sign up to create a profile!";
out.profile_create = "Create a profile";
out.profile_description = "Description";
out.profile_fieldSaved = 'New value saved: {0}';
out.profile_inviteButton = "Connect";
out.profile_inviteButtonTitle ='Create a link that will invite this user to connect with you.';
out.profile_inviteExplanation = "Clicking <strong>OK</strong> will create a link to a secure messaging session that <em>only {0} will be able to redeem.</em><br><br>The link will be copied to your clipboard and can be shared publicly.";
out.profile_viewMyProfile = "View my profile";
// contacts/userlist
out.userlist_addAsFriendTitle = 'Add "{0}" as a contact';
out.userlist_thisIsYou = 'This is you ("{0}")';
out.contacts_title = "Contacts";
out.contacts_addError = 'Error while adding that contact to the list';
out.contacts_added = 'Contact invite accepted.';
out.contacts_rejected = 'Contact invite rejected';
out.contacts_request = '<em>{0}</em> would like to add you as a contact. <b>Accept<b>?';
out.contacts_send = 'Send';
out.contacts_remove = 'Remove this contact';
out.contacts_confirmRemove = 'Are you sure you want to remove <em>{0}</em> from your contacts?';
out.contacts_info1 = "These are your contacts. From here, you can:";
out.contacts_info2 = "Click your contact's icon to chat with them";
out.contacts_info3 = "Double-click their icon to view their profile";
out.contacts_info4 = "Either participant can clear permanently a chat history";
// File manager
@ -241,7 +291,7 @@ define(function () {
out.fm_searchName = "Search";
out.fm_searchPlaceholder = "Search...";
out.fm_newButton = "New";
out.fm_newButtonTitle = "Create a new pad or folder";
out.fm_newButtonTitle = "Create a new pad or folder, import a file in the current folder";
out.fm_newFolder = "New folder";
out.fm_newFile = "New pad";
out.fm_folder = "Folder";
@ -357,28 +407,37 @@ define(function () {
out.register_alreadyRegistered = "This user already exists, do you want to log in?";
// Settings
out.settings_cat_account = "Account";
out.settings_cat_drive = "CryptDrive";
out.settings_title = "Settings";
out.settings_save = "Save";
out.settings_backupCategory = "Backup";
out.settings_backupTitle = "Backup or restore all your data";
out.settings_backup = "Backup";
out.settings_restore = "Restore";
out.settings_resetTitle = "Clean your drive";
out.settings_resetNewTitle = "Clean CryptDrive";
out.settings_resetButton = "Remove";
out.settings_reset = "Remove all the files and folders from your CryptDrive";
out.settings_resetPrompt = "This action will remove all the pads from your drive.<br>"+
"Are you sure you want to continue?<br>" +
"Type “<em>I love CryptPad</em>” to confirm.";
out.settings_resetDone = "Your drive is now empty!";
out.settings_resetError = "Incorrect verification text. Your CryptDrive has not been changed.";
out.settings_resetTips = "Tips in CryptDrive";
out.settings_resetTipsAction = "Reset";
out.settings_resetTips = "Tips";
out.settings_resetTipsButton = "Reset the available tips in CryptDrive";
out.settings_resetTipsDone = "All the tips are now visible again.";
out.settings_importTitle = "Import this browser's recent pads in my CryptDrive";
out.settings_importTitle = "Import this browser's recent pads in your CryptDrive";
out.settings_import = "Import";
out.settings_importConfirm = "Are you sure you want to import recent pads from this browser to your user account's CryptDrive?";
out.settings_importDone = "Import completed";
out.settings_userFeedbackHint1 = "CryptPad provides some very basic feedback to the server, to let us know how to improve your experience.";
out.settings_userFeedbackTitle = "Feedback";
out.settings_userFeedbackHint1 = "CryptPad provides some very basic feedback to the server, to let us know how to improve your experience. ";
out.settings_userFeedbackHint2 = "Your pad's content will never be shared with the server.";
out.settings_userFeedback = "Enable user feedback";
@ -391,10 +450,12 @@ define(function () {
out.settings_pinningError = "Something went wrong";
out.settings_usageAmount = "Your pinned pads occupy {0}MB";
out.settings_logoutEverywhereButton = "Log out";
out.settings_logoutEverywhereTitle = "Log out everywhere";
out.settings_logoutEverywhere = "Log out of all other web sessions";
out.settings_logoutEverywhere = "Force log out of all other web sessions";
out.settings_logoutEverywhereConfirm = "Are you sure? You will need to log in with all your devices.";
out.upload_title = "File upload";
out.upload_serverError = "Server Error: unable to upload your file at this time.";
out.upload_uploadPending = "You already have an upload in progress. Cancel it and upload your new file?";
out.upload_success = "Your file ({0}) has been successfully uploaded and added to your drive.";
@ -408,6 +469,7 @@ define(function () {
out.upload_progress = "Progress";
out.upload_mustLogin = "You must be logged in to upload files";
out.download_button = "Decrypt & Download";
out.download_mt_button = "Download";
// general warnings
out.warn_notPinned = "This pad is not in anyone's CryptDrive. It will expire after 3 months. <a href='/about.html#pinning'>Learn more...</a>";
@ -494,7 +556,8 @@ define(function () {
out.header_france = '<a href="http://www.xwiki.com/" target="_blank" rel="noopener noreferrer">With <img class="bottom-bar-heart" src="/customize/heart.png" alt="love" /> from <img class="bottom-bar-fr" src="/customize/fr.png" title="France" alt="France"/> by <img src="/customize/logo-xwiki.png" alt="XWiki SAS" class="bottom-bar-xwiki"/></a>';
out.header_support = '<a href="http://ng.open-paas.org/" title="OpenPaaS::ng" target="_blank" rel="noopener noreferrer"> <img src="/customize/openpaasng.png" alt="OpenPaaS-ng" class="bottom-bar-openpaas" /></a>';
out.header_logoTitle = 'Go to the main page';
out.updated_0_header_logoTitle = 'Go to your CryptDrive';
out.header_logoTitle = out.updated_0_header_logoTitle;
// Initial states
@ -502,12 +565,12 @@ define(function () {
'<span style="font-size:16px;"><p>',
'This is&nbsp;<strong>CryptPad</strong>, the Zero Knowledge realtime collaborative editor. Everything is saved as you type.',
'<br>',
'Share the link to this pad to edit with friends or use the <span style="background-color:#5cb85c;color:#ffffff;">&nbsp;Share&nbsp;</span> button to share a <em>read-only link</em>&nbsp;which allows viewing but not editing.',
'Share the link to this pad to edit with friends or use the <span class="fa fa-share-alt" style="border: 1px solid black;color:#000;">&nbsp;Share&nbsp;</span> button to share a <em>read-only link</em>&nbsp;which allows viewing but not editing.',
'</p>',
'<p><span style="color:#808080;"><em>',
'<p><em>',
'Go ahead, just start typing...',
'</em></span></p></span>',
'</em></p></span>',
'<p>&nbsp;<br></p>'
].join('');

@ -1,5 +1,5 @@
// Tradução para protuguês brasileiro efetuada por Gustavo Henrique Machado da Silva (www.linkedin.com/in/gustavohmsilva)
// Embora o software original possa não possuir as mesmas licenças, a tradução produzida por mim is protected under
// Embora o software original possa não possuir as mesmas licenças, a tradução produzida por mim é protegida sob termos
// Creative Commons, Attribution-ShareAlike 4.0 International
// Contate-me via email no endereço gustavohmsilva@member.fsf.org
// Translation to brazilian portuguese done by Gustavo Henrique Machado da Silva (www.linkedin.com/in/gustavohmsilva)
@ -9,8 +9,6 @@
define(function () {
var out = {};
// translations must set this key for their language to be available in
// the language dropdowns that are shown throughout Cryptpad's interface
out._languageName = 'Brazilian Portuguese';
out.main_title = "Cryptpad: Zero Knowledge, Edição Colaborativa em Tempo Real";
@ -22,7 +20,33 @@ define(function () {
out.type.poll = 'votação';
out.type.slide = 'Apresentação';
out.common_connectionLost = 'Conexão Perdida com o servidor';
out.type.drive = 'Drive';
out.type.whiteboard = 'Whiteboard';
out.type.file = 'File';
out.type.media = 'Media';
out.button_newpad = 'Novo bloco RTF';
out.button_newcode = 'Novo bloco de código';
out.button_newpoll = 'Novo questionário';
out.button_newslide = 'Nova apresentação';
out.button_newwhiteboard = 'Novo quadro branco';
// NOTE: We want to update the 'common_connectionLost' key.
// Please do not add a new 'updated_common_connectionLostAndInfo' but change directly the value of 'common_connectionLost'
out.updated_0_common_connectionLost = "<b>Conexão com o Servidor Perdida</b><br>Você agora está em modo somente leitura até a conexão ser restaurada.";
out.common_connectionLost = out.updated_0_common_connectionLost;
out.websocketError = 'Incapaz de se conectar com o servidor websocket...';
out.typeError = "Este bloco não é compatível com a aplicação selecionada";
out.onLogout = 'você foi desconectado, <a href="/" target="_blank">clique aqui</a> para se conectar, <br>ou pressione <em>ESC</em> para acessar seu bloco em modo somente leitura.';
out.wrongApp = "Incapaz de mostrar o conteúdo em tempo real no seu navegador. Por favor tente recarregar a página.";
out.loading = "Carregando...";
out.error = "Erro";
out.saved = "Salvo";
out.synced = "Tudo foi salvo";
out.deleted = "Bloco deletado do seu CryptDrive";
out.disconnected = 'Desconectado';
out.synchronizing = 'Sincronizando';
@ -40,45 +64,117 @@ define(function () {
out.editor = "editor";
out.editors = "editores";
out.language = "Lingua";
out.comingSoon = "Em breve...";
out.newVersion = '<b>O CryptPad foi atualizado!</b><br>' +
'Cheque as novidades na última versão:<br>'+
'<a href="https://github.com/xwiki-labs/cryptpad/releases/tag/{0}" target="_blank">Notas da atualização do CryptPad {0}</a>';
out.upgrade = "Upgrade";
out.upgradeTitle = "Faça um upgrade na sua conta para aumentar o limite de armazenamento";
out.MB = "MB";
out.GB = "GB";
out.KB = "KB";
out.formattedMB = "{0} MB";
out.formattedGB = "{0} GB";
out.formattedKB = "{0} KB";
out.greenLight = "Tudo está funcionando bem";
out.orangeLight = "Sua conexão longa pode impactar sua experiência";
out.orangeLight = "Sua conexão lenta pode impactar sua experiência";
out.redLight = "Você está desconectado da sua sessão";
out.pinLimitReached = "Você alcançou o limite de armazenamento";
out.updated_0_pinLimitReachedAlert = "Você alcançou o limite de armazenamento. Novos blocos não serão mais salvos no seu CryptDrive.<br>" +
'Você pode deletar blocos do seu CryptDrive ou <a href="https://accounts.cryptpad.fr/#!on={0}" target="_blank">se inscrever como premium</a> para aumentar o limite de espaço.';
out.pinLimitReachedAlert = out.updated_0_pinLimitReachedAlert;
out.pinAboveLimitAlert = 'A partir desta atualização, nós estamos impondo um limite de 50MB no armazenamento gratuito. Você está atualmente usando {0}. Você irá precisar deletar alguns blocos ou se inscrever no <a href="https://accounts.cryptpad.fr/#!on={1}" target="_blank">accounts.cryptpad.fr</a>. Sua contribuição irá nos ajudar a melhorar o CryptPad e expandir a metodologia Zero Knowledge. Por favor contacte o <a href="https://accounts.cryptpad.fr/#/support" target="_blank">suporte</a> se você possui outras dúvidas.';
out.pinLimitNotPinned = "Você alcançou o limite de armazenamento.<br>"+
"Este bloco não está armazenado no seu CryptDrive.";
out.pinLimitDrive = "Você alcançou o limite de armazenamento.<br>" +
"Você não pode criar novos blocos.";
out.importButtonTitle = 'Importar um documento de um arquivo local';
out.exportButtonTitle = 'Exportar esta sesão para um arquivo local';
out.exportPrompt = 'Como deseja nomeear seu arquivo?';
out.exportPrompt = 'Como deseja nomear seu arquivo?';
out.changeNamePrompt = 'Mude seu nome (deixe em branco para se manter anônimo): ';
out.user_rename = "Mudar nome de exibição";
out.user_displayName = "Nome visível";
out.user_accountName = "Nome da Conta";
out.clickToEdit = "Clique para Editar";
out.forgetButtonTitle = 'Remova este documento da listagem da sua página';
out.forgetPrompt = 'Clicando OK você irá remover o endereço deste bloco de notas do armazenamento local, você tem certeza?';
out.movedToTrash = 'That pad has been moved to the trash.<br><a href="/drive/">Access my Drive</a>';
out.shareButton = 'Compartilhar';
out.shareSuccess = 'Endereço copiado para o clipboard';
out.newButton = 'Novo';
out.newButtonTitle = 'Criar um novo bloco';
out.saveTemplateButton = "Salvar como modelo";
out.saveTemplatePrompt = "Escolha o nome do modelo";
out.templateSaved = "Modelo salvo!";
out.selectTemplate = "Selecione um modelo ou pressione ESC";
out.previewButtonTitle = "Mostrar ou esconder o modo de visualização markdown";
out.presentButtonTitle = "Entrar no modo apresentação";
out.presentSuccess = 'Pressione ESC para sair do modo de apresentação';
out.backgroundButtonTitle = 'Mudar cor do fundo da apresentação';
out.colorButtonTitle = 'Mudar a cor do texto no modo apresentação';
out.printButton = "Imprimir (Enter)";
out.printButtonTitle = "Imprimir seus slides ou exportá-los como PDF";
out.printOptions = "Opções de leiaute";
out.printSlideNumber = "Mostrar o número do slide";
out.printDate = "Mostrar a data";
out.printTitle = "Mostrar título do bloco";
out.printCSS = "Custom style rules (CSS):";
out.printTransition = "Ativar animações de transição";
out.slideOptionsTitle = "Personalizar seus slides";
out.slideOptionsButton = "Salvar (Enter)";
out.editShare = "Compartilhar endereço editável";
out.editShareTitle = "Copiar endereço editável";
out.viewShare = "Compartilhar endereó de visualização";
out.editOpen = "Abrir endereço editável em nova aba";
out.editOpenTitle = "Abrir este bloco em modo editável em nova aba";
out.viewShare = "Compartilhar endereço de visualização";
out.viewShareTitle = "Copiar o endereço somente leitura";
out.viewOpen = "Ver em nova aba";
out.viewOpenTitle = "Abrir o documento em modo somente leitura em nova aba";
out.notifyJoined = "{0} entraram na sessão colaborativa";
out.notifyRenamed = "{0} agora é conhecido como {1}";
out.notifyLeft = "{0} deixou essa sessão colaborativa";
out.tryIt = 'Experimente!';
out.okButton = 'OK (enter)';
out.cancelButton = 'Cancelar (esc)';
out.okButton = 'OK (Enter)';
out.cancel = "Cancelar";
out.cancelButton = 'Cancelar (ESC)';
out.historyButton = "Exibir histórico do documento";
out.history_next = "Ir para próxima versão";
out.history_prev = "Ir para versão anterior";
out.history_goTo = "Ir para versão selecionada";
out.history_close = "Voltar";
out.history_closeTitle = "Fechar o histórico";
out.history_restore = "Restaurar";
out.history_restoreTitle = "Restaurar a versão selecionada do documento";
out.history_restorePrompt = "Você tem certeza que deseja substituir a versão atual do documento pela que está sendo exibida agora?";
out.history_restoreDone = "Documento restaurado";
out.history_version = "Versão:";
out.tryIt = 'Experimente!';
// Polls
@ -92,6 +188,12 @@ define(function () {
out.wizardTitle = "Use o assistente para criar sua enquete";
out.wizardConfirm = "Você está realmente pronto para adicionar estas opções em sua enquete?";
out.poll_publish_button = "Publicar";
out.poll_admin_button = "Admin";
out.poll_create_user = "Adicionar novo usuário";
out.poll_create_option = "Adicionar nova opção";
out.poll_commit = "Submeter";
out.poll_closeWizardButton = "Fechar assistente";
out.poll_closeWizardButtonTitle = "Fechar assistente";
out.poll_wizardComputeButton = "Computar opções";
@ -102,23 +204,241 @@ define(function () {
out.poll_optionPlaceholder = "Alternativa";
out.poll_userPlaceholder = "Seu nome";
out.poll_removeOption = "Você tem certeza que deseja remover esta opção?";
out.poll_removeUser = "Você tem certeza que quer remover este usuário?";
out.poll_titleHint = "Título";
out.poll_descriptionHint = "Descrição";
// index.html
out.main_p2 = 'Este projeto utiliza os Editores visuais <a href="http://ckeditor.com/">CKEditor</a> e <a href="https://codemirror.net/">CodeMirror</a>, e a engine de tempo real <a href="https://github.com/xwiki-contrib/chainpad">ChainPad</a>.';
out.main_howitworks = 'Como funciona';
   out.main_howitworks_p1 = 'CryptPad usa uma variante do algorítmo de <a href="https://en.wikipedia.org/wiki/Operational_transformation">Transformação Operacional</a>, que é capaz de encontrar consenso distribuido usando o <a href="https://bitcoin.org/bitcoin.pdf">Blockchain de Nakamoto</a>, um constructo popularizado pela <a href="https://en.wikipedia.org/wiki/Bitcoin">Criptomoeda Bitcoin</a>. Desta forma o algorítmo pode evitar a necessidade de um servidor central para resolver conflitos de edição operacional sem a necessidade do servidor armazenar o conteúdo que está sendo editado pelos colaboradores.';
// Canvas
out.canvas_clear = "Limpar";
out.canvas_delete = "Deletar seleção";
out.canvas_disable = "Desabilitar desenho";
out.canvas_enable = "Habilitar desenho";
out.canvas_width = "Largura";
out.canvas_opacity = "Opacidade";
// File manager
out.fm_rootName = "Documentos";
out.fm_trashName = "Lixeira";
out.fm_unsortedName = "Arquivos não organizados";
out.fm_filesDataName = "Todos os Arquivos";
out.fm_templateName = "Temas";
out.fm_searchName = "Busca";
out.fm_searchPlaceholder = "Buscar...";
out.fm_newButton = "Novo";
out.fm_newButtonTitle = "Criar um novo bloco ou diretório";
out.fm_newFolder = "Novo diretório";
out.fm_newFile = "Novo bloco";
out.fm_folder = "Diretório";
out.fm_folderName = "Nome do diretório";
out.fm_numberOfFolders = "# de diretórios";
out.fm_numberOfFiles = "# de arquivos";
out.fm_fileName = "Nome do arquivo";
out.fm_title = "Título";
out.fm_type = "Tipo";
out.fm_lastAccess = "Último acesso";
out.fm_creation = "Criação";
out.fm_forbidden = "Ação não permitida";
out.fm_originalPath = "Caminho original";
out.fm_openParent = "Exibir no diretório";
out.fm_noname = "Documento sem título";
out.fm_emptyTrashDialog = "Você tem certeza que deseja limpar a lixeira??";
out.fm_removeSeveralPermanentlyDialog = "Você tem certeza que deseja deletar estes {0} elementos da lixeira permanentemente?";
out.fm_removePermanentlyDialog = "Você tem certeza que deseja deletar este elemento da lixeira permanentemente?";
out.fm_removeSeveralDialog = "Você tem certeza que deseja mover estes {0} elementos para a lixeira?";
out.fm_removeDialog = "Você tem certeza que deseja mover {0} para a lixeira?";
out.fm_restoreDialog = "Você tem certeza que deseja restaurar {0} de volta para seu diretório original?";
out.fm_unknownFolderError = "O diretório selecionado ou visitado por último não existe mais. Abrindo diretório superior...";
out.fm_contextMenuError = "Incapaz de abrir o menu de contextualização para este elementos. Se o problema persistir, tente recarregar a página.";
out.fm_selectError = "Incapaz de selecionar o elemento marcado. Se o problema persistir, tente recarregar a página.";
out.fm_categoryError = "Incapaz de abrir a categoria selecionada, Exibindo diretório raiz";
out.fm_info_root = "Crie quantos diretórios aninhados aqui desejar para organizar seus arquivos..";
out.fm_info_unsorted = "Contém todos os arquivos que você visitou e não estão ainda organizados na pasta Documentos ou foram movidos para a pasta lixeira"; // "My Documents" should match with the "out.fm_rootName" key, and "Trash" with "out.fm_trashName" out.fm_info_template = 'Contains all the pads stored as templates and that you can re-use when you create a new pad.';
out.updated_0_fm_info_trash = 'Empty your trash to free space in your CryptDrive.';
out.fm_info_trash = out.updated_0_fm_info_trash;
out.fm_info_allFiles = 'Contém todos os arquivos de "Documentos", "Não organizados" e "Lixeira". Não é possível mover ou remover arquivos daqui.'; // Same here
out.fm_info_anonymous = 'Você não está logado, então estes blocos podem ser deletados! (<a href="https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/" target="_blank">Descubra o porque</a>). ' +
'<a href="/register/">Cadastre-se</a> or <a href="/login/">Entre</a> Para deixá-los salvos.';
out.fm_alert_backupUrl = "Link de backup desta conta.<br>" +
"É <strong>fortemente recomendado</strong> que você deixe para você e somente você.<br>" +
"Você pode usá-lo para resgatar os seus dados caso a memória do seu navegador se perca.<br>" +
"Qualquer um com este link pode editar ou apagar todos os arquivos no gerenciador da conta.<br>";
out.fm_alert_anonymous = "Ola! Você está utilizando o CryptPad anonimamente, isto é ok, mas seus blocos podem ser apagados " +
"se ficarem muito tempo inativo. Nós desativamos as funções avançadas nas contas anônimas para que isto fique claro para você " +
'Este não é um bom lugar apra salvar senhas! Entenda: <a href="https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/" target="_blank">Clicando aqui!</a> ' +
'Porque estamos fazendo isso e porque você deveria criar uma onta? <a href="/register/">Sign up</a> and <a href="/login/">Clique e entenda!</a>.';
out.fm_backup_title = 'Link de restauração';
out.fm_nameFile = 'Como deseja nomear este arquivo?';
out.fm_error_cantPin = "Erro interno do servidor. Por favor recarregue a página e tente novamente.";
// File - Context menu
out.fc_newfolder = "Nova pasta";
out.fc_rename = "Renomear";
out.fc_open = "Abrir";
out.fc_open_ro = "Abrir (somente leitura)";
out.fc_delete = "Deletar";
out.fc_restore = "Restaurar";
out.fc_remove = "Deletar permanentemente";
out.fc_empty = "Esvaziar lixeira";
out.fc_prop = "Propriedades";
out.fc_sizeInKilobytes = "tamanho em Kilobytes";
// fileObject.js (logs)
out.fo_moveUnsortedError = "Você não pode mover uma pasta na lista de notas não organizadas";
out.fo_existingNameError = "Nome já em uso neste diretório. Por favor escolha outro.";
out.fo_moveFolderToChildError = "Você não pode mover uma sub-diretório para dentro de um de seus sub-diretórios";
out.fo_unableToRestore = "Fomos incapazes de restaurar este arquivo para sua posição original. Você pode tentar move-lo para o local de destino porém.";
out.fo_unavailableName = "Um arquivo ou diretório com o mesmo nome já existe no novo locao. Renomeie-o e tente novamente.";
// login
out.login_login = "Entrar";
out.login_makeAPad = 'Criar bloco anonimamente';
out.login_nologin = "Navegar nos blocos locais";
out.login_register = "Cadastro";
out.logoutButton = "Sair";
out.settingsButton = "Configurações";
out.login_username = "Usuário";
out.login_password = "Senha";
out.login_confirm = "Confirme sua senha";
out.login_remember = "Memorize-me";
out.login_hashing = "Encriptando sua senha, isto pode tomar algum tempo.";
out.login_hello = 'Ola {0},'; // {0} is the username
out.login_helloNoName = 'Ola,';
out.login_accessDrive = 'Acesse seu diretório';
out.login_orNoLogin = 'ou';
out.login_noSuchUser = 'Usuário ou senha inválido. Tente nocamente ou cadastre-se';
out.login_invalUser = 'É necessário um usuário';
out.login_invalPass = 'É necessário uma senha';
out.login_unhandledError = 'Um erro não esperado ocorreu :(';
out.register_importRecent = "Importar histórico de blocos (Recomendado)";
out.register_acceptTerms = "Eu aceito <a href='/terms.html'>os termos de serviço</a>";
out.register_passwordsDontMatch = "Senhas não coincidem!";
out.register_mustAcceptTerms = "Você precisa aceitar os termos de serviço.";
out.register_mustRememberPass = "Nós não podemos restaurar sua senha caso você a esqueça. É muito importante que você lembre-se dela! Clique nesta caixa de seleção para confirmar que você compreendeu isto.";
out.register_header = "Bem vindo ao CryptPad";
out.register_explanation = [
"<p>Lets go over a couple things first</p>",
"<ul>",
"<li>Your password is your secret key which encrypts all of your pads. If you lose it there is no way we can recover your data.</li>",
"<li>You can import pads which were recently viewed in your browser so you have them in your account.</li>",
"<li>If you are using a shared computer, you need to log out when you are done, closing the tab is not enough.</li>",
"</ul>"
].join('');
out.register_writtenPassword = "I have written down my username and password, proceed";
out.register_cancel = "Go back";
out.register_warning = "Zero Knowledge means that we can't recover your data if you lose your password.";
out.register_alreadyRegistered = "This user already exists, do you want to log in?";
// Settings
out.settings_title = "Settings";
out.settings_save = "Save";
out.settings_backupTitle = "Backup or restore all your data";
out.settings_backup = "Backup";
out.settings_restore = "Restore";
out.settings_resetTitle = "Clean your drive";
out.settings_reset = "Remove all the files and folders from your CryptDrive";
out.settings_resetPrompt = "This action will remove all the pads from your drive.<br>"+
"Are you sure you want to continue?<br>" +
"Type “<em>I love CryptPad</em>” to confirm.";
out.settings_resetDone = "Your drive is now empty!";
out.settings_resetError = "Incorrect verification text. Your CryptDrive has not been changed.";
out.settings_resetTips = "Tips in CryptDrive";
out.settings_resetTipsButton = "Reset the available tips in CryptDrive";
out.settings_resetTipsDone = "All the tips are now visible again.";
out.settings_importTitle = "Import this browser's recent pads in my CryptDrive";
out.settings_import = "Import";
out.settings_importConfirm = "Are you sure you want to import recent pads from this browser to your user account's CryptDrive?";
out.settings_importDone = "Import completed";
out.settings_userFeedbackHint1 = "CryptPad provides some very basic feedback to the server, to let us know how to improve your experience.";
out.settings_userFeedbackHint2 = "Your pad's content will never be shared with the server.";
out.settings_userFeedback = "Enable user feedback";
out.settings_anonymous = "You are not logged in. Settings here are specific to this browser.";
out.settings_publicSigningKey = "Public Signing Key";
out.settings_usage = "Usage";
out.settings_usageTitle = "See the total size of your pinned pads in MB";
out.settings_pinningNotAvailable = "Pinned pads are only available to registered users.";
out.settings_pinningError = "Something went wrong";
out.settings_usageAmount = "Your pinned pads occupy {0}MB";
out.settings_logoutEverywhereTitle = "Log out everywhere";
out.settings_logoutEverywhere = "Log out of all other web sessions";
out.settings_logoutEverywhereConfirm = "Are you sure? You will need to log in with all your devices.";
out.upload_serverError = "Server Error: unable to upload your file at this time.";
out.upload_uploadPending = "You already have an upload in progress. Cancel it and upload your new file?";
out.upload_success = "Your file ({0}) has been successfully uploaded and added to your drive.";
out.upload_notEnoughSpace = "There is not enough space for this file in your CryptDrive.";
out.upload_tooLarge = "This file exceeds the maximum upload size.";
out.upload_choose = "Choose a file";
out.upload_pending = "Pending";
out.upload_cancelled = "Cancelled";
out.upload_name = "File name";
out.upload_size = "Size";
out.upload_progress = "Progress";
out.download_button = "Decrypt & Download";
// general warnings
out.warn_notPinned = "This pad is not in anyone's CryptDrive. It will expire after 3 months. <a href='/about.html#pinning'>Learn more...</a>";
   out.main_about_p2 = 'Se você tem alguma questão ou comentário, você pode <a href="https://twitter.com/cryptpad">nos mandar um tweet</a> ou abrir uma requisição <a href="https://github.com/xwiki-labs/cryptpad/issues/" title="no nosso tracker">no github</a>. Venha também nos dar um Oi no IRC (<a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" title="freenode webchat">irc.freenode.net</a>), ou até mesmo via <a href="mailto:research@xwiki.com">e-mail</a>.';
out.button_newpad = 'NOVO BLOCO WYSIWYG';
out.button_newcode = 'NOVO BLOCO DE NOTAS';
out.button_newpoll = 'NOVA ENQUETE';
out.button_newslide = 'NOVA APRESENTAÇÃO';
// index.html
//about.html
out.main_p2 = 'This project uses the <a href="http://ckeditor.com/">CKEditor</a> Visual Editor, <a href="https://codemirror.net/">CodeMirror</a>, and the <a href="https://github.com/xwiki-contrib/chainpad">ChainPad</a> realtime engine.';
out.main_howitworks_p1 = 'CryptPad uses a variant of the <a href="https://en.wikipedia.org/wiki/Operational_transformation">Operational transformation</a> algorithm which is able to find distributed consensus using a <a href="https://bitcoin.org/bitcoin.pdf">Nakamoto Blockchain</a>, a construct popularized by <a href="https://en.wikipedia.org/wiki/Bitcoin">Bitcoin</a>. This way the algorithm can avoid the need for a central server to resolve Operational Transform Edit Conflicts and without the need for resolving conflicts, the server can be kept unaware of the content which is being edited on the pad.';
// contact.html
out.main_about_p2 = 'If you have any questions or comments, you can <a href="https://twitter.com/cryptpad">tweet us</a>, open an issue <a href="https://github.com/xwiki-labs/cryptpad/issues/" title="our issue tracker">on github</a>, come say hi on irc (<a href="http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7" title="freenode webchat">irc.freenode.net</a>), or <a href="mailto:research@xwiki.com">send us an email</a>.';
out.main_info = "<h1>Collaborate in Confidence</h1><br> Grow your ideas together with shared documents while <strong>Zero Knowledge</strong> technology secures your privacy; even from us.";
out.main_howitworks = 'How It Works';
out.main_zeroKnowledge = 'Zero Knowledge';
out.main_zeroKnowledge_p = "You don't have to trust that we <em>won't</em> look at your pads, with CryptPad's revolutionary Zero Knowledge Technology we <em>can't</em>. Learn more about how we protect your <a href=\"/privacy.html\" title='Privacy'>Privacy and Security</a>.";
out.main_writeItDown = 'Write it down';
out.main_writeItDown_p = "The greatest projects come from the smallest ideas. Take down the moments of inspiration and unexpected ideas because you never know which one might be a breakthrough.";
out.main_share = 'Share the link, share the pad';
out.main_share_p = "Grow your ideas together: conduct efficient meetings, collaborate on TODO lists and make quick presentations with all your friends and all your devices.";
out.main_organize = 'Get organized';
out.main_organize_p = "With CryptPad Drive, you can keep your sights on what's important. Folders allow you to keep track of your projects and have a global vision of where things are going.";
out.tryIt = 'Try it out!';
out.main_richText = 'Rich Text editor';
out.main_richText_p = 'Edit rich text pads collaboratively with our realtime Zero Knowledge <a href="http://ckeditor.com" target="_blank">CkEditor</a> application.';
out.main_code = 'Code editor';
out.main_code_p = 'Edit code from your software collaboratively with our realtime Zero Knowledge <a href="https://www.codemirror.net" target="_blank">CodeMirror</a> application.';
out.main_slide = 'Slide editor';
out.main_slide_p = 'Create your presentations using the Markdown syntax, and display them in your browser.';
out.main_poll = 'Polls';
out.main_poll_p = 'Plan your meeting or your event, or vote for the best solution regarding your problem.';
out.main_drive = 'CryptDrive';
out.footer_applications = "Applications";
out.footer_contact = "Contact";
out.footer_aboutUs = "About us";
out.about = "About";
out.privacy = "Privacy";
out.contact = "Contact";
out.terms = "ToS";
out.blog = "Blog";
// privacy.html
@ -152,17 +472,89 @@ define(function () {
// BottomBar.html
out.bottom_france = '<a href="http://www.xwiki.com/" target="_blank" rel="noopener noreferrer">Feito com <img class="bottom-bar-heart" src="/customize/heart.png" /> na <img class="bottom-bar-fr" src="/customize/fr.png" /></a>';
   out.bottom_support = '<a href="http://labs.xwiki.com/" title="XWiki Labs" target="_blank" rel="noopener noreferrer">Um projeto do laboratório <img src="/customize/logo-xwiki2.png" alt="XWiki SAS" class="bottom-bar-xwiki"/></a> com o suporte da <a href="http://ng.open-paas.org/" title="OpenPaaS::ng" target="_blank" rel="noopener noreferrer"> <img src="/customize/openpaasng.png" alt="OpenPaaS-ng" class="bottom-bar-openpaas" /></a>';
out.bottom_support = '<a href="http://labs.xwiki.com/" title="XWiki Labs" target="_blank" rel="noopener noreferrer">Um projeto do laboratório <img src="/customize/logo-xwiki2.png" alt="XWiki SAS" class="bottom-bar-xwiki"/></a> com o suporte da <a href="http://ng.open-paas.org/" title="OpenPaaS::ng" target="_blank" rel="noopener noreferrer"> <img src="/customize/openpaasng.png" alt="OpenPaaS-ng" class="bottom-bar-openpaas" /></a>';
// Header.html
out.header_france = '<a href="http://www.xwiki.com/" target="_blank" rel="noopener noreferrer">Com <img class="bottom-bar-heart" src="/customize/heart.png" /> da <img class="bottom-bar-fr" src="/customize/fr.png" title="France" alt="France"/> por <img src="/customize/logo-xwiki.png" alt="XWiki SAS" class="bottom-bar-xwiki"/></a>';
// TODO Hardcode cause YOLO
//out.header_xwiki = '<a href="http://www.xwiki.com/" target="_blank" rel="noopener noreferrer"><img src="/customize/logo-xwiki.png" alt="XWiki SAS" class="bottom-bar-xwiki"/></a>';
out.header_support = '<a href="http://ng.open-paas.org/" title="OpenPaaS::ng" target="_blank" rel="noopener noreferrer"> <img src="/customize/openpaasng.png" alt="OpenPaaS-ng" class="bottom-bar-openpaas" /></a>';
out.header_logoTitle = 'Ir para página principal';
out.header_logoTitle = 'Go to the main page';
// Initial states
out.initialState = [
'<span style="font-size:16px;"><p>',
'This is&nbsp;<strong>CryptPad</strong>, the Zero Knowledge realtime collaborative editor. Everything is saved as you type.',
'<br>',
'Share the link to this pad to edit with friends or use the <span style="background-color:#5cb85c;color:#ffffff;">&nbsp;Share&nbsp;</span> button to share a <em>read-only link</em>&nbsp;which allows viewing but not editing.',
'</p>',
'<p><span style="color:#808080;"><em>',
'Go ahead, just start typing...',
'</em></span></p></span>',
'<p>&nbsp;<br></p>'
].join('');
out.codeInitialState = [
'# CryptPad\'s Zero Knowledge collaborative code editor\n',
'\n',
'* What you type here is encrypted so only people who have the link can access it.\n',
'* You can choose the programming language to highlight and the UI color scheme in the upper right.'
].join('');
out.slideInitialState = [
'# CryptSlide\n',
'* This is a zero knowledge realtime collaborative editor.\n',
'* What you type here is encrypted so only people who have the link can access it.\n',
'* Even the server cannot see what you type.\n',
'* What you see here, what you hear here, when you leave here, let it stay here.\n',
'\n',
'---',
'\n',
'# How to use\n',
'1. Write your slides content using markdown syntax\n',
' - Learn more about markdown syntax [here](http://www.markdowntutorial.com/)\n',
'2. Separate your slides with ---\n',
'3. Click on the "Play" button to see the result',
' - Your slides are updated in realtime'
].join('');
// Readme
out.driveReadmeTitle = "What is CryptDrive?";
out.readme_welcome = "Welcome to CryptPad !";
out.readme_p1 = "Welcome to CryptPad, this is where you can take note of things alone and with friends.";
out.readme_p2 = "This pad will give you a quick walk through of how you can use CryptPad to take notes, keep them organized and work together on them.";
out.readme_cat1 = "Get to know your CryptDrive";
out.readme_cat1_l1 = "Make a pad: In your CryptDrive, click {0} then {1} and you can make a pad."; // 0: New, 1: Rich Text
out.readme_cat1_l2 = "Open Pads from your CryptDrive: double-click on a pad icon to open it.";
out.readme_cat1_l3 = "Organize your pads: When you are logged in, every pad you access will be shown as in the {0} section of your drive."; // 0: Unsorted files
out.readme_cat1_l3_l1 = "You can click and drag files into folders in the {0} section of your drive and make new folders."; // 0: Documents
out.readme_cat1_l3_l2 = "Remember to try right clicking on icons because there are often additional menus.";
out.readme_cat1_l4 = "Put old pads in the trash: You can click and drag your pads into the {0} the same way you drag them into folders."; // 0: Trash
out.readme_cat2 = "Make pads like a pro";
out.edit = "edit";
out.view = "view";
out.readme_cat2_l1 = "The {0} button in your pad allows you to give access to collaborators to either {1} or to {2} the pad."; // 0: Share, 1: edit, 2: view
out.readme_cat2_l2 = "Change the title of the pad by clicking on the pencil";
out.readme_cat3 = "Discover CryptPad apps";
out.readme_cat3_l1 = "With CryptPad code editor, you can collaborate on code like Javascript and markdown like HTML and Markdown";
out.readme_cat3_l2 = "With CryptPad slide editor, you can make quick presentations using Markdown";
out.readme_cat3_l3 = "With CryptPoll you can take quick votes, especially for scheduling meetings which fit with everybody's calendar";
// Tips
out.tips = {};
out.tips.lag = "The green icon in the upper right shows the quality of your internet connection to the CryptPad server.";
out.tips.shortcuts = "`ctrl+b`, `ctrl+i` and `ctrl+u` are quick shortcuts for bold, italic and underline.";
out.tips.indent = "In numbered and bulleted lists, you can use tab or shift+tab to quickly increase or decrease indentation.";
out.tips.title = "You can set the title of your pad by clicking the top center.";
out.tips.store = "Every time you visit a pad, if you're logged in it will be saved to your CryptDrive.";
out.tips.marker = "You can highlight text in a pad using the \"marker\" item in the styles dropdown menu.";
out.feedback_about = "If you're reading this, you were probably curious why CryptPad is requesting web pages when you perform certain actions";
out.feedback_privacy = "We care about your privacy, and at the same time we want CryptPad to be very easy to use. We use this file to figure out which UI features matter to our users, by requesting it along with a parameter specifying which action was taken.";
out.feedback_optout = "If you would like to opt out, visit <a href='/settings/'>your user settings page</a>, where you'll find a checkbox to enable or disable user feedback";
return out;
});

@ -331,7 +331,7 @@ define(function () {
out.header_france = "<a href=\"http://www.xwiki.com/\" target=\"_blank\" rel=\"noopener noreferrer\">With <img class=\"bottom-bar-heart\" src=\"/customize/heart.png\" alt=\"love\" /> from <img class=\"bottom-bar-fr\" src=\"/customize/fr.png\" title=\"Franța\" alt=\"Franța\"/> by <img src=\"/customize/logo-xwiki.png\" alt=\"XWiki SAS\" class=\"bottom-bar-xwiki\"/></a>";
out.header_support = "<a href=\"http://ng.open-paas.org/\" title=\"OpenPaaS::ng\" target=\"_blank\" rel=\"noopener noreferrer\"> <img src=\"/customize/openpaasng.png\" alt=\"OpenPaaS-ng\" class=\"bottom-bar-openpaas\" /></a>";
out.header_logoTitle = "Mergi la pagina principală";
out.initialState = "<span style=\"font-size:16px;\"><p>Acesta este&nbsp;<strong>CryptPad</strong>, editorul colaborativ bazat pe tehnologia Zero Knowledge în timp real. Totul este salvat pe măsură ce scrii.<br>Partajează link-ul către acest pad pentru a edita cu prieteni sau folosește <span style=\"background-color:#5cb85c;color:#ffffff;\">&nbsp;Share&nbsp;</span> butonul pentru a partaja <em>read-only link</em>&nbsp;permițând vizualizarea dar nu și editarea.</p><p><span style=\"color:#808080;\"><em>Îndrăznește, începe să scrii...</em></span></p></span><p>&nbsp;<br></p>";
out.initialState = "<span style=\"font-size:16px;\"><p>Acesta este&nbsp;<strong>CryptPad</strong>, editorul colaborativ bazat pe tehnologia Zero Knowledge în timp real. Totul este salvat pe măsură ce scrii.<br>Partajează link-ul către acest pad pentru a edita cu prieteni sau folosește <span class=\"fa fa-share-alt\" style=\"border:1px solid black;color:#000;\">&nbsp;Share&nbsp;</span> butonul pentru a partaja <em>read-only link</em>&nbsp;permițând vizualizarea dar nu și editarea.</p><p><em>Îndrăznește, începe să scrii...</em></p></span><p>&nbsp;<br></p>";
out.codeInitialState = "/*\n Acesta este editorul colaborativ de cod bazat pe tehnologia Zero Knowledge CryptPad.\n Ce scrii aici este criptat, așa că doar oamenii care au link-ul pot să-l acceseze.\n Poți să alegi ce limbaj de programare pus n evidență și schema de culori UI n dreapta sus.\n*/";
out.slideInitialState = "# CryptSlide\n* Acesta este un editor colaborativ bazat pe tehnologia Zero Knowledge.\n* Ce scrii aici este criptat, așa că doar oamenii care au link-ul pot să-l acceseze.\n* Nici măcar serverele nu au acces la ce scrii tu.\n* Ce vezi aici, ce auzi aici, atunci când pleci, lași aici.\n\n-\n# Cum se folosește\n1. Scrie-ți conținutul slide-urilor folosind sintaxa markdown\n - Află mai multe despre sintaxa markdown [aici](http://www.markdowntutorial.com/)\n2. Separă-ți slide-urile cu -\n3. Click pe butonul \"Play\" pentru a vedea rezultatele - Slide-urile tale sunt actualizate în timp real.";
out.driveReadmeTitle = "Ce este CryptDrive?";

@ -470,12 +470,12 @@ define(function () {
'<span style="font-size:16px;"><p>',
'這是&nbsp;<strong>CryptPad</strong>, 零知識即時協作編輯平台,當你輸入時一切已即存好。',
'<br>',
'分享這個工作檔案的網址連結給友人或是使用、 <span style="background-color:#5cb85c;color:#ffffff;">&nbsp;分享&nbsp;</span> 按鈕分享<em>唯讀的連結</em>&nbsp;其只能看不能編寫。',
'分享這個工作檔案的網址連結給友人或是使用、 <span class="fa fa-share-alt" style="border: 1px solid black;color:#000;">&nbsp;分享&nbsp;</span> 按鈕分享<em>唯讀的連結</em>&nbsp;其只能看不能編寫。',
'</p>',
'<p><span style="color:#808080;"><em>',
'<p><em>',
'來吧, 開始打字輸入吧...',
'</em></span></p></span>',
'</em></p></span>',
'<p>&nbsp;<br></p>'
].join('');

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server",
"version": "1.9.0",
"version": "1.11.0",
"dependencies": {
"chainpad-server": "^1.0.1",
"express": "~4.10.1",
@ -18,7 +18,6 @@
"scripts": {
"lint": "jshint --config .jshintrc --exclude-path .jshintignore .",
"test": "node TestSelenium.js",
"style": "lessc ./customize.dist/src/less/cryptpad.less > ./customize.dist/main.css && lessc ./customize.dist/src/less/toolbar.less > ./customize.dist/toolbar.css && lessc ./www/drive/file.less > ./www/drive/file.css && lessc ./www/settings/main.less > ./www/settings/main.css && lessc ./www/slide/slide.less > ./www/slide/slide.css && lessc ./www/whiteboard/whiteboard.less > ./www/whiteboard/whiteboard.css && lessc ./www/poll/poll.less > ./www/poll/poll.css && lessc ./www/file/file.less > ./www/file/file.css && lessc ./www/code/code.less > ./www/code/code.css",
"template": "cd customize.dist/src && node build.js"
"template": "cd customize.dist/src && for page in ../index.html ../privacy.html ../terms.html ../about.html ../contact.html ../../www/login/index.html ../../www/register/index.html ../../www/settings/index.html ../../www/user/index.html;do echo $page; cp template.html $page; done;"
}
}

@ -30,7 +30,7 @@ const sizeForHashes = (hashes, dsFileStats) => {
let sum = 0;
hashes.forEach((h) => {
const s = dsFileStats[h];
if (typeof(s) !== 'number') {
if (typeof(s) !== 'object' || typeof(s.size) !== 'number') {
//console.log('missing ' + h + ' ' + typeof(s));
} else {
sum += s.size;
@ -62,11 +62,26 @@ nThen((waitFor) => {
});
});
}).nThen((waitFor) => {
Fs.readdir('./blob', waitFor((err, list) => {
if (err) { throw err; }
dirList = list;
}));
}).nThen((waitFor) => {
dirList.forEach((f) => {
sema.take((returnAfter) => {
Fs.readdir('./blob/' + f, waitFor(returnAfter((err, list2) => {
if (err) { throw err; }
list2.forEach((ff) => { fileList.push('./blob/' + f + '/' + ff); });
})));
});
});
}).nThen((waitFor) => {
fileList.forEach((f) => {
sema.take((returnAfter) => {
Fs.stat(f, waitFor(returnAfter((err, st) => {
if (err) { throw err; }
dsFileStats[f.replace(/^.*\/([^\/]*)\.ndjson$/, (all, a) => (a))] = st;
dsFileStats[f.replace(/^.*\/([^\/\.]*)(\.ndjson)?$/, (all, a) => (a))] = st;
})));
});
});

@ -23,7 +23,7 @@ to others who want to participate.
Cryptpad depends on the Nodejs runtime.
We recommend installing it via [NVM](https://github.com/creationix/nvm "Node Version Manager") to ensure that you are running an up to date version.
Once you have a recent runtime:
Once you have a recent runtime (we use v6.6.0):
```
git clone <this repo>
@ -129,7 +129,7 @@ Still there are other low-lives in the world so using CryptPad over HTTPS is pro
## Setup using Docker
See [Cryptpad-Docker](cryptpad-docker.md)
See [Cryptpad-Docker](docs/cryptpad-docker.md)
## Translations

125
rpc.js

@ -28,8 +28,8 @@ var WARN = function (e, output) {
};
var isValidId = function (chan) {
return chan && chan.length && /^[a-fA-F0-9]/.test(chan) ||
[32, 48].indexOf(chan.length) !== -1;
return chan && chan.length && /^[a-fA-F0-9]/.test(chan) &&
[32, 48].indexOf(chan.length) > -1;
};
var uint8ArrayToHex = function (a) {
@ -113,15 +113,21 @@ var isTooOld = function (time, now) {
return (now - time) > 300000;
};
var expireSession = function (Sessions, key) {
var session = Sessions[key];
if (!session) { return; }
if (session.blobstage) {
session.blobstage.close();
}
delete Sessions[key];
};
var expireSessions = function (Sessions) {
var now = +new Date();
Object.keys(Sessions).forEach(function (key) {
var session = Sessions[key];
if (isTooOld(Sessions[key].atime, now)) {
if (session.blobstage) {
session.blobstage.close();
}
delete Sessions[key];
if (session && isTooOld(session.atime, now)) {
expireSession(Sessions, key);
}
});
};
@ -281,7 +287,7 @@ var getUploadSize = function (Env, channel, cb) {
var paths = Env.paths;
var path = makeFilePath(paths.blob, channel);
if (!path) {
return cb('INVALID_UPLOAD_ID');
return cb('INVALID_UPLOAD_ID', path);
}
Fs.stat(path, function (err, stats) {
@ -578,15 +584,15 @@ var resetUserPins = function (Env, publicKey, channelList, cb) {
});
}
var pins = session.channels = {};
var pins = {};
getMultipleFileSize(Env, channelList, function (e, sizes) {
if (e) { return void cb(e); }
var pinSize = sumChannelSizes(sizes);
getFreeSpace(Env, publicKey, function (e, free) {
getLimit(Env, publicKey, function (e, limit) {
if (e) {
WARN('getFreeSpace', e);
WARN('[RESET_ERR]', e);
return void cb(e);
}
@ -598,7 +604,7 @@ var resetUserPins = function (Env, publicKey, channelList, cb) {
They will not be able to pin additional pads until they upgrade
or delete enough files to go back under their limit. */
if (pinSize > free && session.hasPinned) { return void(cb('E_OVER_LIMIT')); }
if (pinSize > limit && session.hasPinned) { return void(cb('E_OVER_LIMIT')); }
pinStore.message(publicKey, JSON.stringify(['RESET', channelList]),
function (e) {
if (e) { return void cb(e); }
@ -606,6 +612,8 @@ var resetUserPins = function (Env, publicKey, channelList, cb) {
pins[channel] = true;
});
// update in-memory cache IFF the reset was allowed.
session.channels = pins;
getHash(Env, publicKey, function (e, hash) {
cb(e, hash);
});
@ -668,6 +676,29 @@ var makeFileStream = function (root, id, cb) {
});
};
var clearOwnedChannel = function (Env, channelId, unsafeKey, cb) {
if (typeof(channelId) !== 'string' || channelId.length !== 32) {
return cb('INVALID_ARGUMENTS');
}
if (!(Env.msgStore && Env.msgStore.getChannelMetadata)) {
return cb('E_NOT_IMPLEMENTED');
}
Env.msgStore.getChannelMetadata(channelId, function (e, metadata) {
if (e) { return cb(e); }
if (!(metadata && Array.isArray(metadata.owners))) { return void cb('E_NO_OWNERS'); }
// Confirm that the channel is owned by the user is question
if (metadata.owners.indexOf(unsafeKey) === -1) {
return void cb('INSUFFICIENT_PERMISSIONS');
}
return void Env.msgStore.clearChannel(channelId, function (e) {
cb(e);
});
});
};
var upload = function (Env, publicKey, content, cb) {
var paths = Env.paths;
var dec;
@ -825,6 +856,13 @@ var upload_status = function (Env, publicKey, filesize, cb) {
});
};
var isUnauthenticatedCall = function (call) {
return [
'GET_FILE_SIZE',
'GET_MULTIPLE_FILE_SIZE',
].indexOf(call) !== -1;
};
var isAuthenticatedCall = function (call) {
return [
'COOKIE',
@ -833,13 +871,11 @@ var isAuthenticatedCall = function (call) {
'UNPIN',
'GET_HASH',
'GET_TOTAL_SIZE',
'GET_FILE_SIZE',
'UPDATE_LIMITS',
'GET_LIMIT',
'GET_MULTIPLE_FILE_SIZE',
//'UPLOAD',
'UPLOAD_COMPLETE',
'UPLOAD_CANCEL',
'EXPIRE_SESSION',
].indexOf(call) !== -1;
};
@ -866,6 +902,34 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
var blobPath = paths.blob = keyOrDefaultString('blobPath', './blob');
var blobStagingPath = paths.staging = keyOrDefaultString('blobStagingPath', './blobstage');
var isUnauthenticateMessage = function (msg) {
return msg && msg.length === 2 && isUnauthenticatedCall(msg[0]);
};
var handleUnauthenticatedMessage = function (msg, respond) {
switch (msg[0]) {
case 'GET_FILE_SIZE':
return void getFileSize(Env, msg[1], function (e, size) {
if (e) {
console.error(e);
}
WARN(e, msg[1]);
respond(e, [null, size, null]);
});
case 'GET_MULTIPLE_FILE_SIZE':
return void getMultipleFileSize(Env, msg[1], function (e, dict) {
if (e) {
WARN(e, dict);
return respond(e);
}
respond(e, [null, dict, null]);
});
default:
console.error("unsupported!");
return respond('UNSUPPORTED_RPC_CALL', msg);
}
};
var rpc = function (
ctx /*:{ store: Object }*/,
data /*:Array<Array<any>>*/,
@ -887,11 +951,19 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
return void respond('INVALID_ARG_FORMAT');
}
if (isUnauthenticateMessage(msg)) {
return handleUnauthenticatedMessage(msg, respond);
}
var signature = msg.shift();
var publicKey = msg.shift();
// make sure a user object is initialized in the cookie jar
beginSession(Sessions, publicKey);
if (publicKey) {
beginSession(Sessions, publicKey);
} else {
console.log("No public key");
}
var cookie = msg[0];
if (!isValidCookie(Sessions, publicKey, cookie)) {
@ -927,7 +999,8 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
msg.shift();
var Respond = function (e, msg) {
var token = Sessions[safeKey].tokens.slice(-1)[0];
var session = Sessions[safeKey];
var token = session? session.tokens.slice(-1)[0]: '';
var cookie = makeCookie(token).join('|');
respond(e, [cookie].concat(typeof(msg) !== 'undefined' ?msg: []));
};
@ -943,6 +1016,7 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
if (!Env.msgStore) { Env.msgStore = ctx.store; }
var handleMessage = function (privileged) {
if (config.logRPC) { console.log(msg[0]); }
switch (msg[0]) {
case 'COOKIE': return void Respond(void 0);
case 'RESET':
@ -1002,7 +1076,16 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
}
Respond(void 0, dict);
});
case 'EXPIRE_SESSION':
return void setTimeout(function () {
expireSession(Sessions, safeKey);
Respond(void 0, "OK");
});
case 'CLEAR_OWNED_CHANNEL':
return void clearOwnedChannel(Env, msg[1], publicKey, function (e, response) {
if (e) { return void Respond(e); }
Respond(void 0, response);
});
// restricted to privileged users...
case 'UPLOAD':
if (!privileged) { return deny(); }
@ -1044,8 +1127,8 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
return void handleMessage(false);
}
// restrict upload capability unless explicitly disabled
if (config.restrictUploads === false) {
// allow unrestricted uploads unless restrictUploads is true
if (config.restrictUploads !== true) {
return void handleMessage(true);
}

@ -6,6 +6,85 @@ var mkPath = function (env, channelId) {
return Path.join(env.root, channelId.slice(0, 2), channelId) + '.ndjson';
};
var getMetadataAtPath = function (Env, path, cb) {
var remainder = '';
var stream = Fs.createReadStream(path, 'utf8');
var complete = function (err, data) {
var _cb = cb;
cb = undefined;
if (_cb) { _cb(err, data); }
};
stream.on('data', function (chunk) {
if (!/\n/.test(chunk)) {
remainder += chunk;
return;
}
stream.close();
var metadata = chunk.split('\n')[0];
var parsed = null;
try {
parsed = JSON.parse(metadata);
complete(void 0, parsed);
}
catch (e) {
console.log();
console.error(e);
complete('INVALID_METADATA');
}
});
stream.on('end', function () {
complete(null);
});
stream.on('error', function (e) { complete(e); });
};
var getChannelMetadata = function (Env, channelId, cb) {
var path = mkPath(Env, channelId);
getMetadataAtPath(Env, path, cb);
};
var closeChannel = function (env, channelName, cb) {
if (!env.channels[channelName]) { return; }
try {
env.channels[channelName].writeStream.close();
delete env.channels[channelName];
env.openFiles--;
cb();
} catch (err) {
cb(err);
}
};
var clearChannel = function (env, channelId, cb) {
var path = mkPath(env, channelId);
getMetadataAtPath(env, path, function (e, metadata) {
if (e) { return cb(e); }
if (!metadata) {
return void Fs.truncate(path, 0, function (err) {
if (err) {
return cb(err);
}
cb(void 0);
});
}
var len = JSON.stringify(metadata).length + 1;
// as long as closeChannel is synchronous, this should not cause
// any race conditions. truncate ought to return faster than a channel
// can be opened and read by another user. if that turns out not to be
// the case, we'll need to implement locking.
closeChannel(env, channelId, function (err) {
if (err) { cb(err); }
Fs.truncate(path, len, function (err) {
if (err) { return cb(err); }
cb();
});
});
});
};
var readMessages = function (path, msgHandler, cb) {
var remainder = '';
var stream = Fs.createReadStream(path, 'utf8');
@ -49,22 +128,10 @@ var checkPath = function (path, callback) {
};
var removeChannel = function (env, channelName, cb) {
var filename = Path.join(env.root, channelName.slice(0, 2), channelName + '.ndjson');
var filename = mkPath(env, channelName);
Fs.unlink(filename, cb);
};
var closeChannel = function (env, channelName, cb) {
if (!env.channels[channelName]) { return; }
try {
env.channels[channelName].writeStream.close();
delete env.channels[channelName];
env.openFiles--;
cb();
} catch (err) {
cb(err);
}
};
var flushUnusedChannels = function (env, cb, frame) {
var currentTime = +new Date();
@ -260,6 +327,12 @@ module.exports.create = function (conf, cb) {
getChannelSize: function (chanName, cb) {
channelBytes(env, chanName, cb);
},
getChannelMetadata: function (channelName, cb) {
getChannelMetadata(env, channelName, cb);
},
clearChannel: function (channelName, cb) {
clearChannel(env, channelName, cb);
},
});
});
setInterval(function () {

@ -1,79 +0,0 @@
html,
body {
height: 100%;
width: 100%;
padding: 0px;
margin: 0px;
overflow: hidden;
box-sizing: border-box;
position: relative;
}
body {
display: flex;
flex-flow: column;
max-height: 100%;
min-height: auto;
}
.CodeMirror {
display: inline-block;
height: 100%;
width: 50%;
min-width: 20%;
max-width: 80%;
resize: horizontal;
}
.CodeMirror.transition {
transition: width 500ms, min-width 500ms, max-width 500ms;
}
.CodeMirror.fullPage {
min-width: 100%;
max-width: 100%;
resize: none;
}
.CodeMirror-focused .cm-matchhighlight {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
background-position: bottom;
background-repeat: repeat-x;
}
#editorContainer {
flex: 1;
display: flex;
flex-flow: row;
height: 100%;
overflow: hidden;
}
#previewContainer {
flex: 1;
padding: 5px 20px;
overflow: auto;
display: inline-block;
height: 100%;
border-left: 1px solid black;
box-sizing: border-box;
font-family: Calibri, Ubuntu, sans-serif;
word-wrap: break-word;
}
#previewContainer media-tag * {
max-width: 100%;
}
#preview {
max-width: 40vw;
margin: auto;
}
#preview table {
border-collapse: collapse;
}
#preview table tr th {
border: 3px solid black;
padding: 15px;
}
@media (max-width: 600px) {
.CodeMirror {
flex: 1;
max-width: 100%;
resize: none;
}
#previewContainer {
display: none !important;
}
}

@ -1,5 +1,5 @@
@import "../../customize.dist/src/less/variables.less";
@import "../../customize.dist/src/less/mixins.less";
@import "/customize/src/less/variables.less";
@import "/customize/src/less/mixins.less";
html, body{
height: 100%;
@ -30,9 +30,10 @@ body {
resize: horizontal;
}
.CodeMirror.fullPage {
min-width: 100%;
//min-width: 100%;
max-width: 100%;
resize: none;
flex: 1;
}
.CodeMirror-focused .cm-matchhighlight {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);

@ -4,15 +4,8 @@
<title>CryptPad</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<meta name="referrer" content="no-referrer" />
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
<link rel="icon" type="image/png"
href="/customize/main-favicon.png"
data-main-favicon="/customize/main-favicon.png"
data-alt-favicon="/customize/alt-favicon.png"
id="favicon" />
<link rel="stylesheet" href="/customize/main.css" />
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script>
<style>
html, body {
overflow-y: hidden;
@ -46,14 +39,3 @@
<div id="iframe-container">
<iframe id="pad-iframe"></iframe><script src="/common/noscriptfix.js"></script>
</div>
<div id="loading">
<div class="loadingContainer">
<img class="cryptofist" src="/customize/cryptofist_small.png" />
<div class="spinnerContainer">
<span class="fa fa-spinner fa-pulse fa-4x fa-fw"></span>
</div>
<p data-localization="loading"></p>
</div>
</div>
</body>
</html>

@ -2,38 +2,11 @@
<html style="height: 100%;">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
<script src="/bower_components/codemirror/lib/codemirror.js"></script>
<link rel="stylesheet" href="/bower_components/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="/bower_components/codemirror/addon/dialog/dialog.css">
<link rel="stylesheet" href="/bower_components/codemirror/addon/fold/foldgutter.css" />
<link rel="stylesheet" href="/code/code.css" />
<script src="/bower_components/codemirror/mode/javascript/javascript.js"></script>
<script src="/bower_components/codemirror/addon/mode/loadmode.js"></script>
<script src="/bower_components/codemirror/mode/meta.js"></script>
<script src="/bower_components/codemirror/addon/mode/overlay.js"></script>
<script src="/bower_components/codemirror/addon/mode/multiplex.js"></script>
<script src="/bower_components/codemirror/addon/mode/simple.js"></script>
<script src="/bower_components/codemirror/addon/edit/closebrackets.js"></script>
<script src="/bower_components/codemirror/addon/edit/matchbrackets.js"></script>
<script src="/bower_components/codemirror/addon/edit/trailingspace.js"></script>
<script src="/bower_components/codemirror/addon/selection/active-line.js"></script>
<script src="/bower_components/codemirror/addon/search/search.js"></script>
<script src="/bower_components/codemirror/addon/search/match-highlighter.js"></script>
<script src="/bower_components/codemirror/addon/search/searchcursor.js"></script>
<script src="/bower_components/codemirror/addon/dialog/dialog.js"></script>
<script src="/bower_components/codemirror/addon/fold/foldcode.js"></script>
<script src="/bower_components/codemirror/addon/fold/foldgutter.js"></script>
<script src="/bower_components/codemirror/addon/fold/brace-fold.js"></script>
<script src="/bower_components/codemirror/addon/fold/xml-fold.js"></script>
<script src="/bower_components/codemirror/addon/fold/markdown-fold.js"></script>
<script src="/bower_components/codemirror/addon/fold/comment-fold.js"></script>
<script src="/bower_components/codemirror/addon/display/placeholder.js"></script>
<script async data-bootload="inner.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script>
<style> .loading-hidden { display: none; } </style>
</head>
<body>
<body class="loading-hidden">
<div id="cme_toolbox" class="toolbar-container"></div>
<div id="editorContainer">
<textarea id="editor1" name="editor1"></textarea>

@ -0,0 +1,38 @@
define([
'jquery',
'cm/lib/codemirror',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/code/code.less',
'less!/customize/src/less/toolbar.less',
'less!/customize/src/less/cryptpad.less',
'css!cm/lib/codemirror.css',
'css!cm/addon/dialog/dialog.css',
'css!cm/addon/fold/foldgutter.css',
'cm/mode/markdown/markdown',
'cm/addon/mode/loadmode',
'cm/mode/meta',
'cm/addon/mode/overlay',
'cm/addon/mode/multiplex',
'cm/addon/mode/simple',
'cm/addon/edit/closebrackets',
'cm/addon/edit/matchbrackets',
'cm/addon/edit/trailingspace',
'cm/addon/selection/active-line',
'cm/addon/search/search',
'cm/addon/search/match-highlighter',
'cm/addon/search/searchcursor',
'cm/addon/dialog/dialog',
'cm/addon/fold/foldcode',
'cm/addon/fold/foldgutter',
'cm/addon/fold/brace-fold',
'cm/addon/fold/xml-fold',
'cm/addon/fold/markdown-fold',
'cm/addon/fold/comment-fold',
'cm/addon/display/placeholder',
], function ($, CMeditor) {
window.CodeMirror = CMeditor;
$('.loading-hidden').removeClass('loading-hidden');
});

@ -9,7 +9,9 @@ define([
'/common/cryptpad-common.js',
'/common/cryptget.js',
'/common/diffMarked.js',
'/bower_components/tweetnacl/nacl-fast.min.js', // needed for media-tag
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less/cryptpad.less'
], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad,
Cryptget, DiffMd) {
var Messages = Cryptpad.Messages;
@ -41,6 +43,7 @@ define([
var andThen = function (CMeditor) {
var $iframe = $('#pad-iframe').contents();
var $contentContainer = $iframe.find('#editorContainer');
var $previewContainer = $iframe.find('#previewContainer');
var $preview = $iframe.find('#preview');
$preview.click(function (e) {
@ -54,7 +57,7 @@ define([
}
});
var CodeMirror = Cryptpad.createCodemirror(CMeditor, ifrw, Cryptpad);
var CodeMirror = Cryptpad.createCodemirror(ifrw, Cryptpad, null, CMeditor);
$iframe.find('.CodeMirror').addClass('fullPage');
editor = CodeMirror.editor;
@ -157,6 +160,7 @@ define([
if (e) { return void console.error(e); }
if (data !== false) {
$previewContainer.show();
APP.$previewButton.addClass('active');
$codeMirror.removeClass('fullPage');
}
});
@ -164,6 +168,7 @@ define([
}
APP.$previewButton.hide();
$previewContainer.hide();
APP.$previewButton.removeClass('active');
$codeMirror.addClass('fullPage');
};
@ -173,7 +178,7 @@ define([
var titleCfg = { getHeadingText: CodeMirror.getHeadingText };
Title = Cryptpad.createTitle(titleCfg, config.onLocal, Cryptpad);
Metadata = Cryptpad.createMetadata(UserList, Title);
Metadata = Cryptpad.createMetadata(UserList, Title, null, Cryptpad);
var configTb = {
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit', 'upgrade'],
@ -188,7 +193,8 @@ define([
ifrw: ifrw,
realtime: info.realtime,
network: info.network,
$container: $bar
$container: $bar,
$contentContainer: $contentContainer
};
toolbar = APP.toolbar = Toolbar.create(configTb);
@ -196,6 +202,7 @@ define([
CodeMirror.init(config.onLocal, Title, toolbar);
var $rightside = toolbar.$rightside;
var $drawer = toolbar.$drawer;
var editHash;
if (!readOnly) {
@ -215,7 +222,7 @@ define([
$toolbar: $bar
};
var $hist = Cryptpad.createButton('history', true, {histConfig: histConfig});
$rightside.append($hist);
$drawer.append($hist);
/* save as template */
if (!Cryptpad.isTemplate(window.location.href)) {
@ -230,12 +237,12 @@ define([
/* add an export button */
var $export = Cryptpad.createButton('export', true, {}, CodeMirror.exportText);
$rightside.append($export);
$drawer.append($export);
if (!readOnly) {
/* add an import button */
var $import = Cryptpad.createButton('import', true, {}, CodeMirror.importText);
$rightside.append($import);
$drawer.append($import);
}
/* add a forget button */
@ -266,8 +273,10 @@ define([
Cryptpad.setPadAttribute('previewMode', true, function (e) {
if (e) { return console.log(e); }
});
$previewButton.addClass('active');
} else {
$codeMirror.addClass('fullPage');
$previewButton.removeClass('active');
Cryptpad.setPadAttribute('previewMode', false, function (e) {
if (e) { return console.log(e); }
});
@ -307,7 +316,8 @@ define([
if(userDoc !== "") {
var hjson = JSON.parse(userDoc);
if (typeof (hjson) !== 'object' || Array.isArray(hjson)) {
if (typeof (hjson) !== 'object' || Array.isArray(hjson) ||
(typeof(hjson.type) !== 'undefined' && hjson.type !== 'code')) {
var errorText = Messages.typeError;
Cryptpad.errorLoadingScreen(errorText);
throw new Error(errorText);
@ -354,6 +364,21 @@ define([
return;
}
UserList.getLastName(toolbar.$userNameButton, isNew);
var fmConfig = {
dropArea: $iframe.find('.CodeMirror'),
body: $iframe.find('body'),
onUploaded: function (ev, data) {
//var cursor = editor.getCursor();
//var cleanName = data.name.replace(/[\[\]]/g, '');
//var text = '!['+cleanName+']('+data.url+')';
var parsed = Cryptpad.parsePadUrl(data.url);
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>';
editor.replaceSelection(mt);
}
};
APP.FM = Cryptpad.createFileManager(fmConfig);
};
config.onRemote = function () {
@ -418,7 +443,6 @@ define([
};
var interval = 100;
var second = function (CM) {
Cryptpad.ready(function () {
andThen(CM);
@ -433,11 +457,9 @@ define([
var first = function () {
if (ifrw.CodeMirror) {
// it exists, call your continuation
second(ifrw.CodeMirror);
} else {
console.log("CodeMirror was not defined. Trying again in %sms", interval);
// try again in 'interval' ms
setTimeout(first, interval);
}
};

@ -0,0 +1,64 @@
/*@flow*/
/*:: const define = () => {}; */
define([
'/api/config',
'/bower_components/less/dist/less.min.js'
], function (Config, Less) { /*::});module.exports = (function() {
const Config = (undefined:any);
const Less = (undefined:any);
*/
var module = { exports: {} };
var key = Config.requireConf.urlArgs;
var localStorage = window.localStorage || {};
var fixURL = function (url) {
var mark = (url.indexOf('?') !== -1) ? '&' : '?';
return url + mark + key;
};
var doXHR = Less.FileManager.prototype.doXHR;
Less.FileManager.prototype.doXHR = function (url, type, callback, errback) {
url = fixURL(url);
//console.log("xhr: " + url);
return doXHR(url, type, callback, errback);
};
var inject = function (cssText, url) {
var curStyle = document.createElement('style');
curStyle.setAttribute('data-original-src', url);
curStyle.type = 'text/css';
curStyle.appendChild(document.createTextNode(cssText));
if (!document.head) { throw new Error(); }
document.head.appendChild(curStyle);
};
var checkCache = function () {
if (localStorage['LESS_CACHE'] === key) { return; }
Object.keys(localStorage).forEach(function (k) {
if (k.indexOf('LESS_CACHE|') !== 0) { return; }
delete localStorage[k];
});
localStorage['LESS_CACHE'] = key;
};
module.exports.load = function (url /*:string*/, cb /*:()=>void*/) {
checkCache();
if (localStorage['LESS_CACHE|' + key + '|' + url]) {
inject(localStorage['LESS_CACHE|' + key + '|' + url], url);
cb();
return;
}
Less.render('@import (multiple) "' + url + '";', {}, function(err, css) {
if (err) {
console.log(err);
return;
}
localStorage['LESS_CACHE|' + key + '|' + url] = css.css;
inject(css.css, url);
cb();
}, window.less);
};
return module.exports;
})/*::()*/;

@ -0,0 +1,12 @@
define([
'/common/LessLoader.js'
], function (LessLoader) {
var api = {};
api.normalize = function(name, normalize) {
return normalize(name);
};
api.load = function(cssId, req, load /*, config */) {
LessLoader.load(cssId, load);
};
return api;
});

@ -8,8 +8,15 @@ define([], function () {
"jquery": "/bower_components/jquery/dist/jquery.min",
// json.sortify same
"json.sortify": "/bower_components/json.sortify/dist/JSON.sortify",
"pdfjs-dist/build/pdf": "/bower_components/pdfjs-dist/build/pdf",
"pdfjs-dist/build/pdf.worker": "/bower_components/pdfjs-dist/build/pdf.worker"
//"pdfjs-dist/build/pdf": "/bower_components/pdfjs-dist/build/pdf",
//"pdfjs-dist/build/pdf.worker": "/bower_components/pdfjs-dist/build/pdf.worker"
cm: '/bower_components/codemirror'
},
map: {
'*': {
'css': '/bower_components/require-css/css.js',
'less': '/common/RequireLess.js',
}
}
});

@ -2,18 +2,18 @@ define([
'jquery',
'/common/modes.js',
'/common/themes.js',
'/bower_components/file-saver/FileSaver.min.js'
], function ($, Modes, Themes) {
var saveAs = window.saveAs;
var module = {};
module.create = function (CMeditor, ifrw, Cryptpad) {
module.create = function (ifrw, Cryptpad, defaultMode, CMeditor) {
var exp = {};
var Messages = Cryptpad.Messages;
var CodeMirror = exp.CodeMirror = CMeditor;
CodeMirror.modeURL = "/bower_components/codemirror/mode/%N/%N.js";
CodeMirror.modeURL = "cm/mode/%N/%N";
var $pad = $('#pad-iframe');
var $textarea = exp.$textarea = $pad.contents().find('#editor1');
@ -21,12 +21,14 @@ define([
var Title;
var onLocal = function () {};
var $rightside;
var $drawer;
exp.init = function (local, title, toolbar) {
if (typeof local === "function") {
onLocal = local;
}
Title = title;
$rightside = toolbar.$rightside;
$drawer = toolbar.$drawer;
};
var editor = exp.editor = CMeditor.fromTextArea($textarea[0], {
@ -41,14 +43,16 @@ define([
extraKeys: {"Shift-Ctrl-R": undefined},
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
mode: "javascript",
mode: defaultMode || "javascript",
readOnly: true
});
editor.setValue(Messages.codeInitialState);
var setMode = exp.setMode = function (mode, cb) {
exp.highlightMode = mode;
if (mode !== "text") { CMeditor.autoLoadMode(editor, mode); }
if (mode !== "text") {
CMeditor.autoLoadMode(editor, mode);
}
editor.setOption('mode', mode);
if (exp.$language) {
var name = exp.$language.find('a[data-value="' + mode + '"]').text() || undefined;
@ -96,8 +100,18 @@ define([
var text = '';
lines.some(function (line) {
// lines including a c-style comment are also valuable
var clike = /^\s*(\/\*|\/\/)(.*)?(\*\/)*$/;
if (clike.test(line)) {
line.replace(clike, function (a, one, two) {
if (!(two && two.replace)) { return; }
text = two.replace(/\*\/\s*$/, '').trim();
});
return true;
}
// lisps?
var lispy = /^\s*(;|#\|)(.*?)$/;
var lispy = /^\s*(;|#\|)+(.*?)$/;
if (lispy.test(line)) {
line.replace(lispy, function (a, one, two) {
text = two;
@ -107,7 +121,7 @@ define([
// lines beginning with a hash are potentially valuable
// works for markdown, python, bash, etc.
var hash = /^#(.*?)$/;
var hash = /^#+(.*?)$/;
if (hash.test(line)) {
line.replace(hash, function (a, one) {
text = one;
@ -115,16 +129,6 @@ define([
return true;
}
// lines including a c-style comment are also valuable
var clike = /^\s*(\/\*|\/\/)(.*)?(\*\/)*$/;
if (clike.test(line)) {
line.replace(clike, function (a, one, two) {
if (!(two && two.replace)) { return; }
text = two.replace(/\*\/\s*$/, '').trim();
});
return true;
}
// TODO make one more pass for multiline comments
});
@ -148,6 +152,7 @@ define([
options: options, // Entries displayed in the menu
left: true, // Open to the left of the button
isSelect: true,
feedback: 'CODE_LANGUAGE',
};
var $block = exp.$language = Cryptpad.createDropdown(dropdownConfig);
$block.find('button').attr('title', Messages.languageButtonTitle);
@ -156,7 +161,7 @@ define([
onLocal();
});
if ($rightside) { $rightside.append($block); }
if ($drawer) { $drawer.append($block); }
if (cb) { cb(); }
};
@ -181,7 +186,8 @@ define([
options: options, // Entries displayed in the menu
left: true, // Open to the left of the button
isSelect: true,
initialValue: lastTheme
initialValue: lastTheme,
feedback: 'CODE_THEME',
};
var $block = exp.$theme = Cryptpad.createDropdown(dropdownConfig);
$block.find('button').attr('title', Messages.themeButtonTitle);
@ -194,7 +200,7 @@ define([
localStorage.setItem(themeKey, theme);
});
if ($rightside) { $rightside.append($block); }
if ($drawer) { $drawer.append($block); }
if (cb) { cb(); }
};

@ -114,14 +114,9 @@ define([
window.open($link.attr('href'), '_blank');
});
// TODO add button to table which copies link to clipboard?
//APP.toolbar.addElement(['fileshare'], {});
var title = metadata.name;
common.renamePad(title || "", href, function (err) {
if (err) { return void console.error(err); } // TODO
console.log(title);
var onComplete = function () {
common.log(Messages._getKey('upload_success', [title]));
common.prepareFeedback('upload')();
@ -132,6 +127,13 @@ define([
queue.inProgress = false;
queue.next();
};
if (config.noStore) { return void onComplete(); }
common.renamePad(title || "", href, function (err) {
if (err) { return void console.error(err); } // TODO
onComplete();
});
//Title.updateTitle(title || "", href);
//APP.toolbar.title.show();
@ -240,6 +242,10 @@ define([
};
var onFileDrop = File.onFileDrop = function (file, e) {
if (!common.isLoggedIn()) {
return common.alert(common.Messages.upload_mustLogin);
}
Array.prototype.slice.call(file).forEach(function (d) {
handleFile(d, e);
});
@ -271,6 +277,7 @@ define([
})
.on('drop', function (e) {
e.stopPropagation();
var dropped = e.originalEvent.dataTransfer.files;
counter = 0;
$hoverArea.removeClass('hovering');

@ -54,7 +54,7 @@ Version 1
if (!hash) { return; }
var parsed = {};
var hashArr = fixDuplicateSlashes(hash).split('/');
if (['media', 'file', 'user'].indexOf(type) === -1) {
if (['media', 'file', 'user', 'invite'].indexOf(type) === -1) {
parsed.type = 'pad';
if (hash.slice(0,1) !== '/' && hash.length >= 56) {
// Old hash
@ -93,6 +93,16 @@ Version 1
}
return parsed;
}
if (['invite'].indexOf(type) !== -1) {
parsed.type = 'invite';
if (hashArr[1] && hashArr[1] === '1') {
parsed.version = 1;
parsed.channel = hashArr[2];
parsed.pubkey = hashArr[3].replace(/-/g, '/');
return parsed;
}
return parsed;
}
return;
};
var parsePadUrl = Hash.parsePadUrl = function (href) {
@ -320,5 +330,11 @@ Version 1
return hash;
};
Hash.createInviteUrl = function (curvePublic, channel) {
channel = channel || Hash.createChannelId();
return window.location.origin + '/invite/#/1/' + channel +
'/' + curvePublic.replace(/\//g, '-') + '/';
};
return Hash;
});

@ -44,7 +44,6 @@ define([
History.readOnly = 2;
}
else if (!secret.keys.validateKey) {
secret.keys.validateKey = true;
History.readOnly = 0;
}
@ -70,6 +69,10 @@ define([
return;
}
if (parsed[0] !== 'FULL_HISTORY') { return; }
if (parsed[1] && parsed[1].validateKey) { // First message
secret.keys.validateKey = parsed[1].validateKey;
return;
}
msg = parsed[1][4];
if (msg) {
msg = msg.replace(/^cp\|/, '');
@ -129,6 +132,13 @@ define([
var $right = $toolbar.find('.cryptpad-toolbar-rightside');
var $cke = $toolbar.find('.cke_toolbox_main');
$hist.html('').show();
$left.hide();
$right.hide();
$cke.hide();
common.spinner($hist).get().show();
var onUpdate;
var update = function () {
@ -162,10 +172,7 @@ define([
// Create the history toolbar
var display = function () {
$hist.html('').show();
$left.hide();
$right.hide();
$cke.hide();
$hist.html('');
var $prev =$('<button>', {
'class': 'previous fa fa-step-backward buttonPrimary',
title: Messages.history_prev
@ -191,7 +198,7 @@ define([
var $close = $('<button>', {
'class':'closeHistory',
title: Messages.history_closeTitle
}).text(Messages.history_close).appendTo($nav);
}).text(Messages.history_closeTitle).appendTo($nav);
var $rev = $('<button>', {
'class':'revertHistory buttonSuccess',
title: Messages.history_restoreTitle
@ -222,7 +229,7 @@ define([
if (e.which === 33) { p(); return render(getNext(10)); } // PageUp
if (e.which === 34) { p(); return render(getPrevious(10)); } // PageUp
if (e.which === 27) { p(); $close.click(); }
}).focus();
}).keyup(function (e) { e.stopPropagation(); }).focus();
$cur.on('change', function () {
render( get($cur.val() - 1) );
});

@ -19,11 +19,11 @@ define([
Alertify._$$alertify.delay = AppConfig.notificationTimeout || 5000;
var findCancelButton = UI.findCancelButton = function () {
return $('button.cancel');
return $('button.cancel').last();
};
var findOKButton = UI.findOKButton = function () {
return $('button.ok');
return $('button.ok').last();
};
var listenForKeys = UI.listenForKeys = function (yes, no) {
@ -74,14 +74,22 @@ define([
findCancelButton().click();
});
// Make sure we don't call both the "yes" and "no" handlers if we use "findOKButton().click()"
// in the callback
var isClicked = false;
Alertify
.defaultValue(def || '')
.okBtn(opt.ok || Messages.okButton || 'OK')
.cancelBtn(opt.cancel || Messages.cancelButton || 'Cancel')
.prompt(msg, function (val, ev) {
if (isClicked) { return; }
isClicked = true;
cb(val, ev);
stopListening(keyHandler);
}, function (ev) {
if (isClicked) { return; }
isClicked = true;
cb(null, ev);
stopListening(keyHandler);
});
@ -98,13 +106,21 @@ define([
findCancelButton().click();
});
// Make sure we don't call both the "yes" and "no" handlers if we use "findOKButton().click()"
// in the callback
var isClicked = false;
Alertify
.okBtn(opt.ok || Messages.okButton || 'OK')
.cancelBtn(opt.cancel || Messages.cancelButton || 'Cancel')
.confirm(msg, function () {
if (isClicked) { return; }
isClicked = true;
cb(true);
stopListening(keyHandler);
}, function () {
if (isClicked) { return; }
isClicked = true;
cb(false);
stopListening(keyHandler);
});
@ -187,7 +203,7 @@ define([
var $loadingTip = $('<div>', {'id': 'loadingTip'});
$('<span>', {'class': 'tips'}).text(getRandomTip()).appendTo($loadingTip);
$loadingTip.css({
'top': $('body').height()/2 + $container.height()/2 + 20 + 'px'
'bottom': $('body').height()/2 - $container.height()/2 + 20 + 'px'
});
$('body').append($loadingTip);
}
@ -247,5 +263,30 @@ define([
};
};
var $fileIcon = $('<span>', {"class": "fa fa-file-text-o file icon"});
var $fileAppIcon = $('<span>', {"class": "fa fa-file-text-o file icon fileColor"});
var $padIcon = $('<span>', {"class": "fa fa-file-word-o file icon padColor"});
var $codeIcon = $('<span>', {"class": "fa fa-file-code-o file icon codeColor"});
var $slideIcon = $('<span>', {"class": "fa fa-file-powerpoint-o file icon slideColor"});
var $pollIcon = $('<span>', {"class": "fa fa-calendar file icon pollColor"});
var $whiteboardIcon = $('<span>', {"class": "fa fa-paint-brush whiteboardColor"});
var $contactsIcon = $('<span>', {"class": "fa fa-users friendsColor"});
UI.getIcon = function (type) {
var $icon;
switch(type) {
case 'pad': $icon = $padIcon.clone(); break;
case 'file': $icon = $fileAppIcon.clone(); break;
case 'code': $icon = $codeIcon.clone(); break;
case 'slide': $icon = $slideIcon.clone(); break;
case 'poll': $icon = $pollIcon.clone(); break;
case 'whiteboard': $icon = $whiteboardIcon.clone(); break;
case 'contacts': $icon = $contactsIcon.clone(); break;
default: $icon = $fileIcon.clone();
}
return $icon;
};
return UI;
});

@ -0,0 +1,630 @@
define([
'jquery',
'/bower_components/chainpad-crypto/crypto.js',
'/common/curve.js'
], function ($, Crypto, Curve) {
var Msg = {};
var Types = {
message: 'MSG',
update: 'UPDATE',
unfriend: 'UNFRIEND'
};
// TODO
// - mute a channel (hide notifications or don't open it?)
var ready = [];
var pending = {};
var createData = Msg.createData = function (common, hash) {
var proxy = common.getProxy();
return {
channel: hash || common.createChannelId(),
displayName: proxy[common.displayNameKey],
profile: proxy.profile && proxy.profile.view,
edPublic: proxy.edPublic,
curvePublic: proxy.curvePublic,
avatar: proxy.profile && proxy.profile.avatar
};
};
var getFriend = function (common, pubkey) {
var proxy = common.getProxy();
if (pubkey === proxy.curvePublic) {
var data = createData(common);
delete data.channel;
return data;
}
return proxy.friends ? proxy.friends[pubkey] : undefined;
};
var removeFromFriendList = Msg.removeFromFriendList = function (common, curvePublic, cb) {
var proxy = common.getProxy();
if (!proxy.friends) {
return;
}
var friends = proxy.friends;
delete friends[curvePublic];
common.whenRealtimeSyncs(common.getRealtime(), cb);
};
var getFriendList = Msg.getFriendList = function (common) {
var proxy = common.getProxy();
if (!proxy.friends) { proxy.friends = {}; }
return proxy.friends;
};
Msg.getFriendChannelsList = function (common) {
var friends = getFriendList(common);
var list = [];
Object.keys(friends).forEach(function (key) {
if (key === "me") { return; }
list.push(friends[key].channel);
});
return list;
};
// Messaging tools
var avatars = {};
var addToFriendListUI = function (common, $block, open, remove, f) {
var proxy = common.getProxy();
var friends = proxy.friends || {};
if (f === "me") { return; }
var data = friends[f];
var $friend = $('<div>', {'class': 'friend avatar'}).appendTo($block);
$friend.data('key', f);
var $rightCol = $('<span>', {'class': 'right-col'});
$('<span>', {'class': 'name'}).text(data.displayName).appendTo($rightCol);
var $remove = $('<span>', {'class': 'remove fa fa-user-times'}).appendTo($rightCol);
$remove.attr('title', common.Messages.contacts_remove);
$friend.dblclick(function () {
if (data.profile) {
window.open('/profile/#' + data.profile);
}
});
$friend.click(function () {
open(data.curvePublic);
});
$remove.click(function (e) {
e.stopPropagation();
common.confirm(common.Messages._getKey('contacts_confirmRemove', [
common.fixHTML(data.displayName)
]), function (yes) {
if (!yes) { return; }
remove(data.curvePublic);
}, null, true);
});
if (data.avatar && avatars[data.avatar]) {
$friend.append(avatars[data.avatar]);
$friend.append($rightCol);
} else {
common.displayAvatar($friend, data.avatar, data.displayName, function ($img) {
if (data.avatar && $img) {
avatars[data.avatar] = $img[0].outerHTML;
}
$friend.append($rightCol);
});
}
};
Msg.getFriendListUI = function (common, open, remove) {
var proxy = common.getProxy();
var $block = $('<div>');
var friends = proxy.friends || {};
Object.keys(friends).forEach(function (f) {
addToFriendListUI(common, $block, open, remove, f);
});
return $block;
};
Msg.createOwnedChannel = function (common, channelId, validateKey, owners, cb) {
var network = common.getNetwork();
network.join(channelId).then(function (wc) {
var cfg = {
validateKey: validateKey,
owners: owners
};
var msg = ['GET_HISTORY', wc.id, cfg];
network.sendto(network.historyKeeper, JSON.stringify(msg)).then(cb, function (err) {
throw new Error(err);
});
}, function (err) {
throw new Error(err);
});
};
var channels = Msg.channels = window.channels = {};
var pushMsg = function (common, channel, cryptMsg) {
var msg = channel.encryptor.decrypt(cryptMsg);
var parsedMsg = JSON.parse(msg);
if (parsedMsg[0] === Types.message) {
parsedMsg.shift();
channel.messages.push([cryptMsg.slice(0,64), parsedMsg]);
return true;
}
var proxy;
if (parsedMsg[0] === Types.update) {
proxy = common.getProxy();
if (parsedMsg[1] === common.getProxy().curvePublic) { return; }
var newdata = parsedMsg[3];
var data = getFriend(common, parsedMsg[1]);
var types = [];
Object.keys(newdata).forEach(function (k) {
if (data[k] !== newdata[k]) {
types.push(k);
data[k] = newdata[k];
}
});
channel.updateUI(types);
return;
}
if (parsedMsg[0] === Types.unfriend) {
proxy = common.getProxy();
removeFromFriendList(common, channel.friendEd, function () {
channel.wc.leave(Types.unfriend);
channel.removeUI();
});
return;
}
};
var updateMyData = function (common) {
var friends = getFriendList(common);
var mySyncData = friends.me;
var myData = createData(common);
if (!mySyncData || mySyncData.displayName !== myData.displayName
|| mySyncData.profile !== myData.profile
|| mySyncData.avatar !== myData.avatar) {
delete myData.channel;
Object.keys(channels).forEach(function (chan) {
var channel = channels[chan];
var msg = [Types.update, myData.curvePublic, +new Date(), myData];
var msgStr = JSON.stringify(msg);
var cryptMsg = channel.encryptor.encrypt(msgStr);
channel.wc.bcast(cryptMsg).then(function () {
channel.refresh();
}, function (err) {
console.error(err);
});
});
friends.me = myData;
}
};
var onChannelReady = function (common, chanId) {
if (ready.indexOf(chanId) !== -1) { return; }
ready.push(chanId);
var friends = getFriendList(common);
if (ready.length === Object.keys(friends).length) {
// All channels are ready
updateMyData(common);
}
return ready.length;
};
var onDirectMessage = function (common, msg, sender) {
if (sender !== Msg.hk) { return; }
var parsed = JSON.parse(msg);
if ((parsed.validateKey || parsed.owners) && parsed.channel) {
return;
}
if (parsed.state && parsed.state === 1 && parsed.channel) {
if (channels[parsed.channel]) {
// parsed.channel is Ready
// TODO: call a function that shows that the channel is ready? (remove a spinner, ...)
// channel[parsed.channel].ready();
channels[parsed.channel].ready = true;
onChannelReady(common, parsed.channel);
var updateTypes = channels[parsed.channel].updateOnReady;
if (updateTypes) {
channels[parsed.channel].updateUI(updateTypes);
}
}
return;
}
var chan = parsed[3];
if (!chan || !channels[chan]) { return; }
pushMsg(common, channels[chan], parsed[4]);
};
var onMessage = function (common, msg, sender, chan) {
if (!channels[chan.id]) { return; }
var isMessage = pushMsg(common, channels[chan.id], msg);
if (isMessage) {
channels[chan.id].notify();
channels[chan.id].refresh();
}
};
var createChatBox = function (common, $container, curvePublic) {
var data = getFriend(common, curvePublic);
var proxy = common.getProxy();
var $header = $('<div>', {'class': 'header avatar'}).appendTo($container);
/*
var $removeHistory = $('<button>', {
'class': 'remove-history'
}).text('remove chat history').click(function () {
Cryptpad.confirm('are you sure?', function (yes) {
if (!yes) { return; }
Cryptpad.clearOwnedChannel(data.channel, function (e) {
if (e) {
console.error(e);
Cryptpad.alert("Something went wrong");
return;
}
});
});
});
$removeHistory.appendTo($header); //rightCol);
*/
$('<div>', {'class': 'messages'}).appendTo($container);
var $inputBlock = $('<div>', {'class': 'input'}).appendTo($container);
// Input
var channel = channels[data.channel];
var $input = $('<input>', {type: 'text'}).appendTo($inputBlock);
var sending = false;
var send = function () {
if (sending) { return; }
if (!$input.val()) { return; }
// Send the message
var msg = [Types.message, proxy.curvePublic, +new Date(), $input.val()];
var msgStr = JSON.stringify(msg);
var cryptMsg = channel.encryptor.encrypt(msgStr);
sending = true;
channel.wc.bcast(cryptMsg).then(function () {
$input.val('');
pushMsg(common, channel, cryptMsg);
channel.refresh();
sending = false;
}, function (err) {
sending = false;
console.error(err);
});
};
$('<button>').text(common.Messages.contacts_send).appendTo($inputBlock).click(send);
$input.on('keypress', function (e) {
if (e.which === 13) { send(); }
});
// Header
var $rightCol = $('<span>', {'class': 'right-col'});
$('<span>', {'class': 'name'}).text(data.displayName).appendTo($rightCol);
if (data.avatar && avatars[data.avatar]) {
$header.append(avatars[data.avatar]);
$header.append($rightCol);
} else {
common.displayAvatar($header, data.avatar, data.displayName, function ($img) {
if (data.avatar && $img) {
avatars[data.avatar] = $img[0].outerHTML;
}
$header.append($rightCol);
});
}
};
Msg.init = function (common, $listContainer, $msgContainer) {
var network = common.getNetwork();
var proxy = common.getProxy();
Msg.hk = network.historyKeeper;
var friends = getFriendList(common);
network.on('message', function(msg, sender) {
onDirectMessage(common, msg, sender);
});
// Refresh the active channel
var refresh = function (curvePublic) {
if (Msg.active !== curvePublic) { return; }
var data = friends[curvePublic];
if (!data) { return; }
var channel = channels[data.channel];
if (!channel) { return; }
var $chat = $msgContainer.find('.chat').filter(function (idx, el) {
return $(el).data('key') === curvePublic;
});
if (!$chat.length) { return; }
// Add new messages
var messages = channel.messages;
var $messages = $chat.find('.messages');
var $msg, msg, date, name;
var last = typeof(channel.lastDisplayed) === 'number'? channel.lastDisplayed: -1;
for (var i = last + 1; i<messages.length; i++) {
msg = messages[i][1]; // 0 is the hash, 1 the array
$msg = $('<div>', {'class': 'message'}).appendTo($messages);
// date
date = msg[1] ? new Date(msg[1]).toLocaleString() : '?';
//$('<div>', {'class':'date'}).text(date).appendTo($msg);
$msg.attr('title', date);
// name
if (msg[0] !== channel.lastSender) {
name = getFriend(common, msg[0]).displayName;
$('<div>', {'class':'sender'}).text(name).appendTo($msg);
}
channel.lastSender = msg[0];
// content
$('<div>', {'class':'content'}).text(msg[2]).appendTo($msg);
}
$messages.scrollTop($messages[0].scrollHeight);
channel.lastDisplayed = i-1;
channel.unnotify();
if (messages.length > 10) {
var lastKnownMsg = messages[messages.length - 11];
data.lastKnownHash = lastKnownMsg[0];
}
};
// Display a new channel
var display = function (curvePublic) {
$msgContainer.find('.info').hide();
var isNew = false;
var $chat = $msgContainer.find('.chat').filter(function (idx, el) {
return $(el).data('key') === curvePublic;
});
if (!$chat.length) {
$chat = $('<div>', {'class':'chat'})
.data('key', curvePublic).appendTo($msgContainer);
createChatBox(common, $chat, curvePublic);
isNew = true;
}
// Show the correct div
$msgContainer.find('.chat').hide();
$chat.show();
Msg.active = curvePublic;
refresh(curvePublic);
};
var remove = function (curvePublic) {
var data = getFriend(common, curvePublic);
var channel = channels[data.channel];
//var newdata = createData(common, data.channel);
var msg = [Types.unfriend, proxy.curvePublic, +new Date()];
var msgStr = JSON.stringify(msg);
var cryptMsg = channel.encryptor.encrypt(msgStr);
channel.wc.bcast(cryptMsg).then(function () {
removeFromFriendList(common, curvePublic, function () {
channel.wc.leave(Types.unfriend);
channel.removeUI();
});
}, function (err) {
console.error(err);
});
};
// Display friend list
common.getFriendListUI(common, display, remove).appendTo($listContainer);
// Notify on new messages
var notify = function (curvePublic) {
//if (Msg.active === curvePublic) { return; }
var $friend = $listContainer.find('.friend').filter(function (idx, el) {
return $(el).data('key') === curvePublic;
});
$friend.addClass('notify');
common.notify();
};
var unnotify = function (curvePublic) {
var $friend = $listContainer.find('.friend').filter(function (idx, el) {
return $(el).data('key') === curvePublic;
});
$friend.removeClass('notify');
};
var removeUI = function (curvePublic) {
var $friend = $listContainer.find('.friend').filter(function (idx, el) {
return $(el).data('key') === curvePublic;
});
var $chat = $msgContainer.find('.chat').filter(function (idx, el) {
return $(el).data('key') === curvePublic;
});
$friend.remove();
$chat.remove();
$msgContainer.find('.info').show();
};
var updateUI = function (curvePublic, types) {
var data = getFriend(common, curvePublic);
var chan = channels[data.channel];
if (!chan.ready) {
chan.updateOnReady = (chan.updateOnReady || []).concat(types);
return;
}
var $friend = $listContainer.find('.friend').filter(function (idx, el) {
return $(el).data('key') === curvePublic;
});
if (types.indexOf('displayName') >= 0) {
$friend.find('.name').text(data.displayName);
}
if (types.indexOf('avatar') >= 0) {
$friend.find('.default').remove();
$friend.find('media-tag').remove();
if (data.avatar && avatars[data.avatar]) {
$friend.prepend(avatars[data.avatar]);
} else {
common.displayAvatar($friend, data.avatar, data.displayName, function ($img) {
if (data.avatar && $img) {
avatars[data.avatar] = $img[0].outerHTML;
}
});
}
}
};
// Open the channels
var openFriendChannel = function (f) {
if (f === "me") { return; }
var data = friends[f];
var keys = Curve.deriveKeys(data.curvePublic, proxy.curvePrivate);
var encryptor = Curve.createEncryptor(keys);
network.join(data.channel).then(function (chan) {
channels[data.channel] = {
friendEd: f,
keys: keys,
encryptor: encryptor,
messages: [],
refresh: function () { refresh(data.curvePublic); },
notify: function () { notify(data.curvePublic); },
unnotify: function () { unnotify(data.curvePublic); },
removeUI: function () { removeUI(data.curvePublic); },
updateUI: function (types) { updateUI(data.curvePublic, types); },
wc: chan
};
chan.on('message', function (msg, sender) {
onMessage(common, msg, sender, chan);
});
var cfg = {
validateKey: keys.validateKey,
owners: [proxy.edPublic, data.edPublic],
lastKnownHash: data.lastKnownHash
};
var msg = ['GET_HISTORY', chan.id, cfg];
network.sendto(network.historyKeeper, JSON.stringify(msg))
.then($.noop, function (err) {
throw new Error(err);
});
}, function (err) {
console.error(err);
});
};
Object.keys(friends).forEach(openFriendChannel);
var checkNewFriends = function () {
Object.keys(friends).forEach(function (f) {
var $friend = $listContainer.find('.friend').filter(function (idx, el) {
return $(el).data('key') === f;
});
if (!$friend.length) {
openFriendChannel(f);
addToFriendListUI(common, $listContainer.find('> div'), display, remove, f);
}
});
};
common.onDisplayNameChanged(function () {
checkNewFriends();
updateMyData(common);
});
};
// Invitation
var addToFriendList = Msg.addToFriendList = function (common, data, cb) {
var proxy = common.getProxy();
if (!proxy.friends) {
proxy.friends = {};
}
var friends = proxy.friends;
var pubKey = data.curvePublic;
if (pubKey === proxy.curvePublic) { return void cb("E_MYKEY"); }
friends[pubKey] = data;
common.whenRealtimeSyncs(common.getRealtime(), function () {
common.pinPads([data.channel], cb);
});
common.changeDisplayName(proxy[common.displayNameKey]);
};
Msg.addDirectMessageHandler = function (common) {
var network = common.getNetwork();
if (!network) { return void console.error('Network not ready'); }
network.on('message', function (message, sender) {
var msg;
if (sender === network.historyKeeper) { return; }
try {
var parsed = common.parsePadUrl(window.location.href);
if (!parsed.hashData) { return; }
var chan = parsed.hashData.channel;
// Decrypt
var keyStr = parsed.hashData.key;
var cryptor = Crypto.createEditCryptor(keyStr);
var key = cryptor.cryptKey;
var decryptMsg = Crypto.decrypt(message, key);
// Parse
msg = JSON.parse(decryptMsg);
if (msg[1] !== parsed.hashData.channel) { return; }
var msgData = msg[2];
var msgStr;
if (msg[0] === "FRIEND_REQ") {
msg = ["FRIEND_REQ_NOK", chan];
var todo = function (yes) {
if (yes) {
pending[sender] = msgData;
msg = ["FRIEND_REQ_OK", chan, createData(common, msgData.channel)];
}
msgStr = Crypto.encrypt(JSON.stringify(msg), key);
network.sendto(sender, msgStr);
};
var existing = getFriend(common, msgData.curvePublic);
if (existing) {
todo(true);
return;
}
var confirmMsg = common.Messages._getKey('contacts_request', [
common.fixHTML(msgData.displayName)
]);
common.confirm(confirmMsg, todo, null, true);
return;
}
if (msg[0] === "FRIEND_REQ_OK") {
addToFriendList(common, msgData, function (err) {
if (err) {
return void common.log(common.Messages.contacts_addError);
}
common.log(common.Messages.contacts_added);
var msg = ["FRIEND_REQ_ACK", chan];
var msgStr = Crypto.encrypt(JSON.stringify(msg), key);
network.sendto(sender, msgStr);
});
return;
}
if (msg[0] === "FRIEND_REQ_NOK") {
common.log(common.Messages.contacts_rejected);
return;
}
if (msg[0] === "FRIEND_REQ_ACK") {
var data = pending[sender];
if (!data) { return; }
addToFriendList(common, data, function (err) {
if (err) {
return void common.log(common.Messages.contacts_addError);
}
common.log(common.Messages.contacts_added);
});
return;
}
// TODO: timeout ACK: warn the user
} catch (e) {
console.error("Cannot parse direct message", msg || message, "from", sender, e);
}
});
};
Msg.inviteFromUserlist = function (common, netfluxId) {
var network = common.getNetwork();
var parsed = common.parsePadUrl(window.location.href);
if (!parsed.hashData) { return; }
// Message
var chan = parsed.hashData.channel;
var myData = createData(common);
var msg = ["FRIEND_REQ", chan, myData];
// Encryption
var keyStr = parsed.hashData.key;
var cryptor = Crypto.createEditCryptor(keyStr);
var key = cryptor.cryptKey;
var msgStr = Crypto.encrypt(JSON.stringify(msg), key);
// Send encrypted message
network.sendto(netfluxId, msgStr);
};
return Msg;
});

@ -1,7 +1,7 @@
define(function () {
var module = {};
module.create = function (UserList, Title, cfg) {
module.create = function (UserList, Title, cfg, Cryptpad) {
var exp = {};
exp.update = function (shjson) {
@ -15,6 +15,14 @@ define(function () {
metadata = json.metadata;
}
if (typeof metadata === "object") {
if (Cryptpad) {
if (typeof(metadata.type) === 'undefined') {
// initialize pad type by location.pathname
metadata.type = Cryptpad.getAppType();
}
} else {
console.log("Cryptpad should exist but it does not");
}
if (metadata.users) {
var userData = metadata.users;
// Update the local user data

@ -1,4 +1,4 @@
define(function () {
define(['jquery'], function ($) {
var module = {};
module.create = function (cfg, onLocal, Cryptpad) {
@ -45,7 +45,8 @@ define(function () {
};
// update title: href is optional; if not specified, we use window.location.href
exp.updateTitle = function (newTitle, href) {
exp.updateTitle = function (newTitle, href, cb) {
cb = cb || $.noop;
if (newTitle === exp.title) { return; }
// Change the title now, and set it back to the old value if there is an error
var oldTitle = exp.title;
@ -54,9 +55,10 @@ define(function () {
console.log("Couldn't set pad title");
console.error(err);
updateLocalTitle(oldTitle);
return;
return void cb(err);
}
updateLocalTitle(data);
cb(null, data);
if (!$title) { return; }
$title.find('span.title').text(data);
$title.find('input').val(data);

@ -49,18 +49,22 @@ define(function () {
exp.myUserName = myUserNameTemp;
myData = {};
myData[exp.myNetfluxId] = {
name: exp.myUserName,
uid: Cryptpad.getUid(),
name: exp.myUserName,
uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl(),
curvePublic: Cryptpad.getProxy().curvePublic
};
addToUserData(myData);
Cryptpad.setAttribute('username', exp.myUserName, function (err) {
/*Cryptpad.setAttribute('username', exp.myUserName, function (err) {
if (err) {
console.log("Couldn't set username");
console.error(err);
return;
}
if (typeof cb === "function") { cb(); }
});
});*/
if (typeof cb === "function") { cb(); }
};
exp.getLastName = function ($changeNameButton, isNew) {
@ -78,6 +82,9 @@ define(function () {
myData[exp.myNetfluxId] = {
name: "",
uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl(),
curvePublic: Cryptpad.getProxy().curvePublic
};
addToUserData(myData);
onLocal();

@ -135,9 +135,35 @@ define([], function () {
return g;
};
/* takes a function (f) and a time (t) in ms. returns a function wrapper
which prevents the internal function from being called more than once
every t ms. if the function is prevented, returns time til next valid
execution, else null.
*/
Util.notAgainForAnother = function (f, t) {
if (typeof(f) !== 'function' || typeof(t) !== 'number') {
throw new Error("invalid inputs");
}
var last = null;
return function () {
var now = +new Date();
if (last && now <= last + t) { return t - (now - last); }
last = now;
f();
return null;
};
};
Util.createRandomInteger = function () {
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
};
Util.getAppType = function () {
var parts = window.location.pathname.split('/')
.filter(function (x) { return x; });
if (!parts[0]) { return ''; }
return parts[0];
};
return Util;
});

@ -10,14 +10,22 @@ define([
'/common/common-userlist.js',
'/common/common-title.js',
'/common/common-metadata.js',
'/common/common-messaging.js',
'/common/common-codemirror.js',
'/common/common-file.js',
'/file/file-crypto.js',
'/common/clipboard.js',
'/common/pinpad.js',
'/customize/application_config.js'
'/customize/application_config.js',
'/common/media-tag.js',
], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata,
CodeMirror, Files, Clipboard, Pinpad, AppConfig) {
Messaging, CodeMirror, Files, FileCrypto, Clipboard, Pinpad, AppConfig, MediaTag) {
// Configure MediaTags to use our local viewer
if (MediaTag && MediaTag.PdfPlugin) {
MediaTag.PdfPlugin.viewer = '/common/pdfjs/web/viewer.html';
}
/* This file exposes functionality which is specific to Cryptpad, but not to
any particular pad type. This includes functions for committing metadata
@ -32,9 +40,8 @@ define([
Clipboard: Clipboard,
donateURL: 'https://accounts.cryptpad.fr/#/donate?on=' + origin,
upgradeURL: 'https://accounts.cryptpad.fr/#/?on=' + origin,
account: {
usage: 0,
},
account: {},
MediaTag: MediaTag,
};
// constants
@ -50,6 +57,7 @@ define([
var store;
var rpc;
var anon_rpc;
// import UI elements
common.findCancelButton = UI.findCancelButton;
@ -67,6 +75,7 @@ define([
common.errorLoadingScreen = UI.errorLoadingScreen;
common.notify = UI.notify;
common.unnotify = UI.unnotify;
common.getIcon = UI.getIcon;
// import common utilities for export
common.find = Util.find;
@ -76,13 +85,15 @@ define([
var deduplicateString = common.deduplicateString = Util.deduplicateString;
common.uint8ArrayToHex = Util.uint8ArrayToHex;
common.replaceHash = Util.replaceHash;
var getHash = common.getHash = Util.getHash;
common.getHash = Util.getHash;
common.fixFileName = Util.fixFileName;
common.bytesToMegabytes = Util.bytesToMegabytes;
common.bytesToKilobytes = Util.bytesToKilobytes;
common.fetch = Util.fetch;
common.throttle = Util.throttle;
common.createRandomInteger = Util.createRandomInteger;
common.getAppType = Util.getAppType;
common.notAgainForAnother = Util.notAgainForAnother;
// import hash utilities for export
var createRandomHash = common.createRandomHash = Hash.createRandomHash;
@ -103,6 +114,17 @@ define([
common.findWeaker = Hash.findWeaker;
common.findStronger = Hash.findStronger;
common.serializeHash = Hash.serializeHash;
common.createInviteUrl = Hash.createInviteUrl;
// Messaging
common.initMessaging = Messaging.init;
common.addDirectMessageHandler = Messaging.addDirectMessageHandler;
common.inviteFromUserlist = Messaging.inviteFromUserlist;
common.createOwnedChannel = Messaging.createOwnedChannel;
common.getFriendList = Messaging.getFriendList;
common.getFriendChannelsList = Messaging.getFriendChannelsList;
common.getFriendListUI = Messaging.getFriendListUI;
common.createData = Messaging.createData;
// Userlist
common.createUserList = UserList.create;
@ -144,7 +166,34 @@ define([
}
return;
};
common.getUserlist = function () {
if (store) {
if (store.getProxy() && store.getProxy().info) {
return store.getProxy().info.userList;
}
}
return;
};
common.getProfileUrl = function () {
if (store && store.getProfile()) {
return store.getProfile().view;
}
};
common.getAvatarUrl = function () {
if (store && store.getProfile()) {
return store.getProfile().avatar;
}
};
common.getDisplayName = function () {
if (getProxy()) {
return getProxy()[common.displayNameKey] || '';
}
return '';
};
var randomToken = function () {
return Math.random().toString(16).replace(/0./, '');
};
var feedback = common.feedback = function (action, force) {
if (force !== true) {
if (!action) { return; }
@ -153,7 +202,7 @@ define([
} catch (e) { return void console.error(e); }
}
var href = '/common/feedback.html?' + action + '=' + (+new Date());
var href = '/common/feedback.html?' + action + '=' + randomToken();
$.ajax({
type: "HEAD",
url: href,
@ -163,7 +212,20 @@ define([
common.reportAppUsage = function () {
var pattern = window.location.pathname.split('/')
.filter(function (x) { return x; }).join('.');
feedback(pattern);
if (/^#\/1\/view\//.test(window.location.hash)) {
feedback(pattern + '_VIEW');
} else {
feedback(pattern);
}
};
common.reportScreenDimensions = function () {
var h = window.innerHeight;
var w = window.innerWidth;
feedback('DIMENSIONS:' + h + 'x' + w);
};
common.reportLanguage = function () {
feedback('LANG_' + Messages._languageUsed);
};
common.getUid = function () {
@ -179,13 +241,29 @@ define([
return;
};
common.infiniteSpinnerDetected = false;
var whenRealtimeSyncs = common.whenRealtimeSyncs = function (realtime, cb) {
realtime.sync();
window.setTimeout(function () {
if (realtime.getAuthDoc() === realtime.getUserDoc()) {
return void cb();
}
var to = setTimeout(function () {
realtime.abort();
// don't launch more than one popup
if (common.infiniteSpinnerDetected) { return; }
// inform the user their session is in a bad state
common.confirm(Messages.realtime_unrecoverableError, function (yes) {
if (!yes) { return; }
window.location.reload();
});
common.infiniteSpinnerDetected = true;
}, 30000);
realtime.onSettle(function () {
clearTimeout(to);
cb();
});
}, 0);
@ -284,6 +362,21 @@ define([
typeof(proxy.edPublic) === 'string';
};
common.hasCurveKeys = function (proxy) {
return typeof(proxy) === 'object' &&
typeof(proxy.curvePrivate) === 'string' &&
typeof(proxy.curvePublic) === 'string';
};
common.getPublicKeys = function (proxy) {
proxy = proxy || common.getProxy();
if (!proxy || !proxy.edPublic || !proxy.curvePublic) { return; }
return {
curve: proxy.curvePublic,
ed: proxy.edPublic,
};
};
common.isArray = $.isArray;
/*
@ -406,13 +499,12 @@ define([
// STORAGE
common.setPadAttribute = function (attr, value, cb) {
getStore().setDrive([getHash(), attr].join('.'), value, function (err, data) {
cb(err, data);
});
var href = getRelativeHref(window.location.href);
getStore().setPadAttribute(href, attr, value, cb);
};
common.setAttribute = function (attr, value, cb) {
getStore().set(["cryptpad", attr].join('.'), value, function (err, data) {
cb(err, data);
if (cb) { cb(err, data); }
});
};
common.setLSAttribute = function (attr, value) {
@ -421,9 +513,8 @@ define([
// STORAGE
common.getPadAttribute = function (attr, cb) {
getStore().getDrive([getHash(), attr].join('.'), function (err, data) {
cb(err, data);
});
var href = getRelativeHref(window.location.href);
getStore().getPadAttribute(href, attr, cb);
};
common.getAttribute = function (attr, cb) {
getStore().get(["cryptpad", attr].join('.'), function (err, data) {
@ -514,9 +605,9 @@ define([
if (_onDisplayNameChanged.indexOf(h) !== -1) { return; }
_onDisplayNameChanged.push(h);
};
common.changeDisplayName = function (newName) {
common.changeDisplayName = function (newName, isLocal) {
_onDisplayNameChanged.forEach(function (h) {
h(newName);
h(newName, isLocal);
});
};
@ -530,7 +621,7 @@ define([
};
common.setPadTitle = function (name, padHref, cb) {
var href = padHref || window.location.href;
var href = typeof padHref === "string" ? padHref : window.location.href;
var parsed = parsePadUrl(href);
if (!parsed.hash) { return; }
href = getRelativeHref(href);
@ -684,6 +775,20 @@ define([
})
.filter(function (x) { return x; });
// Get the avatar
var profile = store.getProfile();
if (profile) {
var profileChan = profile.edit ? hrefToHexChannelId('/profile/#' + profile.edit) : null;
if (profileChan) { list.push(profileChan); }
var avatarChan = profile.avatar ? hrefToHexChannelId(profile.avatar) : null;
if (avatarChan) { list.push(avatarChan); }
}
if (getProxy().friends) {
var fList = common.getFriendChannelsList(common);
list = list.concat(fList);
}
list.push(common.base64ToHex(userChannel));
list.sort();
@ -703,14 +808,14 @@ define([
return false;
}
if (!rpc) {
console.error('[RPC_NOT_READY]');
console.error('RPC_NOT_READY');
return false;
}
return true;
};
common.arePinsSynced = function (cb) {
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
if (!pinsReady()) { return void cb ('RPC_NOT_READY'); }
var list = getCanonicalChannelList();
var local = Hash.hashChannelList(list);
@ -721,7 +826,7 @@ define([
};
common.resetPins = function (cb) {
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
if (!pinsReady()) { return void cb ('RPC_NOT_READY'); }
var list = getCanonicalChannelList();
rpc.reset(list, function (e, hash) {
@ -731,7 +836,7 @@ define([
};
common.pinPads = function (pads, cb) {
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
if (!pinsReady()) { return void cb ('RPC_NOT_READY'); }
rpc.pin(pads, function (e, hash) {
if (e) { return void cb(e); }
@ -740,7 +845,7 @@ define([
};
common.unpinPads = function (pads, cb) {
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
if (!pinsReady()) { return void cb ('RPC_NOT_READY'); }
rpc.unpin(pads, function (e, hash) {
if (e) { return void cb(e); }
@ -749,24 +854,48 @@ define([
};
common.getPinnedUsage = function (cb) {
if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); }
if (!pinsReady()) { return void cb('RPC_NOT_READY'); }
rpc.getFileListSize(function (err, bytes) {
common.account.usage = typeof(bytes) === 'number'? bytes: 0;
if (typeof(bytes) === 'number') {
common.account.usage = bytes;
}
cb(err, bytes);
});
};
common.getFileSize = function (href, cb) {
if (!anon_rpc) { return void cb('ANON_RPC_NOT_READY'); }
//if (!pinsReady()) { return void cb('RPC_NOT_READY'); }
var channelId = Hash.hrefToHexChannelId(href);
rpc.getFileSize(channelId, function (e, bytes) {
anon_rpc.send("GET_FILE_SIZE", channelId, function (e, response) {
if (e) { return void cb(e); }
cb(void 0, bytes);
if (response && response.length && typeof(response[0]) === 'number') {
return void cb(void 0, response[0]);
} else {
cb('INVALID_RESPONSE');
}
});
};
common.getMultipleFileSize = function (files, cb) {
if (!anon_rpc) { return void cb('ANON_RPC_NOT_READY'); }
if (!Array.isArray(files)) {
return void setTimeout(function () { cb('INVALID_FILE_LIST'); });
}
anon_rpc.send('GET_MULTIPLE_FILE_SIZE', files, function (e, res) {
if (e) { return cb(e); }
if (res && res.length && typeof(res[0]) === 'object') {
cb(void 0, res[0]);
} else {
cb('UNEXPECTED_RESPONSE');
}
});
};
common.updatePinLimit = function (cb) {
if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); }
if (!pinsReady()) { return void cb('RPC_NOT_READY'); }
rpc.updatePinLimits(function (e, limit, plan, note) {
if (e) { return cb(e); }
common.account.limit = limit;
@ -777,14 +906,24 @@ define([
};
common.getPinLimit = function (cb) {
if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); }
rpc.getLimit(function (e, limit, plan, note) {
if (e) { return cb(e); }
common.account.limit = limit;
common.account.plan = plan;
common.account.note = note;
cb(void 0, limit, plan, note);
});
if (!pinsReady()) { return void cb('RPC_NOT_READY'); }
var account = common.account;
var ALWAYS_REVALIDATE = true;
if (ALWAYS_REVALIDATE || typeof(account.limit) !== 'number' ||
typeof(account.plan) !== 'string' ||
typeof(account.note) !== 'string') {
return void rpc.getLimit(function (e, limit, plan, note) {
if (e) { return cb(e); }
common.account.limit = limit;
common.account.plan = plan;
common.account.note = note;
cb(void 0, limit, plan, note);
});
}
cb(void 0, account.limit, account.plan, account.note);
};
common.isOverPinLimit = function (cb) {
@ -806,33 +945,47 @@ define([
common.getPinnedUsage(todo);
};
common.clearOwnedChannel = function (channel, cb) {
if (!pinsReady()) { return void cb('RPC_NOT_READY'); }
rpc.clearOwnedChannel(channel, cb);
};
common.uploadComplete = function (cb) {
if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); }
if (!pinsReady()) { return void cb('RPC_NOT_READY'); }
rpc.uploadComplete(cb);
};
common.uploadStatus = function (size, cb) {
if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); }
if (!pinsReady()) { return void cb('RPC_NOT_READY'); }
rpc.uploadStatus(size, cb);
};
common.uploadCancel = function (cb) {
if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); }
if (!pinsReady()) { return void cb('RPC_NOT_READY'); }
rpc.uploadCancel(cb);
};
/* Create a usage bar which keeps track of how much storage space is used
by your CryptDrive. The getPinnedUsage RPC is one of the heavier calls,
so we throttle its usage. Clients will not update more than once per
LIMIT_REFRESH_RATE. It will be update at least once every three such intervals
If changes are made to your drive in the interim, they will trigger an
update.
*/
var LIMIT_REFRESH_RATE = 30000; // milliseconds
common.createUsageBar = function (cb) {
// getPinnedUsage updates common.account.usage, and other values
// so we can just use those and only check for errors
var todo = function (err) {
var $container = $('<span>', {'class':'limit-container'});
if (err) {
return void window.setTimeout(function () {
common.getPinnedUsage(todo);
}, LIMIT_REFRESH_RATE);
}
var $container = $('<span>', {'class':'limit-container'});
var todo;
var updateUsage = window.updateUsage = common.notAgainForAnother(function () {
common.getPinnedUsage(todo);
}, LIMIT_REFRESH_RATE);
todo = function (err) {
if (err) { return void console.error(err); }
$container.html('');
var unit = Util.magnitudeOfBytes(common.account.limit);
var usage = unit === 'GB'? Util.bytesToGigabytes(common.account.usage):
@ -842,30 +995,24 @@ define([
var $limit = $('<span>', {'class': 'cryptpad-limit-bar'}).appendTo($container);
var quota = usage/limit;
var width = Math.floor(Math.min(quota, 1)*200); // the bar is 200px width
var $usage = $('<span>', {'class': 'usage'}).css('width', width+'px');
var $usage = $('<span>', {'class': 'usage'}).css('width', quota*100+'%');
var makeDonateButton = function () {
var $upgradeLink = $('<a>', {
$('<a>', {
'class': 'upgrade btn btn-success',
href: common.donateURL,
rel: "noreferrer noopener",
target: "_blank",
}).appendTo($container);
$('<button>', {
'class': 'upgrade buttonSuccess',
}).text(Messages.supportCryptpad).appendTo($upgradeLink);
}).text(Messages.supportCryptpad).appendTo($container);
};
var makeUpgradeButton = function () {
var $upgradeLink = $('<a>', {
$('<a>', {
'class': 'upgrade btn btn-success',
href: common.upgradeURL,
rel: "noreferrer noopener",
target: "_blank",
}).appendTo($container);
$('<button>', {
'class': 'upgrade buttonSuccess',
title: Messages.upgradeTitle
}).text(Messages.upgradeAccount).appendTo($upgradeLink);
}).text(Messages.upgradeAccount).appendTo($container);
};
if (!Config.removeDonateButton) {
@ -898,26 +1045,25 @@ define([
var $text = $('<span>', {'class': 'usageText'});
$text.text(usage + ' / ' + prettyLimit);
$limit.append($usage).append($text);
window.setTimeout(function () {
common.getPinnedUsage(todo);
}, LIMIT_REFRESH_RATE);
cb(err, $container);
};
common.getPinnedUsage(todo);
};
var getAppSuffix = function () {
var parts = window.location.pathname.split('/')
.filter(function (x) { return x; });
setInterval(function () {
updateUsage();
}, LIMIT_REFRESH_RATE * 3);
if (!parts[0]) { return ''; }
return '_' + parts[0].toUpperCase();
updateUsage();
getProxy().on('change', ['drive'], function () {
updateUsage();
});
cb(null, $container);
};
var prepareFeedback = common.prepareFeedback = function (key) {
if (typeof(key) !== 'string') { return $.noop; }
var type = common.getAppType();
return function () {
feedback(key.toUpperCase() + getAppSuffix());
feedback((key + (type? '_' + type: '')).toUpperCase());
};
};
@ -927,8 +1073,9 @@ define([
switch (type) {
case 'export':
button = $('<button>', {
'class': 'fa fa-download',
title: Messages.exportButtonTitle,
}).append($('<span>', {'class':'fa fa-download', style: 'font:'+size+' FontAwesome'}));
}).append($('<span>', {'class': 'drawer'}).text(Messages.exportButton));
button.click(prepareFeedback(type));
if (callback) {
@ -937,8 +1084,9 @@ define([
break;
case 'import':
button = $('<button>', {
'class': 'fa fa-upload',
title: Messages.importButtonTitle,
}).append($('<span>', {'class':'fa fa-upload', style: 'font:'+size+' FontAwesome'}));
}).append($('<span>', {'class': 'drawer'}).text(Messages.importButton));
if (callback) {
button
.click(prepareFeedback(type))
@ -961,9 +1109,14 @@ define([
var ev = {
target: data.target
};
if (data.filter && !data.filter(file)) {
common.log('TODO: invalid avatar (type or size)');
return;
}
data.FM.handleFile(file, ev);
if (callback) { callback(); }
});
if (data.accept) { $input.attr('accept', data.accept); }
button.click(function () { $input.click(); });
break;
case 'template':
@ -1085,17 +1238,23 @@ define([
}
button = $('<button>', {
title: Messages.historyButton,
'class': "fa fa-history",
style: 'font:'+size+' FontAwesome'
});
'class': "fa fa-history history",
}).append($('<span>', {'class': 'drawer'}).text(Messages.historyText));
if (data.histConfig) {
button
.click(prepareFeedback(type))
.click(function () {
.on('click', function () {
common.getHistory(data.histConfig);
});
}
break;
case 'more':
button = $('<button>', {
title: Messages.moreActions || 'TODO',
'class': "drawer-button fa fa-ellipsis-h",
style: 'font:'+size+' FontAwesome'
});
break;
default:
button = $('<button>', {
'class': "fa fa-question",
@ -1109,6 +1268,139 @@ define([
return button;
};
var emoji_patt = /([\uD800-\uDBFF][\uDC00-\uDFFF])/;
var isEmoji = function (str) {
return emoji_patt.test(str);
};
var emojiStringToArray = function (str) {
var split = str.split(emoji_patt);
var arr = [];
for (var i=0; i<split.length; i++) {
var char = split[i];
if (char !== "") {
arr.push(char);
}
}
return arr;
};
var getFirstEmojiOrCharacter = function (str) {
if (!str || !str.trim()) { return '?'; }
var emojis = emojiStringToArray(str);
return isEmoji(emojis[0])? emojis[0]: str[0];
};
$(window.document).on('decryption', function (e) {
var decrypted = e.originalEvent;
if (decrypted.callback) {
var cb = decrypted.callback;
cb(function (mediaObject) {
if (mediaObject.type !== 'download') { return; }
var root = mediaObject.rootElement;
if (!root) { return; }
var metadata = decrypted.metadata;
var title = '';
var size = 0;
if (metadata && metadata.name) {
title = metadata.name;
}
if (decrypted.blob) {
size = decrypted.blob.size;
}
var sizeMb = common.bytesToMegabytes(size);
var $btn = $(root).find('button');
$btn.addClass('btn btn-success')
.attr('type', 'download')
.html(function () {
var text = Messages.download_mt_button + '<br>';
if (title) {
text += '<b>' + common.fixHTML(title) + '</b><br>';
}
if (size) {
text += '<em>' + Messages._getKey('formattedMB', [sizeMb]) + '</em>';
}
return text;
});
});
}
});
common.avatarAllowedTypes = [
'image/png',
'image/jpeg',
'image/jpg',
'image/gif',
];
common.displayAvatar = function ($container, href, name, cb) {
var MutationObserver = window.MutationObserver;
var displayDefault = function () {
var text = getFirstEmojiOrCharacter(name);
var $avatar = $('<span>', {'class': 'default'}).text(text);
$container.append($avatar);
if (cb) { cb(); }
};
if (!href) { return void displayDefault(); }
var parsed = common.parsePadUrl(href);
var secret = common.getSecrets('file', parsed.hash);
if (secret.keys && secret.channel) {
var cryptKey = secret.keys && secret.keys.fileKeyStr;
var hexFileName = common.base64ToHex(secret.channel);
var src = common.getBlobPathFromHex(hexFileName);
common.getFileSize(href, function (e, data) {
if (e) {
displayDefault();
return void console.error(e);
}
if (typeof data !== "number") { return void displayDefault(); }
if (common.bytesToMegabytes(data) > 0.5) { return void displayDefault(); }
var $img = $('<media-tag>').appendTo($container);
$img.attr('src', src);
$img.attr('data-crypto-key', 'cryptpad:' + cryptKey);
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'childList' && mutation.addedNodes.length) {
if (mutation.addedNodes.length > 1 ||
mutation.addedNodes[0].nodeName !== 'IMG') {
$img.remove();
return void displayDefault();
}
var $image = $img.find('img');
var onLoad = function () {
var img = new Image();
img.onload = function () {
var w = img.width;
var h = img.height;
if (w>h) {
$image.css('max-height', '100%');
$img.css('flex-direction', 'column');
if (cb) { cb($img); }
return;
}
$image.css('max-width', '100%');
$img.css('flex-direction', 'row');
if (cb) { cb($img); }
};
img.src = $image.attr('src');
};
if ($image[0].complete) { onLoad(); }
$image.on('load', onLoad);
}
});
});
observer.observe($img[0], {
attributes: false,
childList: true,
characterData: false
});
MediaTag($img[0]);
});
}
};
// Create a button with a dropdown menu
// input is a config object with parameters:
// - container (optional): the dropdown container (span)
@ -1143,9 +1435,9 @@ define([
var $button = $('<button>', {
'class': ''
}).append($('<span>', {'class': 'buttonTitle'}).html(config.text || ""));
$('<span>', {
/*$('<span>', {
'class': 'fa fa-caret-down',
}).appendTo($button);
}).appendTo($button);*/
// Menu
var $innerblock = $('<div>', {'class': 'cryptpad-dropdown dropdown-bar-content'});
@ -1184,9 +1476,10 @@ define([
setActive($val);
$innerblock.scrollTop($val.position().top + $innerblock.scrollTop());
}
if (config.feedback && store) { common.feedback(config.feedback); }
};
$button.click(function (e) {
$container.click(function (e) {
e.stopPropagation();
var state = $innerblock.is(':visible');
$('.dropdown-bar-content').hide();
@ -1339,6 +1632,13 @@ define([
content: Messages.user_rename
});
}
if (account) {
options.push({
tag: 'a',
attributes: {'class': 'profile'},
content: Messages.profileButton
});
}
if (parsed && (!parsed.type || parsed.type !== 'settings')) {
options.push({
tag: 'a',
@ -1365,23 +1665,44 @@ define([
content: Messages.login_register
});
}
var $icon = $('<span>', {'class': 'fa fa-user'});
var $userbig = $('<span>', {'class': 'big'}).append($displayedName.clone());
var $userButton = $('<div>').append($icon).append($userbig);
if (account && config.displayNameCls) {
var $icon = $('<span>', {'class': 'fa fa-user-secret'});
//var $userbig = $('<span>', {'class': 'big'}).append($displayedName.clone());
var $userButton = $('<div>').append($icon);//.append($userbig);
if (account) {
$userButton = $('<div>').append(accountName);
}
/*if (account && config.displayNameCls) {
$userbig.append($('<span>', {'class': 'account-name'}).text('(' + accountName + ')'));
} else if (account) {
// If no display name, do not display the parentheses
$userbig.append($('<span>', {'class': 'account-name'}).text(accountName));
}
}*/
var dropdownConfigUser = {
text: $userButton.html(), // Button initial text
options: options, // Entries displayed in the menu
left: true, // Open to the left of the button
container: config.$initBlock // optional
container: config.$initBlock, // optional
feedback: "USER_ADMIN",
};
var $userAdmin = createDropdown(dropdownConfigUser);
if (account && !config.static && store) {
var $avatar = $userAdmin.find('.buttonTitle');
var updateButton = function (newName) {
var profile = store.getProfile();
var url = profile && profile.avatar;
$avatar.html('');
common.displayAvatar($avatar, url, newName, function ($img) {
if ($img) {
$userAdmin.find('button').addClass('avatar');
}
});
};
common.onDisplayNameChanged(updateButton);
updateButton(common.getDisplayName());
}
$userAdmin.find('a.logout').click(function () {
common.logout();
window.location.href = '/';
@ -1393,6 +1714,13 @@ define([
window.location.href = '/settings/';
}
});
$userAdmin.find('a.profile').click(function () {
if (parsed && parsed.type) {
window.open('/profile/');
} else {
window.location.href = '/profile/';
}
});
$userAdmin.find('a.login').click(function () {
if (window.location.pathname !== "/") {
sessionStorage.redirectTo = window.location.href;
@ -1466,9 +1794,15 @@ define([
Store.ready(function (err, storeObj) {
store = common.store = env.store = storeObj;
common.addDirectMessageHandler(common);
var proxy = getProxy();
var network = getNetwork();
if (Object.keys(proxy).length === 1) {
feedback("FIRST_APP_USE", true);
}
if (typeof(window.Proxy) === 'undefined') {
feedback("NO_PROXIES");
}
@ -1477,6 +1811,9 @@ define([
feedback("NO_ISARRAY");
}
common.reportScreenDimensions();
common.reportLanguage();
$(function() {
// Race condition : if document.body is undefined when alertify.js is loaded, Alertify
// won't work. We have to reset it now to make sure it uses a correct "body"
@ -1526,7 +1863,10 @@ define([
common.arePinsSynced(function (err, yes) {
if (!yes) {
common.resetPins(function (err) {
if (err) { console.error(err); }
if (err) {
console.error("Pin Reset Error");
return console.error(err);
}
console.log('RESET DONE');
});
}
@ -1538,6 +1878,21 @@ define([
console.log('pinning disabled');
}
block++;
require([
'/common/rpc.js',
], function (Rpc) {
Rpc.createAnonymous(network, function (e, call) {
if (e) {
console.error(e);
return void cb();
}
anon_rpc = common.anon_rpc = env.anon_rpc = call;
cb();
});
});
// Everything's ready, continue...
if($('#pad-iframe').length) {
block++;

@ -0,0 +1,51 @@
define([
'/common/curve.js',
'/bower_components/chainpad-listmap/chainpad-listmap.js',
], function (Curve, Listmap) {
var Edit = {};
Edit.create = function (config, cb) { //network, channel, theirs, mine, cb) {
var network = config.network;
var channel = config.channel;
var keys = config.keys;
try {
var encryptor = Curve.createEncryptor(keys);
var lm = Listmap.create({
network: network,
data: {},
channel: channel,
readOnly: false,
validateKey: keys.validateKey || undefined,
crypto: encryptor,
userName: 'lol',
logLevel: 1,
});
var done = function () {
// TODO make this abort and disconnect the session after the
// user has finished making changes to the object, and they
// have propagated.
};
lm.proxy
.on('create', function () {
console.log('created');
})
.on('ready', function () {
console.log('ready');
cb(lm, done);
})
.on('disconnect', function () {
console.log('disconnected');
})
.on('change', [], function (o, n, p) {
console.log(o, n, p);
});
} catch (e) {
console.error(e);
}
};
return Edit;
});

@ -0,0 +1,86 @@
define([
'/bower_components/tweetnacl/nacl-fast.min.js',
], function () {
var Nacl = window.nacl;
var Curve = {};
var concatenateUint8s = function (A) {
var len = 0;
var offset = 0;
A.forEach(function (uints) {
len += uints.length || 0;
});
var c = new Uint8Array(len);
A.forEach(function (x) {
c.set(x, offset);
offset += x.length;
});
return c;
};
var encodeBase64 = Nacl.util.encodeBase64;
var decodeBase64 = Nacl.util.decodeBase64;
var decodeUTF8 = Nacl.util.decodeUTF8;
var encodeUTF8 = Nacl.util.encodeUTF8;
Curve.encrypt = function (message, secret) {
var buffer = decodeUTF8(message);
var nonce = Nacl.randomBytes(24);
var box = Nacl.box.after(buffer, nonce, secret);
return encodeBase64(nonce) + '|' + encodeBase64(box);
};
Curve.decrypt = function (packed, secret) {
var unpacked = packed.split('|');
var nonce = decodeBase64(unpacked[0]);
var box = decodeBase64(unpacked[1]);
var message = Nacl.box.open.after(box, nonce, secret);
return encodeUTF8(message);
};
Curve.signAndEncrypt = function (msg, cryptKey, signKey) {
var packed = Curve.encrypt(msg, cryptKey);
return encodeBase64(Nacl.sign(decodeUTF8(packed), signKey));
};
Curve.openSigned = function (msg, cryptKey /*, validateKey STUBBED*/) {
var content = decodeBase64(msg).subarray(64);
return Curve.decrypt(encodeUTF8(content), cryptKey);
};
Curve.deriveKeys = function (theirs, mine) {
var pub = decodeBase64(theirs);
var secret = decodeBase64(mine);
var sharedSecret = Nacl.box.before(pub, secret);
var salt = decodeUTF8('CryptPad.signingKeyGenerationSalt');
// 64 uint8s
var hash = Nacl.hash(concatenateUint8s([salt, sharedSecret]));
var signKp = Nacl.sign.keyPair.fromSeed(hash.subarray(0, 32));
var cryptKey = hash.subarray(32, 64);
return {
cryptKey: encodeBase64(cryptKey),
signKey: encodeBase64(signKp.secretKey),
validateKey: encodeBase64(signKp.publicKey)
};
};
Curve.createEncryptor = function (keys) {
var cryptKey = decodeBase64(keys.cryptKey);
var signKey = decodeBase64(keys.signKey);
var validateKey = decodeBase64(keys.validateKey);
return {
encrypt: function (msg) {
return Curve.signAndEncrypt(msg, cryptKey, signKey);
},
decrypt: function (packed) {
return Curve.openSigned(packed, cryptKey, validateKey);
}
};
};
return Curve;
});

@ -19,6 +19,8 @@ define([
return Marked(md);
};
var mediaMap = {};
// Tasks list
var checkedTaskItemPtn = /^\s*\[x\]\s*/;
var uncheckedTaskItemPtn = /^\s*\[ \]\s*/;
@ -40,7 +42,14 @@ define([
if (href.slice(0,6) === '/file/') {
var parsed = Cryptpad.parsePadUrl(href);
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel);
var mt = '<media-tag src="/blob/' + hexFileName.slice(0,2) + '/' + hexFileName + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>';
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '">';
if (mediaMap[src]) {
mediaMap[src].forEach(function (n) {
mt += n.outerHTML;
});
}
mt += '</media-tag>';
return mt;
}
var out = '<img src="' + href + '" alt="' + text + '"';
@ -51,19 +60,20 @@ define([
return out;
};
var MutationObserver = window.MutationObserver;
var forbiddenTags = [
'SCRIPT',
'IFRAME',
'OBJECT',
'APPLET',
'VIDEO',
//'VIDEO',
'AUDIO',
];
var unsafeTag = function (info) {
if (info.node && $(info.node).parents('media-tag').length) {
/*if (info.node && $(info.node).parents('media-tag').length) {
// Do not remove elements inside a media-tag
return true;
}
}*/
if (['addAttribute', 'modifyAttribute'].indexOf(info.diff.action) !== -1) {
if (/^on/.test(info.diff.name)) {
console.log("Rejecting forbidden element attribute with name", info.diff.name);
@ -133,7 +143,17 @@ define([
DiffMd.apply = function (newHtml, $content) {
var id = $content.attr('id');
if (!id) { throw new Error("The element must have a valid id"); }
var $div = $('<div>', {id: id}).append(newHtml);
var pattern = /(<media-tag src="([^"]*)" data-crypto-key="([^"]*)">)<\/media-tag>/g;
var newHtmlFixed = newHtml.replace(pattern, function (all, tag, src) {
var mt = tag;
if (mediaMap[src]) {
mediaMap[src].forEach(function (n) {
mt += n.outerHTML;
});
}
return mt + '</media-tag>';
});
var $div = $('<div>', {id: id}).append(newHtmlFixed);
var Dom = domFromHTML($('<div>').append($div).html());
var oldDom = domFromHTML($content[0].outerHTML);
var patch = makeDiff(oldDom, Dom, id);
@ -144,15 +164,25 @@ define([
var $mts = $content.find('media-tag:not(:has(*))');
$mts.each(function (i, el) {
MediaTag(el);
console.log(el.outerHTML);
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'childList') {
//console.log(el.outerHTML);
var list_values = [].slice.call(el.children);
mediaMap[el.getAttribute('src')] = list_values;
}
});
});
observer.observe(el, {
attributes: false,
childList: true,
characterData: false
});
});
}
};
$(window.document).on('decryption', function (e) {
var decrypted = e.originalEvent;
if (decrypted.callback) { decrypted.callback(); }
});
return DiffMd;
});

@ -60,6 +60,9 @@ define([
cb(void 0, res);
};
ret.setPadAttribute = filesOp.setAttribute;
ret.getPadAttribute = filesOp.getAttribute;
ret.getDrive = function (key, cb) {
cb(void 0, storeObj.drive[key]);
};
@ -128,6 +131,10 @@ define([
ret.change = function () {};
ret.getProfile = function () {
return storeObj.profile;
};
return ret;
};
@ -199,7 +206,8 @@ define([
}
// if the user is logged in, but does not have signing keys...
if (Cryptpad.isLoggedIn() && !Cryptpad.hasSigningKeys(proxy)) {
if (Cryptpad.isLoggedIn() && (!Cryptpad.hasSigningKeys(proxy) ||
!Cryptpad.hasCurveKeys(proxy))) {
return void requestLogin();
}
@ -207,8 +215,15 @@ define([
if (typeof(n) !== "string") { return; }
Cryptpad.changeDisplayName(n);
});
proxy.on('change', ['profile'], function () {
// Trigger userlist update when the avatar has changed
Cryptpad.changeDisplayName(proxy[Cryptpad.displayNameKey]);
});
proxy.on('change', ['friends'], function () {
// Trigger userlist update when the avatar has changed
Cryptpad.changeDisplayName(proxy[Cryptpad.displayNameKey]);
});
proxy.on('change', [tokenKey], function () {
console.log('wut');
var localToken = tryParsing(localStorage.getItem(tokenKey));
if (localToken !== proxy[tokenKey]) {
return void requestLogin();

@ -0,0 +1,398 @@
define([], function () {
var Hyperscript;
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var split = require('browser-split')
var ClassList = require('class-list')
require('html-element')
function context () {
var cleanupFuncs = []
function h() {
var args = [].slice.call(arguments), e = null
function item (l) {
var r
function parseClass (string) {
// Our minimal parser doesnt understand escaping CSS special
// characters like `#`. Dont use them. More reading:
// https://mathiasbynens.be/notes/css-escapes .
var m = split(string, /([\.#]?[^\s#.]+)/)
if(/^\.|#/.test(m[1]))
e = document.createElement('div')
forEach(m, function (v) {
var s = v.substring(1,v.length)
if(!v) return
if(!e)
e = document.createElement(v)
else if (v[0] === '.')
ClassList(e).add(s)
else if (v[0] === '#')
e.setAttribute('id', s)
})
}
if(l == null)
;
else if('string' === typeof l) {
if(!e)
parseClass(l)
else
e.appendChild(r = document.createTextNode(l))
}
else if('number' === typeof l
|| 'boolean' === typeof l
|| l instanceof Date
|| l instanceof RegExp ) {
e.appendChild(r = document.createTextNode(l.toString()))
}
//there might be a better way to handle this...
else if (isArray(l))
forEach(l, item)
else if(isNode(l))
e.appendChild(r = l)
else if(l instanceof Text)
e.appendChild(r = l)
else if ('object' === typeof l) {
for (var k in l) {
if (!l.hasOwnProperty(k)) { return; }
if('function' === typeof l[k]) {
if(/^on\w+/.test(k)) {
(function (k, l) { // capture k, l in the closure
if (e.addEventListener){
e.addEventListener(k.substring(2), l[k], false)
cleanupFuncs.push(function(){
e.removeEventListener(k.substring(2), l[k], false)
})
}else{
e.attachEvent(k, l[k])
cleanupFuncs.push(function(){
e.detachEvent(k, l[k])
})
}
})(k, l)
} else {
// observable
e[k] = l[k]()
cleanupFuncs.push(l[k](function (v) {
e[k] = v
}))
}
}
else if(k === 'style') {
if('string' === typeof l[k]) {
e.style.cssText = l[k]
}else{
for (var s in l[k]) (function(s, v) {
if('function' === typeof v) {
// observable
e.style.setProperty(s, v())
cleanupFuncs.push(v(function (val) {
e.style.setProperty(s, val)
}))
} else
e.style.setProperty(s, l[k][s])
})(s, l[k][s])
}
} else if (k.substr(0, 5) === "data-") {
e.setAttribute(k, l[k])
} else {
e[k] = l[k]
}
}
} else if ('function' === typeof l) {
//assume it's an observable!
var v = l()
e.appendChild(r = isNode(v) ? v : document.createTextNode(v))
cleanupFuncs.push(l(function (v) {
if(isNode(v) && r.parentElement)
r.parentElement.replaceChild(v, r), r = v
else
r.textContent = v
}))
}
return r
}
while(args.length)
item(args.shift())
return e
}
h.cleanup = function () {
for (var i = 0; i < cleanupFuncs.length; i++){
cleanupFuncs[i]()
}
cleanupFuncs.length = 0
}
return h
}
var h = module.exports = context()
h.context = context
Hyperscript = h;
function isNode (el) {
return el && el.nodeName && el.nodeType
}
function forEach (arr, fn) {
if (arr.forEach) return arr.forEach(fn)
for (var i = 0; i < arr.length; i++) fn(arr[i], i)
}
function isArray (arr) {
return Object.prototype.toString.call(arr) == '[object Array]'
}
},{"browser-split":2,"class-list":3,"html-element":6}],2:[function(require,module,exports){
/*!
* Cross-Browser Split 1.1.1
* Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
* Available under the MIT License
* ECMAScript compliant, uniform cross-browser split method
*/
/**
* Splits a string into an array of strings using a regex or string separator. Matches of the
* separator are not included in the result array. However, if `separator` is a regex that contains
* capturing groups, backreferences are spliced into the result each time `separator` is matched.
* Fixes browser bugs compared to the native `String.prototype.split` and can be used reliably
* cross-browser.
* @param {String} str String to split.
* @param {RegExp|String} separator Regex or string to use for separating the string.
* @param {Number} [limit] Maximum number of items to include in the result array.
* @returns {Array} Array of substrings.
* @example
*
* // Basic use
* split('a b c d', ' ');
* // -> ['a', 'b', 'c', 'd']
*
* // With limit
* split('a b c d', ' ', 2);
* // -> ['a', 'b']
*
* // Backreferences in result array
* split('..word1 word2..', /([a-z]+)(\d+)/i);
* // -> ['..', 'word', '1', ' ', 'word', '2', '..']
*/
module.exports = (function split(undef) {
var nativeSplit = String.prototype.split,
compliantExecNpcg = /()??/.exec("")[1] === undef,
// NPCG: nonparticipating capturing group
self;
self = function(str, separator, limit) {
// If `separator` is not a regex, use `nativeSplit`
if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
return nativeSplit.call(str, separator, limit);
}
var output = [],
flags = (separator.ignoreCase ? "i" : "") + (separator.multiline ? "m" : "") + (separator.extended ? "x" : "") + // Proposed for ES6
(separator.sticky ? "y" : ""),
// Firefox 3+
lastLastIndex = 0,
// Make `global` and avoid `lastIndex` issues by working with a copy
separator = new RegExp(separator.source, flags + "g"),
separator2, match, lastIndex, lastLength;
str += ""; // Type-convert
if (!compliantExecNpcg) {
// Doesn't need flags gy, but they don't hurt
separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
}
/* Values for `limit`, per the spec:
* If undefined: 4294967295 // Math.pow(2, 32) - 1
* If 0, Infinity, or NaN: 0
* If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
* If negative number: 4294967296 - Math.floor(Math.abs(limit))
* If other: Type-convert, then use the above rules
*/
limit = limit === undef ? -1 >>> 0 : // Math.pow(2, 32) - 1
limit >>> 0; // ToUint32(limit)
while (match = separator.exec(str)) {
// `separator.lastIndex` is not reliable cross-browser
lastIndex = match.index + match[0].length;
if (lastIndex > lastLastIndex) {
output.push(str.slice(lastLastIndex, match.index));
// Fix browsers whose `exec` methods don't consistently return `undefined` for
// nonparticipating capturing groups
if (!compliantExecNpcg && match.length > 1) {
match[0].replace(separator2, function() {
for (var i = 1; i < arguments.length - 2; i++) {
if (arguments[i] === undef) {
match[i] = undef;
}
}
});
}
if (match.length > 1 && match.index < str.length) {
Array.prototype.push.apply(output, match.slice(1));
}
lastLength = match[0].length;
lastLastIndex = lastIndex;
if (output.length >= limit) {
break;
}
}
if (separator.lastIndex === match.index) {
separator.lastIndex++; // Avoid an infinite loop
}
}
if (lastLastIndex === str.length) {
if (lastLength || !separator.test("")) {
output.push("");
}
} else {
output.push(str.slice(lastLastIndex));
}
return output.length > limit ? output.slice(0, limit) : output;
};
return self;
})();
},{}],3:[function(require,module,exports){
// contains, add, remove, toggle
var indexof = require('indexof')
module.exports = ClassList
function ClassList(elem) {
var cl = elem.classList
if (cl) {
return cl
}
var classList = {
add: add
, remove: remove
, contains: contains
, toggle: toggle
, toString: $toString
, length: 0
, item: item
}
return classList
function add(token) {
var list = getTokens()
if (indexof(list, token) > -1) {
return
}
list.push(token)
setTokens(list)
}
function remove(token) {
var list = getTokens()
, index = indexof(list, token)
if (index === -1) {
return
}
list.splice(index, 1)
setTokens(list)
}
function contains(token) {
return indexof(getTokens(), token) > -1
}
function toggle(token) {
if (contains(token)) {
remove(token)
return false
} else {
add(token)
return true
}
}
function $toString() {
return elem.className
}
function item(index) {
var tokens = getTokens()
return tokens[index] || null
}
function getTokens() {
var className = elem.className
return filter(className.split(" "), isTruthy)
}
function setTokens(list) {
var length = list.length
elem.className = list.join(" ")
classList.length = length
for (var i = 0; i < list.length; i++) {
classList[i] = list[i]
}
delete list[length]
}
}
function filter (arr, fn) {
var ret = []
for (var i = 0; i < arr.length; i++) {
if (fn(arr[i])) ret.push(arr[i])
}
return ret
}
function isTruthy(value) {
return !!value
}
},{"indexof":4}],4:[function(require,module,exports){
var indexOf = [].indexOf;
module.exports = function(arr, obj){
if (indexOf) return arr.indexOf(obj);
for (var i = 0; i < arr.length; ++i) {
if (arr[i] === obj) return i;
}
return -1;
};
},{}],5:[function(require,module,exports){
var h = require("./index.js");
module.exports = h;
/*
$(function () {
var newDoc = h('p',
h('ul', 'bang bang bang'.split(/\s/).map(function (word) {
return h('li', word);
}))
);
$('body').html(newDoc.outerHTML);
});
*/
},{"./index.js":1}],6:[function(require,module,exports){
},{}]},{},[5]);
return Hyperscript;
});

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

Loading…
Cancel
Save