manual merge of staging

This commit is contained in:
Caleb James DeLisle 2017-08-17 12:14:19 +02:00
commit e04c0b89a1
51 changed files with 1672 additions and 394 deletions

View File

@ -9,3 +9,5 @@ www/scratch
www/common/toolbar.js
www/common/hyperscript.js
www/common/tippy.min.js
www/pad/wysiwygarea-plugin.js

View File

@ -81,23 +81,6 @@ module.exports = {
*/
websocketPath: '/cryptpad_websocket',
/* it is assumed that your websocket will bind to the same port as http
* you can override this behaviour by supplying a number via websocketPort
*/
//websocketPort: 3000,
/* if you want to run a different version of CryptPad but using the same websocket
* server, you should use the other server port as websocketPort and disable
* the websockets on that server
*/
//useExternalWebsocket: false,
/* If CryptPad is proxied without using https, the server needs to know.
* Specify 'useSecureWebsockets: true' so that it can send
* Content Security Policy Headers that prevent http and https from mixing
*/
useSecureWebsockets: false,
/* CryptPad can log activity to stdout
* This may be useful for debugging
*/
@ -237,14 +220,6 @@ module.exports = {
*/
suppressRPCErrors: false,
/* WARNING: EXPERIMENTAL
*
* CryptPad features experimental support for encrypted file upload.
* Our encryption format is still liable to change. As such, we do not
* guarantee that files uploaded now will be supported in the future
*/
/* Setting this value to anything other than true will cause file upload
* attempts to be rejected outright.
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

14
customize.dist/code.svg Normal file
View File

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

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -30,6 +30,9 @@ define([
return;
}
$main.find('a[href="/drive/"] div.pad-button-text h4')
.text(Messages.main_yourCryptDrive);
var name = localStorage[Cryptpad.userNameKey] || sessionStorage[Cryptpad.userNameKey];
var $loggedInBlock = $main.find('#loggedIn');
var $hello = $loggedInBlock.find('#loggedInHello');

View File

@ -46,7 +46,9 @@ define([
h('div.container', [
h('div.row', [
footerCol(null, [
setHTML(h('div.cp-bio-foot'), '<p>With CryptPad, you can make quick collaborative documents for taking notes and writing down ideas together.</p>'),
h('div.cp-bio-foot', [
h('p', Msg.main_footerText)
])
], ''),
/* footerCol(null, [
footLink('/about.html', 'about'),
@ -84,11 +86,11 @@ define([
var username = window.localStorage.getItem('User_name');
if (username === null) {
rightLinks = [
h('a.nav-item.nav-link.cp-login-btn', { href: '/login'}, Msg.login_login),
h('a.nav-item.nav-link.cp-register-btn', { href: '/register'}, Msg.login_register)
h('a.nav-item.nav-link.cp-login-btn', { href: '/login/'}, Msg.login_login),
h('a.nav-item.nav-link.cp-register-btn', { href: '/register/'}, Msg.login_register)
];
} else {
rightLinks = h('a.nav-item.nav-link.cp-user-btn', { href: '/drive' }, [
rightLinks = h('a.nav-item.nav-link.cp-user-btn', { href: '/drive/' }, [
h('i.fa.fa-user-circle'),
" ",
username
@ -121,45 +123,110 @@ define([
]),
]),
h('div.container.cp-container', [
/*h('center', [
h('h1', Msg.about)
]),
setHTML(h('p'), 'CryptPad is created inside of the Research Team at <a href="http://xwiki.com">XWiki SAS</a>, a small business located in Paris France and Iasi Romania. There are 3 core team members working on CryptPad plus a number of contributors both inside and outside of XWiki SAS.'),*/
h('div.row', [
h('h2.col-12', 'Core Developers'),
h('div.col-md-4', [
h('img.bio-avatar', {'src': '/customize/images/aaron.jpg'}),
h('h3', "Aaron MacSween"),
setHTML(h('div#bio'), '<p>Aaron transitioned into distributed systems development from a background in jazz and live stage performance.</p><p>He appreciates the elegance of biological systems and functional programming, and focused on both as a student at the University of Toronto, where he studied cognitive and computer sciences.</p><p>He moved to Paris in 2015 to work as a research engineer at XWiki SAS, after having dedicated significant time to various cryptography-related software projects.</p><p>He spends his spare time experimenting with guitars, photography, science fiction, and spicy food.</p>')
h('div.cp-develop-about.col-12',[
h('div.cp-icon-cent'),
h('h2.text-center', 'Core Developers')
]),
]),
h('div.col-md-4', [
h('img.bio-avatar', {'src': '/customize/images/caleb.jpg'}),
h('div.row.align-items-center', [
h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-bio-avatar', [
h('img.img-fluid', {'src': '/customize/images/CalebJames.jpg'})
]),
h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-profile-det', [
h('h3', "Caleb James Delisle"),
setHTML(h('div#bio'), '<p>Caleb is a cryptography developer, Machine Technology graduate of the Franklin County Technical School and lifelong tinkerer.</p><p>In 2011, he started the cjdns Open Source project to show that secure networking could be invisible and easily deployed.</p><p>After joining XWiki SAS in 2014, he started the CryptPad project with the intent of bringing the same transparent security to collaborative editing.</p><p>He\'s always trying to learn from more experienced colleagues and when someone passes through the Research Team office, his favorite words are "Pull up a chair!".</p>')
]),
h('div.col-md-4', [
h('img.bio-avatar', {'src': '/customize/images/yann.jpg'}),
h('h3', "Yann Flory"),
setHTML(h('div#bio'), '<p>Yann is a mysterious person.</p>')
h('hr'),
setHTML(h('div#bioCaleb'), '<p>Caleb is a cryptography developer, Machine Technology graduate of the Franklin County Technical School and lifelong tinkerer.<br/>In 2011, he started the cjdns Open Source project to show that secure networking could be invisible and easily deployed.<br/>After joining XWiki SAS in 2014, he started the CryptPad project with the intent of bringing the same transparent security to collaborative editing.<br/>He\'s always trying to learn from more experienced colleagues and when someone passes through the Research Team office, his favorite words are "Pull up a chair!".</p>'),
h('a.cp-soc-media', { href : 'https://twitter.com/cjdelisle'}, [
h('i.fa.fa-twitter')
]),
h('a.cp-soc-media', { href : 'https://github.com/cjdelisle'}, [
h('i.fa.fa-github')
])
]),
]),
h('div.row.align-items-center',[
h('div.col-12.col-sm-12.col-md-12.col-lg-6.push-lg-6.cp-bio-avatar.cp-bio-avatar-right', [
h('img.img-fluid', {'src': '/customize/images/AaronMacSween.jpg'})
]),
h('div.col-12.col-sm-12.col-md-12.col-lg-6.pull-lg-6.cp-profile-det',[
h('h3', "Aaron MacSween"),
h('hr'),
setHTML(h('div#bioAaron'), '<p>Aaron transitioned into distributed systems development from a background in jazz and live stage performance. <br/> He appreciates the elegance of biological systems and functional programming, and focused on both as a student at the University of Toronto, where he studied cognitive and computer sciences.<br/>He moved to Paris in 2015 to work as a research engineer at XWiki SAS, after having dedicated significant time to various cryptography-related software projects.<br/>He spends his spare time experimenting with guitars, photography, science fiction, and spicy food.</p>'),
h('a.cp-soc-media', { href : 'https://twitter.com/fc00ansuz'}, [
h('i.fa.fa-twitter')
]),
h('a.cp-soc-media', { href : 'https://github.com/ansuz/'}, [
h('i.fa.fa-github')
])
]),
]),
h('div.row.align-items-center', [
h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-bio-avatar', [
h('img.img-fluid', {'src': '/customize/images/YannFlory.jpg'})
]),
h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-profile-det', [
h('h3', "Yann Flory"),
h('hr'),
setHTML(h('div#bioYann'), '<p>In 2015, Yann graduated with an engineering degree from Ecole Centrale de Lille majoring in Data Science. In his studies he worked on a project to detect defects in optical fiber using image processing technology.<br/>Upon joining XWiki SAS, Yann developed a Wiki page recommendation system, a common API for accessing data server-side and client-side, and an integrated development environment for development of XWiki applications.<br/>Yann is soft spoken but brutally efficient, he is known to say "It will take 5 minutes".</p>'),
h('a.cp-soc-media', { href : 'https://github.com/yflory/'}, [
h('i.fa.fa-github')
])
]),
]),
h('h2', 'Key Contributors'),
h('div.row', [
h('div.col-md-4', [
h('img.bio-avatar', {'src': '/customize/images/Pierre-new.jpg'}),
h('div.cp-develop-about.col-12.cp-contrib',[
h('div.cp-icon-cent'),
h('h2.text-center', 'Key Contributors')
]),
]),
h('div.row.align-items-center', [
h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-bio-avatar', [
h('img.img-fluid', {'src': '/customize/images/Pierre-new.jpg'})
]),
h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-profile-det', [
h('h3', "Pierre Bondoerffer"),
setHTML(h('div#bio'), '<p>Resident CSS wizard and emoji extraordinaire, Pierre is passionate about anything related to technology. He loves to hack around computers and put parts together.</p><p>He is currently studying at 42, where he learns about algorithms, networking, kernel programming and graphics.</p><p>As a part of an internship, he joined XWiki SAS and worked on CryptPad to improve user experience. He also maintains the Spanish translation.</p>')
h('hr'),
setHTML(h('div#bioPierre'), '<p>Resident CSS wizard and emoji extraordinaire, Pierre is passionate about anything related to technology. He loves to hack around computers and put parts together.<br/>He is currently studying at 42, where he learns about algorithms, networking, kernel programming and graphics.<br/>As a part of an internship, he joined XWiki SAS and worked on CryptPad to improve user experience. He also maintains the Spanish translation.</p>'),
h('a.cp-soc-media', { href : 'https://twitter.com/cjdelisle'}, [
h('i.fa.fa-twitter')
]),
h('a.cp-soc-media', { href : 'https://github.com/cjdelisle'}, [
h('i.fa.fa-github')
])
]),
h('div.col-md-4', [
h('img.bio-avatar', {'src': '/customize/images/Catalin.jpg'}),
]),
h('div.row.align-items-center',[
h('div.col-12.col-sm-12.col-md-12.col-lg-6.push-lg-6.cp-bio-avatar.cp-bio-avatar-right', [
h('img.img-fluid', {'src': '/customize/images/Catalin.jpg'})
]),
h('div.col-12.col-sm-12.col-md-12.col-lg-6.pull-lg-6.cp-profile-det',[
h('h3', "Catalin Scripcariu"),
setHTML(h('div#bio'), '<p> Catalin is a Maths majour and has worked in B2B sales for 12 years. Design was always his passion and 3 years ago he started to dedicate himself to web design and front-end.</p><p>At the beginning of 2017 he joined the Xwiki family, where he worked both on the business and the community side of XWiki, including the research team and CryptPad. </p>')
h('hr'),
setHTML(h('div#bioCatalin'), '<p> Catalin is a Maths majour and has worked in B2B sales for 12 years. Design was always his passion and 3 years ago he started to dedicate himself to web design and front-end.<br/>At the beginning of 2017 he joined the XWiki, where he worked both on the business and the community side of XWiki, including the research team and CryptPad. </p>'),
h('a.cp-soc-media', { href : 'https://twitter.com/catalinscr'}, [
h('i.fa.fa-twitter')
]),
h('a.cp-soc-media', { href : 'https://www.linkedin.com/in/catalinscripcariu/'}, [
h('i.fa.fa-linkedin')
])
]),
h('div.col-md-4', [
h('img.bio-avatar', {'src': '/customize/images/ludovic.jpg'}),
]),
h('div.row.align-items-center.cp-margin-bot', [
h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-bio-avatar', [
h('img.img-fluid', {'src': '/customize/images/LudovicDuboist.jpg'})
]),
h('div.col-12.col-sm-12.col-md-12.col-lg-6.cp-profile-det', [
h('h3', "Ludovic Dubost"),
setHTML(h('div#bio'), '<p>A graduate of PolyTech (X90) and Telecom School in Paris, Ludovic Dubost started his career as a software architect for Netscape Communications Europe. He then became CTO of NetValue, one of the first French start-ups that went public. He left NetValue after the company was purchased by Nielsen/NetRatings and in 2004 launched XWiki, the next generation wiki.</p><p>Since the very beginning, Ludovic has been immensely helpful to the CryptPad project. He believed in the idea when there was nothing more than the collaborative pad and his help with sales strategy for the project.</p>')
])
h('hr'),
setHTML(h('div#bioLudovic'), '<p>A graduate of PolyTech (X90) and Telecom School in Paris, Ludovic Dubost started his career as a software architect for Netscape Communications Europe. He then became CTO of NetValue, one of the first French start-ups that went public. He left NetValue after the company was purchased by Nielsen/NetRatings and in 2004 launched XWiki, the next generation wiki.<br/>Since the very beginning, Ludovic has been immensely helpful to the CryptPad project. He believed in the idea when there was nothing more than the collaborative pad and his help with sales strategy for the project.</p>'),
h('a.cp-soc-media', { href : 'https://twitter.com/ldubost'}, [
h('i.fa.fa-twitter')
]),
h('a.cp-soc-media', { href : 'https://github.com/ldubost'}, [
h('i.fa.fa-github')
])
]),
]),
]),
infopageFooter()
@ -280,7 +347,7 @@ define([
{ href: x[1] },
h('div.bs-callout.cp-callout-' + x[0], [
h('i.fa.' + x[3]),
h('div', [ h('h4', x[2]) ])
h('div.pad-button-text', [ h('h4', x[2]) ])
])
]);
}),
@ -325,7 +392,6 @@ define([
])
);
};
loadingScreen = loadingScreen; // TODO use this
Pages['/user/'] = Pages['/user/index.html'] = function () {
return h('div#container');
@ -334,10 +400,18 @@ define([
Pages['/register/'] = Pages['/register/index.html'] = function () {
return [h('div#cp-main', [
infopageTopbar(),
h('div.container-fluid.cp-register-wel',[
h('div.container',[
h('div.row',[
h('div.col-12',[
h('h1.text-center', Msg.register_header)
])
])
])
]),
h('div.container.cp-container', [
h('div.row.align-items-center', [
h('div.row.cp-register-det', [
h('div#data.hidden.col-md-6', [
h('h1', Msg.register_header),
setHTML(h('p.register-explanation'), Msg.register_explanation)
]),
h('div#userForm.form-group.hidden.col-md-6', [
@ -381,10 +455,18 @@ define([
'for': 'accept-terms',
}), Msg.register_acceptTerms),*/
]),
h('button#register.btn.btn-primary', Msg.login_register)
h('button#register.btn.cp-login-register', Msg.login_register)
])
]),
h('div.row.cp-register-test',[
h('hr'),
h('div.col-12', [
setHTML(h('p.test-details'), Msg.register_testimonial),
h('a.cp-test-source.pull-right', { href : 'http://boingboing.net/2016/09/26/cryptpad-a-freeopen-end-to.html'}, Msg.register_testimonial_name)
])
])
]),
infopageFooter(),
])];
};
@ -412,8 +494,8 @@ define([
placeholder: Msg.login_password,
}),
h('div.extra', [
h('button.btn.btn-primary.login.first', Msg.login_login),
h('button#register.btn.btn-success.register', Msg.login_register)
h('button.login.first.btn', Msg.login_login),
h('button#register.btn.register.cp-login-register', Msg.login_register)
])
])
]),

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
</style>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="4" y1="12" x2="20" y2="12">
<stop offset="0" style="stop-color:#4592C4"/>
<stop offset="1" style="stop-color:#545ACD"/>
</linearGradient>
<path class="st0" d="M13,14c-3.4,0-4.5,1.4-4.8,2.2C9.3,16.7,10,17.8,10,19c0,1.7-1.3,3-3,3s-3-1.3-3-3c0-1.3,0.8-2.4,2-2.8V7.8
C4.8,7.4,4,6.3,4,5c0-1.7,1.3-3,3-3s3,1.3,3,3c0,1.3-0.8,2.4-2,2.8v5.3c0.9-0.6,2.2-1.1,4-1.1c2.7,0,3.6-1.3,3.9-2.2
C14.8,9.3,14,8.3,14,7c0-1.7,1.3-3,3-3s3,1.3,3,3c0,1.3-0.9,2.5-2.1,2.9C17.6,11.3,16.7,14,13,14 M7,18c-0.6,0-1,0.4-1,1s0.4,1,1,1
s1-0.4,1-1S7.6,18,7,18 M7,4C6.4,4,6,4.4,6,5s0.4,1,1,1s1-0.4,1-1S7.6,4,7,4 M17,6c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1
S17.6,6,17,6z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -4,6 +4,7 @@
@import "../less2/include/alertify.less";
@import "../less2/include/colortheme.less";
@import "../less2/include/modal.less";
@import "../less2/include/font.less";
@import "./bar.less";
@import "./loading.less";
@import "./dropdown.less";
@ -12,6 +13,8 @@
@toolbar-green: #5cb85c;
.font_open-sans();
.alertify_main();
html.cp, .cp body {

View File

@ -1,7 +1,7 @@
@import "./variables.less";
@import (once) "../less2/include/colortheme.less";
.cp footer {
footer {
background: @category-bg;
font-family: @colortheme_font;
padding-top: 1em;

View File

@ -94,6 +94,7 @@
background-color: @alertify-dialog-bg;
&.half {
width: 50%;
max-width: 50%;
@media (max-width: @browser_media-medium-screen) {
width: 100%;
}
@ -107,9 +108,9 @@
transform: translateY(-50%);
> * {
width: 30%;
min-width: 500px;
max-width: 95%;
width: 100%;
min-width: 300px;
max-width: 500px;
margin: 0 auto;
text-align: left;
padding: @alertify_padding-base;

View File

@ -66,3 +66,5 @@
@cryptpad_color_blue: #4591C4;
@cryptpad_color_grey: #999999;
@cryptpad_header_col: #1E1F1F;
@cryptpad_text_col: #3F4141;

View File

@ -0,0 +1,9 @@
.font_neuropolitical () {
@font-face {
font-family: Neuropolitical;
src: url(./customize/fonts/neuropolitical.ttf)
}
}
.font_open-sans () {
@import (once) '/customize/fonts/open-sans.less';
}

View File

@ -6,22 +6,18 @@
// Basic setup for info pages, this should be used at the global level
.infopages_main () {
background-color: @colortheme_info-background;
a {
color: @cryptpad_color_blue;
&:visited { color: darken(@cryptpad_color_blue, 10%); }
&:visited { color: darken(@cryptpad_color_blue, 5%); }
}
a:hover {
opacity: 1;
}
border: 0;
padding: 0;
margin: 0;
font-size: 16px;
font-family: @colortheme_font;
.cp-container {
font-size: 16px;
padding-top: @infopages_padding;
@ -41,24 +37,19 @@
margin-top: 1em;
margin-bottom: 0.5em;
}
img {
&.left {
float: left;
}
max-width: 100%;
}
.form-group {
& > * {
margin-top: 0.5em;
}
display: flex;
flex-direction: column;
align-items: center;
.checkbox-container {
width: 100%;
display: flex;
@ -72,7 +63,6 @@
}
}
}
footer {
background-color: @cryptpad_color_blue;
.container {
@ -82,7 +72,7 @@
a {
color: #fff;
&:visited {
color: darken(#fff, 10%);
color: darken(#fff, 5%);
};
}
margin-bottom: 1em;
@ -130,12 +120,10 @@
z-index: 9001;
position: fixed;
top: 0;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
width: 100%;
height: @infopages_infobar-height;
@ -144,24 +132,20 @@
vertical-align: middle;
font-size: 1.25em;
line-height: 1.25em;
cursor: default;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
.navbar-nav {
display: flex;
align-items: center;
}
a {
font-weight: 500;
padding: 0.6em;
.infopages_link()
}
.cp-logo {
margin-right: 0.5em;
}

View File

@ -1,8 +1,6 @@
@font-face {
font-family: Neuropolitical;
src: url(./customize/fonts/neuropolitical.ttf)
}
@import (once) '/customize/fonts/open-sans.less';
@import (once) './include/font.less';
.font_neuropolitical();
.font_open-sans();
body.cp-page-index { @import "./pages/page-index.less"; }
body.cp-page-contact { @import "./pages/page-contact.less"; }

View File

@ -3,12 +3,8 @@
.infopages_main();
.infopages_topbar();
.bio-avatar {
border-radius: 50%;
margin: 1em 0;
max-width: 300px;
max-height: 300px;
#cp-main {
background: #fff;
}
.cp-about-intro {
padding-top: 3em;
@ -21,7 +17,7 @@
color: #fff;
font-family: "Open Sans";
h1 {
font-weight: bold;
font-weight: 700;
}
a {
color: #fff;
@ -31,5 +27,89 @@
padding-top: 1em;
}
}
}
}
.cp-container {
.row {
background: #fff;
}
.cp-bio-avatar {
padding-right: 0;
@media (max-width: 991px) {
padding-right: 15px;
}
img {
@media (max-width: 991px) {
margin: 0 auto;
display: block;
}
}
}
.cp-bio-avatar-right {
padding-right: 15px;
padding-left: 0;
@media (max-width: 991px) {
padding-left: 15px;
}
}
}
.cp-develop-about {
.cp-icon-cent {
width: 6rem;
background: #fff;
border-radius: 50%;
height: 6rem;
box-shadow: 0 5px 15px rgba(69,145,196, 0.3);
margin: 0 auto;
background-image: url(/customize/code.svg);
background-repeat: no-repeat;
margin-top: 1em;
margin-bottom: 1.5em;
background-position: 50%;
background-size: 4rem;
}
h2 {
margin-top: 0;
font-weight: 600;
color: #1E1F1F;
margin-bottom: 1.5em;
}
}
.cp-profile-det {
padding-left: 30px;
h3 {
color: #1E1F1F;
font-weight: 700;
}
p {
color: #3F4141;
margin-bottom: 1em;
}
hr {
margin-left: 0;
width: 15rem;
border-top: 2px solid @cryptpad_color_blue;
}
margin-bottom: 1em;
}
.cp-soc-media {
font-size: 1.5em;
color: @cryptpad_color_blue;
padding-right: 1em;
display: inline-block;
&:hover {
transform: scale(1.1);
}
&:visited {
color: @cryptpad_color_blue;
}
}
.cp-contrib {
margin-top: 3em;
.cp-icon-cent {
background-image: url(/customize/source-branch.svg);
background-position: 60%;
}
}
.cp-margin-bot {
margin-bottom: 1.5em;
}

View File

@ -25,6 +25,9 @@
}
}
}
body {
font-family: "Open Sans", Helvetica;
}
.cp-right {
.cp-register-btn {
padding: 0.5em 1em 0.7em 1em;
@ -39,7 +42,7 @@
&:hover {
transform: scale(1.05);
}
}
}
}
.cp-title {
display: flex;
@ -75,7 +78,7 @@
a {
color: #fff;
&:visited {
color: rgba(255,255,255,.8);
color: rgba(255,255,255,.9);
};
}
.nav-link {

View File

@ -17,3 +17,62 @@
}
}
}
.cp-container {
#data {
background: #4591C4;
padding-top: 3em;
padding-bottom: 7em;
padding-left: 30px;
padding-right: 30px;
p {
color: #fff;
}
h2 {
font-weight: 700;
color: @cryptpad_header_col;
}
}
#userForm {
padding-top: 3em;
padding-bottom: 2em;
.form-control {
border-radius: 0;
color: @cryptpad_text_col;
margin-top: 1em;
&:focus {
border-color: @cryptpad_color_blue;
}
}
.checkbox-container {
color: @cryptpad_text_col;
}
}
.align-items-center {
box-shadow: 0 5px 15px rgba(69,145,196, 0.3);
background: #fff;
}
.extra {
margin-top: 1em;
.cp-login-register {
color: @cryptpad_color_blue;
background: #fff;
border: 2px solid @cryptpad_color_blue;
border-radius: 0;
&:hover {
transform: scale(1.05);
}
}
.login {
background: transparent;
color: @cryptpad_color_blue;
padding: 0;
&:hover {
transform: scale(1.05);
}
}
}
}
.cp-container {
padding-top: 0;
min-height: 66vh;
}

View File

@ -19,14 +19,112 @@
#register {
margin-top: 16px;
font-size: 1.25em;
min-width: 30%; // conflict?
width: 30%;
@media (max-width: 500px) {
width: 45%;
}
}
}
padding-bottom: 3em;
min-height: 5vh;
}
.alertify {
// workaround for alertify making empty p
p:empty {
display: none;
}
}
.cp-register-wel {
padding-top: 6em;
padding-bottom: 20em;
background-image: url(/customize/bkregister.jpg);
background-size: cover;
background-repeat: no-repeat;
background-position: center;
h1 {
font-weight: 700;
color: #fff;
text-shadow: 0 1px 5px rgba(0,0,0,.2);
}
}
.cp-register-det {
margin-top: -7em;
background: #fff;
box-shadow: 0 5px 15px rgba(69,145,196, 0.3);
#data {
background: #4591C4; /* fallback for old browsers */
background: -webkit-linear-gradient(to right, #FF7C4F, #4592C4); /* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to right, #FF7C4F, #4592C4); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
padding-top: 3em;
padding-bottom: 7em;
padding-left: 30px;
padding-right: 30px;
p {
color: #fff;
li {
margin-bottom: 1em;
}
.fa {
font-size: 1.5em;
padding-right: 10px;
color: #000;
}
}
h3 {
font-weight: 700;
margin-bottom: 1em;
}
}
#userForm {
padding-top: 3em;
padding-bottom: 2em;
.form-control {
border-radius: 0;
color: @cryptpad_text_col;
margin-top: 1em;
&:focus {
border-color: @cryptpad_color_blue;
}
}
.checkbox-container {
color: @cryptpad_text_col;
}
}
.cp-login-register {
color: @cryptpad_color_blue;
background: #fff;
border: 2px solid @cryptpad_color_blue;
border-radius: 0;
&:hover {
transform: scale(1.05);
}
}
}
.cp-register-test {
margin-top: 3em;
hr {
width: 15rem;
border-top: 2px solid @cryptpad_color_blue;
margin-top: 0;
margin-bottom: 2em;
}
p {
margin-bottom: 0;
}
.cp-test-source {
font-style: italic;
}
.test-details {
padding-left: 4em;
background-image: url(/customize/testimonial.svg);
background-repeat: no-repeat;
background-position: left top;
background-size: 3em;
color: @cryptpad_text_col;
}
}
#cp-main {
background: #fff;
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 26 25" style="enable-background:new 0 0 26 25;" xml:space="preserve">
<style type="text/css">
.st0{fill:#4591C4;}
</style>
<title>testimonial</title>
<g id="Layer_2">
<g id="_25_Quote_Quotation_Text_Sample">
<path class="st0" d="M8.5,12l-4.1,0c0.5-3.5,2.9-6.5,6.2-7.7l0.2,0c1-0.4,1.6-1.5,1.2-2.6c-0.4-1-1.5-1.6-2.6-1.2l-0.2,0
c-5.3,2-8.8,7.1-8.8,12.7l0,7.4l0,0c-0.1,2,1.4,3.8,3.5,3.9c0.1,0,0.3,0,0.4,0l4,0c2,0.1,3.7-1.5,3.8-3.6c0,0,0-0.1,0-0.1l0-5l0,0
C12.1,13.8,10.5,12.1,8.5,12z M8.2,20.6l-3.9,0l0-4.7l3.9,0L8.2,20.6z"/>
<path class="st0" d="M25.5,15.8c0-2.1-1.7-3.8-3.8-3.8c0,0-0.1,0-0.1,0l-3.8,0c0.5-3.5,2.9-6.5,6.2-7.7h0.2c1-0.4,1.6-1.5,1.2-2.6
S24,0.2,22.9,0.5l-0.2,0c-5.3,2-8.8,7.1-8.8,12.7l0,7.6c0.1,2,1.7,3.6,3.7,3.7l4.1,0c2,0.1,3.8-1.4,3.9-3.5c0-0.1,0-0.3,0-0.4
L25.5,15.8L25.5,15.8z M21.7,20.7l-3.9,0l0-4.8l3.9,0l0,4.6V20.7z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -305,7 +305,7 @@ define(function () {
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.driveReadmeTitle = "¿Qué es CryptPad?";
out.readme_welcome = "¡Bienvenido a CryptPad!";
out.readme_p1 = "Bienvenido a CryptPad, aquí podrás anotar cosas solo o con otra gente.";
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.";
@ -557,6 +557,27 @@ define(function () {
// 1.13.0 - Naiad
out.topbar_whatIsCryptpad = "Qué es CryptPad";
out.header_homeTitle = "Volver a la página de inicio";
out.userListButton = "Lista de usuarios";
out.userAccountButton = "Tu cuenta";
out.canvas_saveToDrive = "Guardar esta imagen como archivo en tu CryptDrive";
out.canvas_currentBrush = "Pincel actual";
out.canvas_chooseColor = "Eligir un color";
out.fm_viewListButton = "Lista";
out.fm_viewGridButton = "Cuadrícula";
out.settings_cat_code = "Código";
out.settings_codeIndentation = "Indentación del editor de código (espacios)";
out.settings_codeUseTabs = "Utilizar tabulaciones en vez de espacios";
out.pad_showToolbar = "Mostrar la barra de herramientas";
out.pad_hideToolbar = "Esconder la barra de herramientas";
out.main_catch_phrase = "El Cloud Zero Knowledge";
out.main_richTextPad = "Pad de Texto Enriquecido";
out.main_codePad = "Pad de Código";
out.main_slidePad = "Presentación Markdown";
out.main_pollPad = "Encuesta";
out.main_whiteboardPad = "Pizarra";
out.main_localPads = "Pad Locales";
out.main_yourCryptDrive = "Tu CryptDrive";
return out;
});

View File

@ -116,6 +116,10 @@ define(function () {
out.shareButton = 'Partager';
out.shareSuccess = 'Lien copié dans le presse-papiers';
out.userListButton = "Liste d'utilisateurs";
out.userAccountButton = "Votre compte";
out.newButton = 'Nouveau';
out.newButtonTitle = 'Créer un nouveau pad';
out.uploadButton = 'Importer des fichiers';
@ -240,10 +244,13 @@ define(function () {
out.canvas_delete = "Supprimer la sélection";
out.canvas_disable = "Désactiver le dessin";
out.canvas_enable = "Activer le dessin";
out.canvas_width = "Épaisseur";
out.canvas_width = "Taille";
out.canvas_opacity = "Opacité";
out.canvas_opacityLabel = "opacité: {0}";
out.canvas_widthLabel = "taille: {0}";
out.canvas_opacityLabel = "Opacité: {0}";
out.canvas_widthLabel = "Taille: {0}";
out.canvas_saveToDrive = "Sauvegarder cette image en tant que fichier dans CryptDrive";
out.canvas_currentBrush = "Pinceau actuel";
out.canvas_chooseColor = "Choisir une couleur";
// Profile
out.profileButton = "Profil"; // dropdown menu
@ -257,6 +264,9 @@ define(function () {
out.profile_description = "Description";
out.profile_fieldSaved = 'Nouvelle valeur enregistrée: {0}';
out.profile_inviteButton = "Inviter";
out.profile_inviteButtonTitle = 'Créer un lien pour inviter cet utilisateur à se connecter avec vous.';
out.profile_inviteExplanation = "Cliquer sur <strong>OK</strong> créera un lien vers une session de messagerie sécurisée <em>uniquement accessible par {0}.</em><br><br>Le lien peut être copié et partagé de manière publique.";
out.profile_viewMyProfile = "Voir mon profil";
// contacts/userlist
@ -338,6 +348,8 @@ define(function () {
out.fm_backup_title = 'Lien de secours';
out.fm_nameFile = 'Comment souhaitez-vous nommer ce fichier ?';
out.fm_error_cantPin = "Erreur interne du serveur. Veuillez recharger la page et essayer de nouveau.";
out.fm_viewListButton = "Liste";
out.fm_viewGridButton = "Grille";
// File - Context menu
out.fc_newfolder = "Nouveau dossier";
out.fc_rename = "Renommer";
@ -383,8 +395,6 @@ define(function () {
out.login_invalPass = 'Mot de passe requis';
out.login_unhandledError = "Une erreur inattendue s'est produite :(";
out.login_notRegistered = 'Pas encore inscrit ?';
out.register_importRecent = "Importer l'historique (Recommendé)";
out.register_acceptTerms = "J'accepte <a href='/terms.html' tabindex='-1'>les conditions d'utilisation</a>";
out.register_passwordsDontMatch = "Les mots de passe doivent être identiques!";
@ -403,11 +413,12 @@ define(function () {
"<li>Vous pouvez importer les pads récents de ce navigateur pour les avoir dans votre compte utilisateur.</li>",
"<li>Si vous utilisez un ordinateur partagé, vous devez vous déconnecter avant de partir, fermer l'onglet n'est pas suffisant.</li>",
"</ul>"
];
].join('');
// Settings
out.settings_cat_account = "Compte";
out.settings_cat_drive = "CryptDrive";
out.settings_cat_code = "Code";
out.settings_title = "Préférences";
out.settings_save = "Sauver";
@ -454,6 +465,9 @@ define(function () {
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.settings_codeIndentation = "Indentation dans l'éditeur de code (nombre d'espaces)";
out.settings_codeUseTabs = "Utiliser des tabulations au lieu d'espaces";
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 ?";
@ -473,10 +487,14 @@ define(function () {
out.todo_title = "CryptTodo";
out.todo_newTodoNamePlaceholder = "Décrivez votre tâche...";
out.todo_newTodoNameTitle = "Ajouter cette tâche à votre liste";
out.todo_markAsCompleteTitle = "Marquer tâche comme terminée";
out.todo_markAsIncompleteTitle = "Marquer tâche comme non incomplète";
out.todo_markAsCompleteTitle = "Marquer cette tâche comme terminée";
out.todo_markAsIncompleteTitle = "Marquer cette tâche comme incomplète";
out.todo_removeTaskTitle = "Enlever cette tâche de votre liste";
// pad
out.pad_showToolbar = "Afficher la barre d'outils";
out.pad_hideToolbar = "Cacher la barre d'outils";
// 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>";
@ -488,7 +506,8 @@ define(function () {
//contact.html
out.main_about_p2 = 'Si vous avez des questions ou commentaires, vous pouvez <a href="https://twitter.com/cryptpad"><i class="fa fa-twitter"></i>nous tweeter</a>, ouvrir une issue sur <a href="https://github.com/xwiki-labs/cryptpad/issues/" title="our issue tracker"><i class="fa fa-github"></i>GitHub</a>, venir dire bonjour sur <a href="https://riot.im/app/#/room/#cryptpad:matrix.org" title="Matrix">notre <i class="fa fa-comment"></i>salle Matrix</a> ou IRC (#cryptpad sur irc.freenode.net), ou bien encore <a href="mailto:research@xwiki.com"><i class="fa fa-envelope"></i>nous envoyer un email</a>.';
out.main_info = "<h2>Collaborez avec confiance</h2><br>Développez vos idées en groupe avec des document partagés; la technologie <strong>Zero Knowledge</strong> sécurise vos données.";
out.main_info = "<h2>Collaborez avec confiance</h2><br>Développez vos idées en groupe avec des documents partagés; la technologie <strong>Zero Knowledge</strong> sécurise vos données.";
out.main_catch_phrase = "Le Cloud Zero Knowledge";
out.main_howitworks = 'Comment ça fonctionne';
out.main_zeroKnowledge = 'Zero Knowledge';
@ -510,6 +529,15 @@ define(function () {
out.main_poll_p = 'Plannifiez vos réunions ou évènements, ou votez pour la meilleure solution concernant votre problème.';
out.main_drive = 'CryptDrive';
out.main_richTextPad = 'Pad de Texte Riche';
out.main_codePad = 'Pad de Code';
out.main_slidePad = 'Présentation Markdown';
out.main_pollPad = 'Sondage ou Planning';
out.main_whiteboardPad = 'Tableau blanc';
out.main_localPads = 'Pads Locaux';
out.main_yourCryptDrive = 'Votre CryptDrive';
out.main_footerText = "Avec CryptPad, vous pouvez créer des documents collaboratifs rapidement pour prendre des notes à plusieurs.";
out.footer_applications = "Applications";
out.footer_contact = "Contact";
out.footer_aboutUs = "À propos";
@ -562,6 +590,7 @@ define(function () {
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.updated_0_header_logoTitle = 'Retourner vers votre CryptDrive';
out.header_logoTitle = out.updated_0_header_logoTitle;
out.header_homeTitle = "Aller sur la page d'accueil";
// Initial states
@ -602,7 +631,7 @@ define(function () {
' - La présentation est mise à jour en temps-réel'
].join('');
out.driveReadmeTitle = "Qu'est-ce que CryptDrive ?";
out.driveReadmeTitle = "Qu'est-ce que CryptPad ?";
out.readme_welcome = "Bienvenue dans CryptPad !";
out.readme_p1 = "Bienvenue dans CryptPad, le lieu où vous pouvez prendre des notes seul ou avec des amis.";
out.readme_p2 = "Ce pad va vous donner un aperçu de la manière dont vous pouvez utiliser CryptPad pour prendre des notes, les organiser et travailler en groupe sur celles-ci.";

View File

@ -336,10 +336,10 @@ define(function () {
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 = 'Contains all the files from "Documents", "Unsorted" and "Trash". You can\'t move or remove files from here.'; // Same here
out.fm_info_anonymous = 'You are not logged in so these pads may be deleted (<a href="https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/" target="_blank">find out why</a>). ' +
out.fm_info_anonymous = 'You are not logged in so your pads will expire after 3 months (<a href="https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/" target="_blank">find out more</a>). ' +
'<a href="/register/">Sign up</a> or <a href="/login/">Log in</a> to keep them alive.';
out.fm_alert_backupUrl = "Backup link for this drive.<br>" +
"It is <strong>highly recommended</strong> that you keep ip for yourself only.<br>" +
"It is <strong>highly recommended</strong> that you keep it secret.<br>" +
"You can use it to retrieve all your files in case your browser memory got erased.<br>" +
"Anybody with that link can edit or remove all the files in your file manager.<br>";
out.fm_alert_anonymous = "Hello there, you are currently using CryptPad anonymously, that's ok but your pads may be deleted after a period of " +
@ -404,13 +404,15 @@ define(function () {
out.register_header = "Welcome to 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>",
"<h3>Lets go over a couple things first:</h3>",
"<ul class='list-unstyled'>",
"<li><i class='fa fa-info-circle'> </i> 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><i class='fa fa-info-circle'> </i> You can import pads which were recently viewed in your browser so you have them in your account.</li>",
"<li><i class='fa fa-info-circle'> </i> 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_testimonial =" \"Tools like Etherpad and Google Docs [...] all share a weakness, which is that whomever owns the document server can see everything you're typing. Cryptpad is a free/open project that uses some of the ideas behind blockchain to implement a \"zero-knowledge\" version of a collaborative document editor, ensuring that only the people working on a document can see it.\" ";
out.register_testimonial_name = "Cory Doctorow";
out.register_writtenPassword = "I have written down my username and password, proceed";
out.register_cancel = "Go back";
@ -422,6 +424,7 @@ define(function () {
// Settings
out.settings_cat_account = "Account";
out.settings_cat_drive = "CryptDrive";
out.settings_cat_code = "Code";
out.settings_title = "Settings";
out.settings_save = "Save";
@ -468,6 +471,9 @@ define(function () {
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.settings_codeIndentation = 'Code editor indentation (spaces)';
out.settings_codeUseTabs = "Indent using tabs (instead of spaces)";
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?";
@ -508,7 +514,7 @@ define(function () {
// contact.html
out.main_about_p2 = 'If you have any questions or comments, feel free to reach out! You can <a href="https://twitter.com/cryptpad"><i class="fa fa-twitter"></i>tweet us</a>, open an issue <a href="https://github.com/xwiki-labs/cryptpad/issues/" title="our issue tracker">on <i class="fa fa-github"></i>GitHub</a>. Come say hi on <a href="https://riot.im/app/#/room/#cryptpad:matrix.org" title="Matrix">our <i class="fa fa-comment"></i>Matrix channel</a> or IRC (#cryptpad on irc.freenode.net), or <a href="mailto:research@xwiki.com"><i class="fa fa-envelope"></i>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_info = "<h2>Collaborate in Confidence</h2> Grow your ideas together with shared documents while <strong>Zero Knowledge</strong> technology secures your privacy; <strong>even from us</strong>.";
out.main_catch_phrase = "The Zero Knowledge Cloud";
out.main_howitworks = 'How It Works';
@ -538,6 +544,8 @@ define(function () {
out.main_pollPad = 'Poll or Schedule';
out.main_whiteboardPad = 'Whiteboard';
out.main_localPads = 'Local Pads';
out.main_yourCryptDrive = 'Your CryptDrive';
out.main_footerText = "With CryptPad, you can make quick collaborative documents for taking notes and writing down ideas together.";
out.footer_applications = "Applications";
out.footer_contact = "Contact";
@ -654,7 +662,7 @@ define(function () {
// Readme
out.driveReadmeTitle = "What is CryptDrive?";
out.driveReadmeTitle = "What is CryptPad?";
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.";

View File

@ -522,7 +522,7 @@ define(function () {
// Readme
out.driveReadmeTitle = "What is CryptDrive?";
out.driveReadmeTitle = "What is CryptPad?";
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.";

View File

@ -334,7 +334,7 @@ define(function () {
out.initialState = "<p>Acesta este&nbsp;<strong>CryptPad</strong>, editorul colaborativ bazat pe tehnologia Zero Knowledge în timp real. Totul este salvat pe măsură ce scrii.<br>Partajează link-ul către acest pad pentru a edita cu prieteni sau folosește <span class=\"fa fa-share-alt\" style=\"border:1px solid black;color:#000;\">&nbsp;Share&nbsp;</span> butonul pentru a partaja <em>read-only link</em>&nbsp;permițând vizualizarea dar nu și editarea.</p><p><em>Îndrăznește, începe să scrii...</em></p><p>&nbsp;<br></p>";
out.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?";
out.driveReadmeTitle = "Ce este CryptPad?";
out.readme_welcome = "Bine ai venit n CryptPad !";
out.readme_p1 = "Bine ai venit în CryptPad, acesta este locul unde îți poți lua notițe, singur sau cu prietenii.";
out.readme_p2 = "Acest pad o să îți ofere un scurt ghid în cum poți să folosești CryptPad pentru a lua notițe, a le ține organizate și a colabora pe ele.";

View File

@ -505,7 +505,7 @@ define(function () {
// Readme
out.driveReadmeTitle = "什麼是 CryptDrive?";
out.driveReadmeTitle = "什麼是 CryptPad?";
out.readme_welcome = "歡迎來到 CryptPad !";
out.readme_p1 = "歡迎來到 CryptPad, 這裏你可以獨自作個人筆記或是和別人共享協作。";
out.readme_p2 = "這個工作檔案可以讓你快速地了解如何使用 CryptPad 作筆記,有效地整理管理文件工作檔案。";

View File

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server",
"version": "1.12.0",
"version": "1.13.0",
"dependencies": {
"chainpad-server": "^1.0.1",
"express": "~4.10.1",

View File

@ -32,10 +32,7 @@ var setHeaders = (function () {
if (typeof(config.httpHeaders) !== 'object') { return function () {}; }
const headers = clone(config.httpHeaders);
headers['Access-Control-Allow-Origin'] = "*";
if (config.contentSecurity && false) {
if (config.contentSecurity) {
headers['Content-Security-Policy'] = clone(config.contentSecurity);
if (!/;$/.test(headers['Content-Security-Policy'])) { headers['Content-Security-Policy'] += ';' }
if (headers['Content-Security-Policy'].indexOf('frame-ancestors') === -1) {

View File

@ -30,6 +30,7 @@ body {
min-width: 20%;
max-width: 80%;
resize: horizontal;
font-size: initial;
}
.CodeMirror.fullPage {
//min-width: 100%;
@ -84,7 +85,6 @@ body {
width: 8px;
top: 0;
left: 0;
z-index: 9999;
cursor: col-resize;
}

View File

@ -61,19 +61,28 @@ define([
$iframe.find('.CodeMirror').addClass('fullPage');
editor = CodeMirror.editor;
var setIndentation = APP.setIndentation = function (units) {
var setIndentation = APP.setIndentation = function (units, useTabs) {
if (typeof(units) !== 'number') { return; }
editor.setOption('indentUnit', units);
editor.setOption('tabSize', units);
//editor.setOption('indentWithTabs', true);
editor.setOption('indentWithTabs', useTabs);
};
var indentKey = 'cryptpad.indentUnit';
var useTabsKey = 'cryptpad.indentWithTabs';
var proxy = Cryptpad.getProxy();
proxy.on('change', [indentKey], function (o, n) {
APP.setIndentation(n);
});
setIndentation(proxy[indentKey]);
var updateIndentSettings = function () {
var indentUnit = proxy[indentKey];
var useTabs = proxy[useTabsKey];
setIndentation(
typeof(indentUnit) === 'number'? indentUnit: 2,
typeof(useTabs) === 'boolean'? useTabs: false);
};
proxy.on('change', [indentKey], updateIndentSettings);
proxy.on('change', [useTabsKey], updateIndentSettings);
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
@ -401,6 +410,7 @@ define([
}
});
/*
// add the splitter
if (!$iframe.has('.cp-splitter').length) {
var $preview = $iframe.find('#previewContainer');
@ -428,6 +438,7 @@ define([
});
});
}
*/
Cryptpad.removeLoadingScreen();
setEditable(true);

View File

@ -301,7 +301,16 @@ define([
// Tooltips
UI.clearTooltips = function () {
$('.tippy-popper').remove();
// If an element is removed from the UI while a tooltip is applied on that element, the tooltip will get hung
// forever, this is a solution which just searches for tooltips which have no corrisponding element and removes
// them.
var win;
$('.tippy-popper').each(function (i, el) {
win = win || $('#pad-iframe')[0].contentWindow;
if (win.$('[aria-describedby=' + el.getAttribute('id') + ']').length === 0) {
el.remove();
}
});
};
UI.addTooltips = function () {

View File

@ -2,8 +2,42 @@ define([
'jquery',
'/bower_components/chainpad-crypto/crypto.js',
'/common/curve.js',
'/common/common-hash.js',
'/bower_components/marked/marked.min.js',
], function ($, Crypto, Curve, Marked) {
'/common/common-realtime.js',
// displayAvatar
// whenRealtimeSyncs
// getRealtime -> removeFromFriendList
/* UI
Messages
confirm
fixHTML
displayAvatar
clearOwnedChannel
alert
pushMsg
removeFromFriendList
onDirectMessage
getNetwork
getProxy
pushMsg
Init
getNetwork
getProxy
onDirectMessage
removeFromFriendList
notify
onMessage
*/
], function ($, Crypto, Curve, Hash, Marked, Realtime) {
var Msg = {
inputs: [],
};
@ -28,11 +62,10 @@ define([
return Marked(content);
};
var createData = Msg.createData = function (common, hash) {
var proxy = common.getProxy();
var createData = Msg.createData = function (proxy, hash) {
return {
channel: hash || common.createChannelId(),
displayName: proxy[common.displayNameKey],
channel: hash || Hash.createChannelId(),
displayName: proxy['cryptpad.username'],
profile: proxy.profile && proxy.profile.view,
edPublic: proxy.edPublic,
curvePublic: proxy.curvePublic,
@ -40,39 +73,39 @@ define([
};
};
var getFriend = function (common, pubkey) {
var proxy = common.getProxy();
var getFriend = function (proxy, pubkey) {
if (pubkey === proxy.curvePublic) {
var data = createData(common);
var data = createData(proxy);
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 removeFromFriendList = function (proxy, realtime, curvePublic, cb) {
if (!proxy.friends) { return; }
var friends = proxy.friends;
delete friends[curvePublic];
common.whenRealtimeSyncs(common.getRealtime(), cb);
Realtime.whenRealtimeSyncs(realtime, cb);
};
// TODO set this up as an observable data structure
var getFriendList = Msg.getFriendList = function (common) {
var proxy = common.getProxy();
var getFriendList = Msg.getFriendList = function (proxy) {
if (!proxy.friends) { proxy.friends = {}; }
return proxy.friends;
};
Msg.getFriendChannelsList = function (common) {
var friends = getFriendList(common);
var eachFriend = function (friends, cb) {
Object.keys(friends).forEach(function (id) {
if (id === 'me') { return; }
cb(friends[id], id, friends);
});
};
Msg.getFriendChannelsList = function (proxy) {
var list = [];
Object.keys(friends).forEach(function (key) {
if (key === "me") { return; }
list.push(friends[key].channel);
eachFriend(proxy, function (friend) {
list.push(friend.channel);
});
return list;
};
@ -85,131 +118,6 @@ define([
var UI = Msg.UI = {};
// TODO extract into UI method
var createChatBox = function (common, $container, curvePublic, ui) {
var data = getFriend(common, curvePublic);
// Input
var channel = channels[data.channel];
var $header = $('<div>', {
'class': 'header',
}).appendTo($container);
var $avatar = $('<div>', {'class': 'avatar'}).appendTo($header);
// more history...
$('<span>', {
'class': 'more-history',
})
.text('get more history')
.click(function () {
console.log("GETTING HISTORY");
channel.getPreviousMessages();
})
.appendTo($header);
var $removeHistory = $('<span>', {
'class': 'remove-history fa fa-eraser',
title: common.Messages.contacts_removeHistoryTitle
})
.click(function () {
common.confirm(common.Messages.contacts_confirmRemoveHistory, function (yes) {
if (!yes) { return; }
common.clearOwnedChannel(data.channel, function (e) {
if (e) {
console.error(e);
common.alert(common.Messages.contacts_removeHistoryServerError);
return;
}
});
});
});
$removeHistory.appendTo($header);
$('<div>', {'class': 'messages'}).appendTo($container);
var $inputBlock = $('<div>', {'class': 'input'}).appendTo($container);
var $input = $('<textarea>').appendTo($inputBlock);
$input.attr('placeholder', common.Messages.contacts_typeHere);
ui.input = $input[0];
var send = function () {
// TODO implement sending queue
// TODO separate message logic from UI
var channel = channels[data.channel];
if (channel.sending) {
console.error("still sending");
return;
}
if (!$input.val()) {
console.error("nothing to send");
return;
}
if ($input.attr('disabled')) {
console.error("input is disabled");
return;
}
var payload = $input.val();
// Send the message
channel.sending = true;
channel.send(payload, function (e) {
if (e) {
channel.sending = false;
console.error(e);
return;
}
$input.val('');
channel.refresh();
channel.sending = false;
});
};
$('<button>', {
'class': 'btn btn-primary fa fa-paper-plane',
title: common.Messages.contacts_send,
}).appendTo($inputBlock).click(send);
var onKeyDown = function (e) {
if (e.keyCode === 13) {
if (e.ctrlKey || e.shiftKey) {
var val = this.value;
if (typeof this.selectionStart === "number" && typeof this.selectionEnd === "number") {
var start = this.selectionStart;
this.value = val.slice(0, start) + "\n" + val.slice(this.selectionEnd);
this.selectionStart = this.selectionEnd = start + 1;
} else if (document.selection && document.selection.createRange) {
this.focus();
var range = document.selection.createRange();
range.text = "\r\n";
range.collapse(false);
range.select();
}
return false;
}
send();
return false;
}
};
$input.on('keydown', onKeyDown);
// Header
var $rightCol = $('<span>', {'class': 'right-col'});
$('<span>', {'class': 'name'}).text(data.displayName).appendTo($rightCol);
if (data.avatar && avatars[data.avatar]) {
$avatar.append(avatars[data.avatar]);
$avatar.append($rightCol);
} else {
common.displayAvatar($avatar, data.avatar, data.displayName, function ($img) {
if (data.avatar && $img) {
avatars[data.avatar] = $img[0].outerHTML;
}
$avatar.append($rightCol);
});
}
};
UI.init = function (common, $listContainer, $msgContainer) {
var ui = {
containers: {
@ -260,9 +168,8 @@ define([
ui.createFriendList = function (friends, display, remove) {
var $block = ui.containers.friendBlock = $('<div>');
Object.keys(friends).forEach(function (f) {
if (f === 'me') { return; }
ui.addToFriendList(friends[f], display, remove);
eachFriend(friends, function (friend) {
ui.addToFriendList(friend, display, remove);
});
$block.appendTo($listContainer);
};
@ -282,7 +189,8 @@ define([
};
ui.update = function (curvePublic, types) {
var data = getFriend(common, curvePublic);
var proxy = common.getProxy();
var data = getFriend(proxy, curvePublic);
var chan = channels[data.channel];
if (!chan.ready) {
chan.updateOnReady = (chan.updateOnReady || []).concat(types);
@ -311,7 +219,7 @@ define([
ui.updateStatus = function (curvePublic, online) {
ui.getFriend(curvePublic).find('.status')
.attr('class', 'status ' + online? 'online' : 'offline');
.attr('class', 'status ' + (online? 'online' : 'offline'));
};
ui.getChannel = function (curvePublic) {
@ -384,6 +292,129 @@ define([
}
};
ui.createChatBox = function (proxy, $container, curvePublic) {
var data = getFriend(proxy, curvePublic);
// Input
var channel = channels[data.channel];
var $header = $('<div>', {
'class': 'header',
}).appendTo($container);
var $avatar = $('<div>', {'class': 'avatar'}).appendTo($header);
// more history...
$('<span>', {
'class': 'more-history',
})
.text('get more history')
.click(function () {
console.log("GETTING HISTORY");
channel.getPreviousMessages();
})
.appendTo($header);
var $removeHistory = $('<span>', {
'class': 'remove-history fa fa-eraser',
title: common.Messages.contacts_removeHistoryTitle
})
.click(function () {
common.confirm(common.Messages.contacts_confirmRemoveHistory, function (yes) {
if (!yes) { return; }
common.clearOwnedChannel(data.channel, function (e) {
if (e) {
console.error(e);
common.alert(common.Messages.contacts_removeHistoryServerError);
return;
}
});
});
});
$removeHistory.appendTo($header);
$('<div>', {'class': 'messages'}).appendTo($container);
var $inputBlock = $('<div>', {'class': 'input'}).appendTo($container);
var $input = $('<textarea>').appendTo($inputBlock);
$input.attr('placeholder', common.Messages.contacts_typeHere);
ui.input = $input[0];
var send = function () {
// TODO implement sending queue
// TODO separate message logic from UI
var channel = channels[data.channel];
if (channel.sending) {
console.error("still sending");
return;
}
if (!$input.val()) {
console.error("nothing to send");
return;
}
if ($input.attr('disabled')) {
console.error("input is disabled");
return;
}
var payload = $input.val();
// Send the message
channel.sending = true;
channel.send(payload, function (e) {
if (e) {
channel.sending = false;
console.error(e);
return;
}
$input.val('');
channel.refresh();
channel.sending = false;
});
};
$('<button>', {
'class': 'btn btn-primary fa fa-paper-plane',
title: common.Messages.contacts_send,
}).appendTo($inputBlock).click(send);
var onKeyDown = function (e) {
if (e.keyCode === 13) {
if (e.ctrlKey || e.shiftKey) {
var val = this.value;
if (typeof this.selectionStart === "number" && typeof this.selectionEnd === "number") {
var start = this.selectionStart;
this.value = val.slice(0, start) + "\n" + val.slice(this.selectionEnd);
this.selectionStart = this.selectionEnd = start + 1;
} else if (document.selection && document.selection.createRange) {
this.focus();
var range = document.selection.createRange();
range.text = "\r\n";
range.collapse(false);
range.select();
}
return false;
}
send();
return false;
}
};
$input.on('keydown', onKeyDown);
// Header
var $rightCol = $('<span>', {'class': 'right-col'});
$('<span>', {'class': 'name'}).text(data.displayName).appendTo($rightCol);
if (data.avatar && avatars[data.avatar]) {
$avatar.append(avatars[data.avatar]);
$avatar.append($rightCol);
} else {
common.displayAvatar($avatar, data.avatar, data.displayName, function ($img) {
if (data.avatar && $img) {
avatars[data.avatar] = $img[0].outerHTML;
}
$avatar.append($rightCol);
});
}
};
return ui;
};
@ -393,7 +424,8 @@ define([
});
};
var pushMsg = function (common, channel, cryptMsg) {
// TODO remove dependency on common
var pushMsg = function (realtime, proxy, common, channel, cryptMsg) {
var msg = channel.encryptor.decrypt(cryptMsg);
var sig = cryptMsg.slice(0, 64);
@ -413,12 +445,10 @@ define([
channel.messages.push(res);
return true;
}
var proxy;
if (parsedMsg[0] === Types.update) {
proxy = common.getProxy();
if (parsedMsg[1] === common.getProxy().curvePublic) { return; }
if (parsedMsg[1] === proxy.curvePublic) { return; }
var newdata = parsedMsg[3];
var data = getFriend(common, parsedMsg[1]);
var data = getFriend(proxy, parsedMsg[1]);
var types = [];
Object.keys(newdata).forEach(function (k) {
if (data[k] !== newdata[k]) {
@ -430,11 +460,7 @@ define([
return;
}
if (parsedMsg[0] === Types.unfriend) {
proxy = common.getProxy();
// FIXME pushMsg shouldn't need access to common
// implement this as a callback or use some other API
removeFromFriendList(common, channel.friendEd, function () {
removeFromFriendList(proxy, realtime, channel.friendEd, function () {
channel.wc.leave(Types.unfriend);
channel.removeUI();
});
@ -444,10 +470,10 @@ define([
/* Broadcast a display name, profile, or avatar change to all contacts
*/
var updateMyData = function (common) {
var friends = getFriendList(common);
var updateMyData = function (proxy) {
var friends = getFriendList(proxy);
var mySyncData = friends.me;
var myData = createData(common);
var myData = createData(proxy);
if (!mySyncData || mySyncData.displayName !== myData.displayName
|| mySyncData.profile !== myData.profile
|| mySyncData.avatar !== myData.avatar) {
@ -467,20 +493,20 @@ define([
}
};
var onChannelReady = function (common, chanId) {
var onChannelReady = function (proxy, chanId) {
if (ready.indexOf(chanId) !== -1) { return; }
ready.push(chanId);
channels[chanId].updateStatus(); // c'est quoi?
var friends = getFriendList(common);
var friends = getFriendList(proxy);
if (ready.length === Object.keys(friends).length) {
// All channels are ready
updateMyData(common);
updateMyData(proxy);
}
return ready.length;
};
// Id message allows us to map a netfluxId with a public curve key
var onIdMessage = function (common, msg, sender) {
var onIdMessage = function (proxy, network, msg, sender) {
var channel;
var isId = Object.keys(channels).some(function (chanId) {
if (channels[chanId].userList.indexOf(sender) !== -1) {
@ -516,6 +542,9 @@ define([
return;
}
if (parsed[0] !== Types.mapId && parsed[0] !== Types.mapIdAck) { return; }
// check that the responding peer's encrypted netflux id matches
// the sender field. This is to prevent replay attacks.
if (parsed[2] !== sender || !parsed[1]) { return; }
channel.mapId[sender] = parsed[1];
@ -523,15 +552,19 @@ define([
if (parsed[0] !== Types.mapId) { return; } // Don't send your key if it's already an ACK
// Answer with your own key
var proxy = common.getProxy();
var network = common.getNetwork();
var rMsg = [Types.mapIdAck, proxy.curvePublic, channel.wc.myID];
var rMsgStr = JSON.stringify(rMsg);
var cryptMsg = channel.encryptor.encrypt(rMsgStr);
network.sendto(sender, cryptMsg);
};
// HERE
var onDirectMessage = function (common, msg, sender) {
if (sender !== Msg.hk) { return void onIdMessage(common, msg, sender); }
var proxy = common.getProxy();
var network = common.getNetwork();
var realtime = common.getRealtime();
if (sender !== Msg.hk) { return void onIdMessage(proxy, network, msg, sender); }
var parsed = JSON.parse(msg);
if ((parsed.validateKey || parsed.owners) && parsed.channel) {
return;
@ -542,7 +575,7 @@ define([
// 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);
onChannelReady(proxy, parsed.channel);
var updateTypes = channels[parsed.channel].updateOnReady;
if (updateTypes) {
channels[parsed.channel].updateUI(updateTypes);
@ -552,12 +585,16 @@ define([
}
var chan = parsed[3];
if (!chan || !channels[chan]) { return; }
pushMsg(common, channels[chan], parsed[4]);
pushMsg(realtime, proxy, common, channels[chan], parsed[4]);
channels[chan].refresh();
};
var onMessage = function (common, msg, sender, chan) {
if (!channels[chan.id]) { return; }
var isMessage = pushMsg(common, channels[chan.id], msg);
var realtime = common.getRealtime();
var proxy = common.getProxy();
var isMessage = pushMsg(realtime, proxy, common, channels[chan.id], msg);
if (isMessage) {
// Don't notify for your own messages
if (channels[chan.id].wc.myID !== sender) {
@ -602,12 +639,15 @@ define([
});
};
/* TODO remove dependency on common
*/
Msg.init = function (common, ui) {
// declare common variables
var network = common.getNetwork();
var proxy = common.getProxy();
var realtime = common.getRealtime();
Msg.hk = network.historyKeeper;
var friends = getFriendList(common);
var friends = getFriendList(proxy);
// listen for messages...
network.on('message', function(msg, sender) {
@ -645,7 +685,7 @@ define([
for (var i = last + 1; i<messages.length; i++) {
msg = messages[i];
name = (msg.channel !== channel.lastSender)?
getFriend(common, msg.channel).displayName: undefined;
getFriend(proxy, msg.channel).displayName: undefined;
ui.createMessage(msg, name).appendTo($messages);
channel.lastSender = msg.channel;
@ -667,7 +707,7 @@ define([
var $chat = ui.getChannel(curvePublic);
if (!$chat) {
$chat = ui.createChat(curvePublic);
createChatBox(common, $chat, curvePublic, ui);
ui.createChatBox(proxy, $chat, curvePublic);
}
// Show the correct div
ui.hideChat();
@ -682,9 +722,8 @@ define([
// TODO take a callback
var remove = function (curvePublic) {
var data = getFriend(common, curvePublic);
var data = getFriend(proxy, 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);
@ -704,9 +743,7 @@ define([
// Open the channels
// TODO extract this into an external function
var openFriendChannel = function (f) {
if (f === "me") { return; }
var data = friends[f];
var openFriendChannel = function (data, f) {
var keys = Curve.deriveKeys(data.curvePublic, proxy.curvePrivate);
var encryptor = Curve.createEncryptor(keys);
network.join(data.channel).then(function (chan) {
@ -719,13 +756,14 @@ define([
refresh: function () { refresh(data.curvePublic); },
notify: function () {
ui.notify(data.curvePublic);
common.notify();
common.notify(); // HERE
},
unnotify: function () { ui.unnotify(data.curvePublic); },
removeUI: function () { ui.remove(data.curvePublic); },
updateUI: function (types) { ui.update(data.curvePublic, types); },
updateStatus: function () {
ui.updateStatus(data.curvePublic, channel.getStatus(data.curvePublic));
ui.updateStatus(data.curvePublic,
channel.getStatus(data.curvePublic));
},
setLastMessageRead: function (hash) {
data.lastKnownHash = hash;
@ -774,7 +812,7 @@ define([
var cryptMsg = channel.encryptor.encrypt(msgStr);
channel.wc.bcast(cryptMsg).then(function () {
pushMsg(common, channel, cryptMsg);
pushMsg(realtime, proxy, common, channel, cryptMsg);
cb();
}, function (err) {
cb(err);
@ -823,7 +861,7 @@ define([
};
var openFriendChannels = messenger.openFriendChannels = function () {
Object.keys(friends).forEach(openFriendChannel);
eachFriend(friends, openFriendChannel);
};
messenger.setEditable = ui.setEditable;
@ -832,13 +870,10 @@ define([
// TODO split loop innards into ui methods
var checkNewFriends = function () {
Object.keys(friends).forEach(function (f) {
var $friend = ui.getFriend(f);
eachFriend(friends, function (friend, id) {
var $friend = ui.getFriend(id);
if (!$friend.length) {
openFriendChannel(f);
if (f === 'me') { return; }
var friend = friends[f];
openFriendChannel(friend, id);
ui.addToFriendList(friend, display, remove);
}
});
@ -846,7 +881,7 @@ define([
common.onDisplayNameChanged(function () {
checkNewFriends();
updateMyData(common);
updateMyData(proxy);
});
return messenger;
@ -856,17 +891,14 @@ define([
// FIXME there are too many functions with this name
var addToFriendList = Msg.addToFriendList = function (common, data, cb) {
var proxy = common.getProxy();
if (!proxy.friends) {
proxy.friends = {};
}
var friends = proxy.friends;
var friends = getFriendList(proxy);
var pubKey = data.curvePublic;
if (pubKey === proxy.curvePublic) { return void cb("E_MYKEY"); }
friends[pubKey] = data;
common.whenRealtimeSyncs(common.getRealtime(), function () {
Realtime.whenRealtimeSyncs(common.getRealtime(), function () {
cb();
common.pinPads([data.channel]);
});
@ -876,6 +908,7 @@ define([
/* Used to accept friend requests within apps other than /contacts/ */
Msg.addDirectMessageHandler = function (common) {
var network = common.getNetwork();
var proxy = common.getProxy();
if (!network) { return void console.error('Network not ready'); }
network.on('message', function (message, sender) {
var msg;
@ -910,7 +943,7 @@ define([
msgStr = Crypto.encrypt(JSON.stringify(msg), key);
network.sendto(sender, msgStr);
};
var existing = getFriend(common, msgData.curvePublic);
var existing = getFriend(proxy, msgData.curvePublic);
if (existing) {
todo(true);
return;
@ -941,7 +974,6 @@ define([
var i = pendingRequests.indexOf(sender);
if (i !== -1) { pendingRequests.splice(i, 1); }
common.log(common.Messages.contacts_rejected);
var proxy = common.getProxy();
common.changeDisplayName(proxy[common.displayNameKey]);
return;
}

View File

@ -0,0 +1,42 @@
define([
'/customize/application_config.js',
'/customize/messages.js',
], function (AppConfig, Messages) {
var common = {};
common.infiniteSpinnerDetected = false;
var BAD_STATE_TIMEOUT = typeof(AppConfig.badStateTimeout) === 'number'?
AppConfig.badStateTimeout: 30000;
/*
TODO make this not blow up when disconnected or lagging...
*/
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;
}, BAD_STATE_TIMEOUT);
realtime.onSettle(function () {
clearTimeout(to);
cb();
});
}, 0);
};
return common;
});

View File

@ -14,13 +14,15 @@ define([
'/common/common-codemirror.js',
'/common/common-file.js',
'/file/file-crypto.js',
'/common/common-realtime.js',
'/common/clipboard.js',
'/common/pinpad.js',
'/customize/application_config.js',
'/common/media-tag.js',
], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata,
Messaging, CodeMirror, Files, FileCrypto, Clipboard, Pinpad, AppConfig, MediaTag) {
Messaging, CodeMirror, Files, FileCrypto, Realtime, Clipboard,
Pinpad, AppConfig, MediaTag) {
// Configure MediaTags to use our local viewer
if (MediaTag && MediaTag.PdfPlugin) {
@ -129,6 +131,9 @@ define([
common.getLatestMessages = Messaging.getLatestMessages;
common.initMessagingUI = Messaging.UI.init;
// Realtime
var whenRealtimeSyncs = common.whenRealtimeSyncs = Realtime.whenRealtimeSyncs;
// Userlist
common.createUserList = UserList.create;
@ -244,36 +249,6 @@ define([
return;
};
common.infiniteSpinnerDetected = false;
var BAD_STATE_TIMEOUT = typeof(AppConfig.badStateTimeout) === 'number'?
AppConfig.badStateTimeout: 30000;
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;
}, BAD_STATE_TIMEOUT);
realtime.onSettle(function () {
clearTimeout(to);
cb();
});
}, 0);
};
common.getWebsocketURL = function () {
if (!Config.websocketPath) { return Config.websocketURL; }
var path = Config.websocketPath;

View File

@ -70,8 +70,8 @@ define([
'IFRAME',
'OBJECT',
'APPLET',
//'VIDEO',
'AUDIO',
//'VIDEO', // privacy implications of videos are the same as images
//'AUDIO', // same with audio
];
var unsafeTag = function (info) {
/*if (info.node && $(info.node).parents('media-tag').length) {
@ -79,7 +79,7 @@ define([
return true;
}*/
if (['addAttribute', 'modifyAttribute'].indexOf(info.diff.action) !== -1) {
if (/^on/.test(info.diff.name)) {
if (/^on/i.test(info.diff.name)) {
console.log("Rejecting forbidden element attribute with name", info.diff.name);
return true;
}
@ -101,10 +101,25 @@ define([
return Array.prototype.slice.call(coll);
};
var removeNode = function (node) {
if (!(node && node.parentElement)) { return; }
var parent = node.parentElement;
if (!parent) { return; }
console.log('removing %s tag', node.nodeName);
parent.removeChild(node);
};
var removeForbiddenTags = function (root) {
if (!root) { return; }
if (forbiddenTags.indexOf(root.nodeName) !== -1) { removeNode(root); }
slice(root.children).forEach(removeForbiddenTags);
};
/* remove listeners from the DOM */
var removeListeners = function (root) {
slice(root.attributes).map(function (attr) {
if (/^on/.test(attr.name)) {
if (/^on/i.test(attr.name)) {
console.log('removing attribute', attr.name, root.attributes[attr.name]);
root.attributes.removeNamedItem(attr.name);
}
});
@ -114,6 +129,7 @@ define([
var domFromHTML = function (html) {
var Dom = new DOMParser().parseFromString(html, "text/html");
removeForbiddenTags(Dom.body);
removeListeners(Dom.body);
return Dom;
};
@ -148,7 +164,8 @@ define([
var id = $content.attr('id');
if (!id) { throw new Error("The element must have a valid id"); }
var pattern = /(<media-tag src="([^"]*)" data-crypto-key="([^"]*)">)<\/media-tag>/g;
var newHtmlFixed = newHtml.replace(pattern, function (all, tag, src) {
var unsafe_newHtmlFixed = newHtml.replace(pattern, function (all, tag, src) {
var mt = tag;
if (mediaMap[src]) {
mediaMap[src].forEach(function (n) {
@ -157,7 +174,10 @@ define([
}
return mt + '</media-tag>';
});
var $div = $('<div>', {id: id}).append(newHtmlFixed);
var safe_newHtmlFixed = domFromHTML(unsafe_newHtmlFixed).body.outerHTML;
var $div = $('<div>', {id: id}).append(safe_newHtmlFixed);
var Dom = domFromHTML($('<div>').append($div).html());
var oldDom = domFromHTML($content[0].outerHTML);
var patch = makeDiff(oldDom, Dom, id);

View File

@ -624,7 +624,7 @@ define([
}).appendTo(toolbar.$top);
// We need to override the "a" tag action here because it is inside the iframe!
var inDrive = /^\/drive/;
var inDrive = /^\/drive/.test(window.location.pathname);
var href = inDrive ? '/index.html' : '/drive/';
var buttonTitle = inDrive ? Messages.header_homeTitle : Messages.header_logoTitle;

View File

@ -11,7 +11,7 @@
@content-bg: #fff;
@content-bg-ro: darken(@content-bg, 10%);
@content-fg: @tree-fg;
@info-box-bg: #ddddff;
@info-box-bg: #d2e1f2;
@info-box-border: #bbb;
@table-header-fg: #555;
@table-header-bg: #e8e8e8;
@ -380,19 +380,16 @@ span {
margin-top: 10px;
}
.info-box {
line-height: 40px;
padding-left: 10px;
margin: 10px auto;
line-height: 2em;
padding: 0.25em 0.75em;
margin: 1em;
background: @info-box-bg;
border: 1px solid @info-box-border;
border-radius: 5px;
span {
cursor: pointer;
margin-left: 10px;
float: right;
margin-top: 0.5em;
}
&.noclose {
padding-right: 10px;
}
}
li {

View File

@ -0,0 +1,3 @@
<!DOCTYPE html>
<html dir="ltr" lang="en"><head><title>Rich Text Editor, editor1</title><style data-cke-temp="1">html{cursor:text;*cursor:auto}
img,input,textarea{cursor:default}</style><link type="text/css" rel="stylesheet" href="/customize/ckeditor-contents.css"><link type="text/css" rel="stylesheet" href="/bower_components/ckeditor/plugins/tableselection/styles/tableselection.css"></head><body><p><br></p></body></html>

View File

@ -7,6 +7,7 @@
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<script async data-bootload="/pad/inner.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script>
<script src="/bower_components/ckeditor/ckeditor.js"></script>
<script src="/pad/wysiwygarea-plugin.js"></script>
<style>
html, body {
margin: 0px;

View File

@ -43,7 +43,7 @@ define([
var removeListeners = function (root) {
slice(root.attributes).map(function (attr) {
if (/^on/.test(attr.name)) {
if (/^on/i.test(attr.name)) {
root.attributes.removeNamedItem(attr.name);
}
});
@ -313,6 +313,10 @@ define([
if (!readOnly && !initializing) {
userDocStateDom.setAttribute("contenteditable", "true"); // lol wtf
}
$(userDocStateDom).find('script, applet, object, iframe').remove();
$(userDocStateDom).find('a').filter(function (i, x) {
return ! /^(https|http|ftp):\/\/[^\s\n]*$/.test(x.getAttribute('href'));
}).remove();
var patch = (DD).diff(inner, userDocStateDom);
(DD).apply(inner, patch);
if (readOnly) {
@ -625,8 +629,10 @@ define([
if (stringify(hjson2) !== stringify(hjson)) {
console.log('err');
console.error("shjson2 !== shjson");
Cryptpad.errorLoadingScreen(Messages.wrongApp);
throw new Error();
// TODO(cjd): This is removed because the XSS filter in applyHjson()
// is applied on incoming content so it causes this to fail.
//Cryptpad.errorLoadingScreen(Messages.wrongApp);
//throw new Error();
}
}
} else {

View File

@ -0,0 +1,735 @@
/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview The WYSIWYG Area plugin. It registers the "wysiwyg" editing
* mode, which handles the main editing area space.
*/
( function() {
var framedWysiwyg;
var iframe;
CKEDITOR.plugins.registered.wysiwygarea.init = function( editor ) {
if ( editor.config.fullPage ) {
editor.addFeature( {
allowedContent: 'html head title; style [media,type]; body (*)[id]; meta link [*]',
requiredContent: 'body'
} );
}
editor.addMode( 'wysiwyg', function( callback ) {
var src = 'document.open();' +
// In IE, the document domain must be set any time we call document.open().
( CKEDITOR.env.ie ? '(' + CKEDITOR.tools.fixDomain + ')();' : '' ) +
'document.close();';
// With IE, the custom domain has to be taken care at first,
// for other browers, the 'src' attribute should be left empty to
// trigger iframe's 'load' event.
// Microsoft Edge throws "Permission Denied" if treated like an IE (http://dev.ckeditor.com/ticket/13441).
if ( CKEDITOR.env.air ) {
src = 'javascript:void(0)'; // jshint ignore:line
} else if ( CKEDITOR.env.ie && !CKEDITOR.env.edge ) {
src = 'javascript:void(function(){' + encodeURIComponent( src ) + '}())'; // jshint ignore:line
} else {
src = '';
}
// CryptPad
src = '/pad/ckeditor-inner.html';
iframe = CKEDITOR.dom.element.createFromHtml( '<iframe src="' + src + '" frameBorder="0"></iframe>' );
iframe.setStyles( { width: '100%', height: '100%' } );
iframe.addClass( 'cke_wysiwyg_frame' ).addClass( 'cke_reset' );
// CryptPad
// this is impossible because ckeditor uses some (non-inline) script inside of the iframe...
//iframe.setAttribute('sandbox', 'allow-same-origin');
var contentSpace = editor.ui.space( 'contents' );
contentSpace.append( iframe );
// Asynchronous iframe loading is only required in IE>8 and Gecko (other reasons probably).
// Do not use it on WebKit as it'll break the browser-back navigation.
var useOnloadEvent = ( CKEDITOR.env.ie && !CKEDITOR.env.edge ) || CKEDITOR.env.gecko;
if ( useOnloadEvent )
iframe.on( 'load', onLoad );
var frameLabel = editor.title,
helpLabel = editor.fire( 'ariaEditorHelpLabel', {} ).label;
if ( frameLabel ) {
if ( CKEDITOR.env.ie && helpLabel )
frameLabel += ', ' + helpLabel;
iframe.setAttribute( 'title', frameLabel );
}
if ( helpLabel ) {
var labelId = CKEDITOR.tools.getNextId(),
desc = CKEDITOR.dom.element.createFromHtml( '<span id="' + labelId + '" class="cke_voice_label">' + helpLabel + '</span>' );
contentSpace.append( desc, 1 );
iframe.setAttribute( 'aria-describedby', labelId );
}
// Remove the ARIA description.
editor.on( 'beforeModeUnload', function( evt ) {
evt.removeListener();
if ( desc )
desc.remove();
} );
iframe.setAttributes( {
tabIndex: editor.tabIndex,
allowTransparency: 'true'
} );
// Execute onLoad manually for all non IE||Gecko browsers.
!useOnloadEvent && onLoad();
editor.fire( 'ariaWidget', iframe );
function onLoad( evt ) {
evt && evt.removeListener();
var fw = new framedWysiwyg( editor, iframe.$.contentWindow.document.body );
editor.editable( fw );
editor.setData( editor.getData( 1 ), callback );
}
} );
};
/**
* Adds the path to a stylesheet file to the exisiting {@link CKEDITOR.config#contentsCss} value.
*
* **Note:** This method is available only with the `wysiwygarea` plugin and only affects
* classic editors based on it (so it does not affect inline editors).
*
* editor.addContentsCss( 'assets/contents.css' );
*
* @since 4.4
* @param {String} cssPath The path to the stylesheet file which should be added.
* @member CKEDITOR.editor
*/
CKEDITOR.editor.prototype.addContentsCss = function( cssPath ) {
var cfg = this.config,
curContentsCss = cfg.contentsCss;
// Convert current value into array.
if ( !CKEDITOR.tools.isArray( curContentsCss ) )
cfg.contentsCss = curContentsCss ? [ curContentsCss ] : [];
cfg.contentsCss.push( cssPath );
};
function onDomReady( win ) {
var editor = this.editor,
doc = win.document,
body = doc.body;
// Remove helper scripts from the DOM.
var script = doc.getElementById( 'cke_actscrpt' );
script && script.parentNode.removeChild( script );
script = doc.getElementById( 'cke_shimscrpt' );
script && script.parentNode.removeChild( script );
script = doc.getElementById( 'cke_basetagscrpt' );
script && script.parentNode.removeChild( script );
body.contentEditable = true;
if ( CKEDITOR.env.ie ) {
// Don't display the focus border.
body.hideFocus = true;
// Disable and re-enable the body to avoid IE from
// taking the editing focus at startup. (http://dev.ckeditor.com/ticket/141 / http://dev.ckeditor.com/ticket/523)
body.disabled = true;
body.removeAttribute( 'disabled' );
}
delete this._.isLoadingData;
// Play the magic to alter element reference to the reloaded one.
this.$ = body;
doc = new CKEDITOR.dom.document( doc );
this.setup();
this.fixInitialSelection();
var editable = this;
// Without it IE8 has problem with removing selection in nested editable. (http://dev.ckeditor.com/ticket/13785)
if ( CKEDITOR.env.ie && !CKEDITOR.env.edge ) {
doc.getDocumentElement().addClass( doc.$.compatMode );
}
// Prevent IE/Edge from leaving a new paragraph/div after deleting all contents in body. (http://dev.ckeditor.com/ticket/6966, http://dev.ckeditor.com/ticket/13142)
if ( CKEDITOR.env.ie && !CKEDITOR.env.edge && editor.enterMode != CKEDITOR.ENTER_P ) {
removeSuperfluousElement( 'p' );
} else if ( CKEDITOR.env.edge && editor.enterMode != CKEDITOR.ENTER_DIV ) {
removeSuperfluousElement( 'div' );
}
// Fix problem with cursor not appearing in Webkit and IE11+ when clicking below the body (http://dev.ckeditor.com/ticket/10945, http://dev.ckeditor.com/ticket/10906).
// Fix for older IEs (8-10 and QM) is placed inside selection.js.
if ( CKEDITOR.env.webkit || ( CKEDITOR.env.ie && CKEDITOR.env.version > 10 ) ) {
doc.getDocumentElement().on( 'mousedown', function( evt ) {
if ( evt.data.getTarget().is( 'html' ) ) {
// IE needs this timeout. Webkit does not, but it does not cause problems too.
setTimeout( function() {
editor.editable().focus();
} );
}
} );
}
// Config props: disableObjectResizing and disableNativeTableHandles handler.
objectResizeDisabler( editor );
// Enable dragging of position:absolute elements in IE.
try {
editor.document.$.execCommand( '2D-position', false, true );
} catch ( e ) {}
if ( CKEDITOR.env.gecko || CKEDITOR.env.ie && editor.document.$.compatMode == 'CSS1Compat' ) {
this.attachListener( this, 'keydown', function( evt ) {
var keyCode = evt.data.getKeystroke();
// PageUp OR PageDown
if ( keyCode == 33 || keyCode == 34 ) {
// PageUp/PageDown scrolling is broken in document
// with standard doctype, manually fix it. (http://dev.ckeditor.com/ticket/4736)
if ( CKEDITOR.env.ie ) {
setTimeout( function() {
editor.getSelection().scrollIntoView();
}, 0 );
}
// Page up/down cause editor selection to leak
// outside of editable thus we try to intercept
// the behavior, while it affects only happen
// when editor contents are not overflowed. (http://dev.ckeditor.com/ticket/7955)
else if ( editor.window.$.innerHeight > this.$.offsetHeight ) {
var range = editor.createRange();
range[ keyCode == 33 ? 'moveToElementEditStart' : 'moveToElementEditEnd' ]( this );
range.select();
evt.data.preventDefault();
}
}
} );
}
if ( CKEDITOR.env.ie ) {
// [IE] Iframe will still keep the selection when blurred, if
// focus is moved onto a non-editing host, e.g. link or button, but
// it becomes a problem for the object type selection, since the resizer
// handler attached on it will mark other part of the UI, especially
// for the dialog. (http://dev.ckeditor.com/ticket/8157)
// [IE<8 & Opera] Even worse For old IEs, the cursor will not vanish even if
// the selection has been moved to another text input in some cases. (http://dev.ckeditor.com/ticket/4716)
//
// Now the range restore is disabled, so we simply force IE to clean
// up the selection before blur.
this.attachListener( doc, 'blur', function() {
// Error proof when the editor is not visible. (http://dev.ckeditor.com/ticket/6375)
try {
doc.$.selection.empty();
} catch ( er ) {}
} );
}
if ( CKEDITOR.env.iOS ) {
// [iOS] If touch is bound to any parent of the iframe blur happens on any touch
// event and body becomes the focused element (http://dev.ckeditor.com/ticket/10714).
this.attachListener( doc, 'touchend', function() {
win.focus();
} );
}
var title = editor.document.getElementsByTag( 'title' ).getItem( 0 );
// document.title is malfunctioning on Chrome, so get value from the element (http://dev.ckeditor.com/ticket/12402).
title.data( 'cke-title', title.getText() );
// [IE] JAWS will not recognize the aria label we used on the iframe
// unless the frame window title string is used as the voice label,
// backup the original one and restore it on output.
if ( CKEDITOR.env.ie )
editor.document.$.title = this._.docTitle;
CKEDITOR.tools.setTimeout( function() {
// Editable is ready after first setData.
if ( this.status == 'unloaded' )
this.status = 'ready';
editor.fire( 'contentDom' );
if ( this._.isPendingFocus ) {
editor.focus();
this._.isPendingFocus = false;
}
setTimeout( function() {
editor.fire( 'dataReady' );
}, 0 );
}, 0, this );
function removeSuperfluousElement( tagName ) {
var lockRetain = false;
// Superfluous elements appear after keydown
// and before keyup, so the procedure is as follows:
// 1. On first keydown mark all elements with
// a specified tag name as non-superfluous.
editable.attachListener( editable, 'keydown', function() {
var body = doc.getBody(),
retained = body.getElementsByTag( tagName );
if ( !lockRetain ) {
for ( var i = 0; i < retained.count(); i++ ) {
retained.getItem( i ).setCustomData( 'retain', true );
}
lockRetain = true;
}
}, null, null, 1 );
// 2. On keyup remove all elements that were not marked
// as non-superfluous (which means they must have had appeared in the meantime).
// Also we should preserve all temporary elements inserted by editor otherwise we'd likely
// leak fake selection's content into editable due to removing hidden selection container (http://dev.ckeditor.com/ticket/14831).
editable.attachListener( editable, 'keyup', function() {
var elements = doc.getElementsByTag( tagName );
if ( lockRetain ) {
if ( elements.count() == 1 && !elements.getItem( 0 ).getCustomData( 'retain' ) &&
!elements.getItem( 0 ).hasAttribute( 'data-cke-temp' ) ) {
elements.getItem( 0 ).remove( 1 );
}
lockRetain = false;
}
} );
}
}
framedWysiwyg = CKEDITOR.tools.createClass( {
$: function() {
this.base.apply( this, arguments );
this._.frameLoadedHandler = CKEDITOR.tools.addFunction( function( win ) {
// Avoid opening design mode in a frame window thread,
// which will cause host page scrolling.(http://dev.ckeditor.com/ticket/4397)
CKEDITOR.tools.setTimeout( onDomReady, 0, this, win );
}, this );
this._.docTitle = this.getWindow().getFrame().getAttribute( 'title' );
},
base: CKEDITOR.editable,
proto: {
setData: function( data, isSnapshot ) {
var editor = this.editor;
if ( isSnapshot ) {
this.setHtml( data );
this.fixInitialSelection();
// Fire dataReady for the consistency with inline editors
// and because it makes sense. (http://dev.ckeditor.com/ticket/10370)
editor.fire( 'dataReady' );
}
else {
this._.isLoadingData = true;
editor._.dataStore = { id: 1 };
var config = editor.config,
fullPage = config.fullPage,
docType = config.docType;
// Build the additional stuff to be included into <head>.
var headExtra = CKEDITOR.tools.buildStyleHtml( iframeCssFixes() ).replace( /<style>/, '<style data-cke-temp="1">' );
if ( !fullPage )
headExtra += CKEDITOR.tools.buildStyleHtml( editor.config.contentsCss );
var baseTag = config.baseHref ? '<base href="' + config.baseHref + '" data-cke-temp="1" />' : '';
if ( fullPage ) {
// Search and sweep out the doctype declaration.
data = data.replace( /<!DOCTYPE[^>]*>/i, function( match ) {
editor.docType = docType = match;
return '';
} ).replace( /<\?xml\s[^\?]*\?>/i, function( match ) {
editor.xmlDeclaration = match;
return '';
} );
}
// Get the HTML version of the data.
data = editor.dataProcessor.toHtml( data );
if ( fullPage ) {
// Check if the <body> tag is available.
if ( !( /<body[\s|>]/ ).test( data ) )
data = '<body>' + data;
// Check if the <html> tag is available.
if ( !( /<html[\s|>]/ ).test( data ) )
data = '<html>' + data + '</html>';
// Check if the <head> tag is available.
if ( !( /<head[\s|>]/ ).test( data ) )
data = data.replace( /<html[^>]*>/, '$&<head><title></title></head>' );
else if ( !( /<title[\s|>]/ ).test( data ) )
data = data.replace( /<head[^>]*>/, '$&<title></title>' );
// The base must be the first tag in the HEAD, e.g. to get relative
// links on styles.
baseTag && ( data = data.replace( /<head[^>]*?>/, '$&' + baseTag ) );
// Inject the extra stuff into <head>.
// Attention: do not change it before testing it well. (V2)
// This is tricky... if the head ends with <meta ... content type>,
// Firefox will break. But, it works if we place our extra stuff as
// the last elements in the HEAD.
data = data.replace( /<\/head\s*>/, headExtra + '$&' );
// Add the DOCTYPE back to it.
data = docType + data;
} else {
data = config.docType +
'<html dir="' + config.contentsLangDirection + '"' +
' lang="' + ( config.contentsLanguage || editor.langCode ) + '">' +
'<head>' +
'<title>' + this._.docTitle + '</title>' +
baseTag +
headExtra +
'</head>' +
'<body' + ( config.bodyId ? ' id="' + config.bodyId + '"' : '' ) +
( config.bodyClass ? ' class="' + config.bodyClass + '"' : '' ) +
'>' +
data +
'</body>' +
'</html>';
}
if ( CKEDITOR.env.gecko ) {
// Hack to make Fx put cursor at the start of doc on fresh focus.
data = data.replace( /<body/, '<body contenteditable="true" ' );
// Another hack which is used by onDomReady to remove a leading
// <br> which is inserted by Firefox 3.6 when document.write is called.
// This additional <br> is present because of contenteditable="true"
if ( CKEDITOR.env.version < 20000 )
data = data.replace( /<body[^>]*>/, '$&<!-- cke-content-start -->' );
}
// The script that launches the bootstrap logic on 'domReady', so the document
// is fully editable even before the editing iframe is fully loaded (http://dev.ckeditor.com/ticket/4455).
var bootstrapCode =
'<script id="cke_actscrpt" type="text/javascript"' + ( CKEDITOR.env.ie ? ' defer="defer" ' : '' ) + '>' +
'var wasLoaded=0;' + // It must be always set to 0 as it remains as a window property.
'function onload(){' +
'if(!wasLoaded)' + // FF3.6 calls onload twice when editor.setData. Stop that.
'window.parent.CKEDITOR.tools.callFunction(' + this._.frameLoadedHandler + ',window);' +
'wasLoaded=1;' +
'}' +
( CKEDITOR.env.ie ? 'onload();' : 'document.addEventListener("DOMContentLoaded", onload, false );' ) +
'</script>';
// For IE<9 add support for HTML5's elements.
// Note: this code must not be deferred.
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) {
bootstrapCode +=
'<script id="cke_shimscrpt">' +
'window.parent.CKEDITOR.tools.enableHtml5Elements(document)' +
'</script>';
}
// IE<10 needs this hack to properly enable <base href="...">.
// See: http://stackoverflow.com/a/13373180/1485219 (http://dev.ckeditor.com/ticket/11910).
if ( baseTag && CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) {
bootstrapCode +=
'<script id="cke_basetagscrpt">' +
'var baseTag = document.querySelector( "base" );' +
'baseTag.href = baseTag.href;' +
'</script>';
}
data = data.replace( /(?=\s*<\/(:?head)>)/, bootstrapCode );
// Current DOM will be deconstructed by document.write, cleanup required.
this.clearCustomData();
this.clearListeners();
editor.fire( 'contentDomUnload' );
var doc = this.getDocument();
// CryptPad
var _iframe = window._iframe = iframe.$;
var fw = this;
_iframe.contentWindow.onload = function () {}
var intr = setInterval(function () {
//console.log(_iframe.contentWindow.document.body);
if (!_iframe.contentWindow) { return; }
if (!_iframe.contentWindow.document) { return; }
if (_iframe.contentWindow.document.readyState !== 'complete') { return; }
if (!_iframe.contentWindow.document.getElementsByTagName('title').length) { return; }
clearInterval(intr);
CKEDITOR.tools.callFunction(fw._.frameLoadedHandler, _iframe.contentWindow);
}, 10);
return;
// Work around Firefox bug - error prune when called from XUL (http://dev.ckeditor.com/ticket/320),
// defer it thanks to the async nature of this method.
try {
doc.write( data );
} catch ( e ) {
setTimeout( function() {
doc.write( data );
}, 0 );
}
}
},
getData: function( isSnapshot ) {
if ( isSnapshot )
return this.getHtml();
else {
var editor = this.editor,
config = editor.config,
fullPage = config.fullPage,
docType = fullPage && editor.docType,
xmlDeclaration = fullPage && editor.xmlDeclaration,
doc = this.getDocument();
var data = fullPage ? doc.getDocumentElement().getOuterHtml() : doc.getBody().getHtml();
// BR at the end of document is bogus node for Mozilla. (http://dev.ckeditor.com/ticket/5293).
// Prevent BRs from disappearing from the end of the content
// while enterMode is ENTER_BR (http://dev.ckeditor.com/ticket/10146).
if ( CKEDITOR.env.gecko && config.enterMode != CKEDITOR.ENTER_BR )
data = data.replace( /<br>(?=\s*(:?$|<\/body>))/, '' );
data = editor.dataProcessor.toDataFormat( data );
if ( xmlDeclaration )
data = xmlDeclaration + '\n' + data;
if ( docType )
data = docType + '\n' + data;
return data;
}
},
focus: function() {
if ( this._.isLoadingData )
this._.isPendingFocus = true;
else
framedWysiwyg.baseProto.focus.call( this );
},
detach: function() {
var editor = this.editor,
doc = editor.document,
iframe,
onResize;
// Trying to access window's frameElement property on Edge throws an exception
// when frame was already removed from DOM. (http://dev.ckeditor.com/ticket/13850, http://dev.ckeditor.com/ticket/13790)
try {
iframe = editor.window.getFrame();
} catch ( e ) {}
framedWysiwyg.baseProto.detach.call( this );
// Memory leak proof.
this.clearCustomData();
doc.getDocumentElement().clearCustomData();
CKEDITOR.tools.removeFunction( this._.frameLoadedHandler );
// On IE, iframe is returned even after remove() method is called on it.
// Checking if parent is present fixes this issue. (http://dev.ckeditor.com/ticket/13850)
if ( iframe && iframe.getParent() ) {
iframe.clearCustomData();
onResize = iframe.removeCustomData( 'onResize' );
onResize && onResize.removeListener();
// IE BUG: When destroying editor DOM with the selection remains inside
// editing area would break IE7/8's selection system, we have to put the editing
// iframe offline first. (http://dev.ckeditor.com/ticket/3812 and http://dev.ckeditor.com/ticket/5441)
iframe.remove();
} else {
CKEDITOR.warn( 'editor-destroy-iframe' );
}
}
}
} );
function objectResizeDisabler( editor ) {
if ( CKEDITOR.env.gecko ) {
// FF allows to change resizing preferences by calling execCommand.
try {
var doc = editor.document.$;
doc.execCommand( 'enableObjectResizing', false, !editor.config.disableObjectResizing );
doc.execCommand( 'enableInlineTableEditing', false, !editor.config.disableNativeTableHandles );
} catch ( e ) {}
} else if ( CKEDITOR.env.ie && CKEDITOR.env.version < 11 && editor.config.disableObjectResizing ) {
// It's possible to prevent resizing up to IE10.
blockResizeStart( editor );
}
// Disables resizing by preventing default action on resizestart event.
function blockResizeStart() {
var lastListeningElement;
// We'll attach only one listener at a time, instead of adding it to every img, input, hr etc.
// Listener will be attached upon selectionChange, we'll also check if there was any element that
// got listener before (lastListeningElement) - if so we need to remove previous listener.
editor.editable().attachListener( editor, 'selectionChange', function() {
var selectedElement = editor.getSelection().getSelectedElement();
if ( selectedElement ) {
if ( lastListeningElement ) {
lastListeningElement.detachEvent( 'onresizestart', resizeStartListener );
lastListeningElement = null;
}
// IE requires using attachEvent, because it does not work using W3C compilant addEventListener,
// tested with IE10.
selectedElement.$.attachEvent( 'onresizestart', resizeStartListener );
lastListeningElement = selectedElement.$;
}
} );
}
function resizeStartListener( evt ) {
evt.returnValue = false;
}
}
function iframeCssFixes() {
var css = [];
// IE>=8 stricts mode doesn't have 'contentEditable' in effect
// on element unless it has layout. (http://dev.ckeditor.com/ticket/5562)
if ( CKEDITOR.document.$.documentMode >= 8 ) {
css.push( 'html.CSS1Compat [contenteditable=false]{min-height:0 !important}' );
var selectors = [];
for ( var tag in CKEDITOR.dtd.$removeEmpty )
selectors.push( 'html.CSS1Compat ' + tag + '[contenteditable=false]' );
css.push( selectors.join( ',' ) + '{display:inline-block}' );
}
// Set the HTML style to 100% to have the text cursor in affect (http://dev.ckeditor.com/ticket/6341)
else if ( CKEDITOR.env.gecko ) {
css.push( 'html{height:100% !important}' );
css.push( 'img:-moz-broken{-moz-force-broken-image-icon:1;min-width:24px;min-height:24px}' );
}
// http://dev.ckeditor.com/ticket/6341: The text cursor must be set on the editor area.
// http://dev.ckeditor.com/ticket/6632: Avoid having "text" shape of cursor in IE7 scrollbars.
css.push( 'html{cursor:text;*cursor:auto}' );
// Use correct cursor for these elements
css.push( 'img,input,textarea{cursor:default}' );
return css.join( '\n' );
}
} )();
/**
* Disables the ability to resize objects (images and tables) in the editing area.
*
* config.disableObjectResizing = true;
*
* **Note:** Because of incomplete implementation of editing features in browsers
* this option does not work for inline editors (see ticket [#10197](http://dev.ckeditor.com/ticket/10197)),
* does not work in Internet Explorer 11+ (see [#9317](http://dev.ckeditor.com/ticket/9317#comment:16) and
* [IE11+ issue](https://connect.microsoft.com/IE/feedback/details/742593/please-respect-execcommand-enableobjectresizing-in-contenteditable-elements)).
* In Internet Explorer 8-10 this option only blocks resizing, but it is unable to hide the resize handles.
*
* @cfg
* @member CKEDITOR.config
*/
CKEDITOR.config.disableObjectResizing = false;
/**
* Disables the "table tools" offered natively by the browser (currently
* Firefox only) to perform quick table editing operations, like adding or
* deleting rows and columns.
*
* config.disableNativeTableHandles = false;
*
* @cfg
* @member CKEDITOR.config
*/
CKEDITOR.config.disableNativeTableHandles = true;
/**
* Disables the built-in spell checker if the browser provides one.
*
* **Note:** Although word suggestions provided natively by the browsers will
* not appear in CKEditor's default context menu,
* users can always reach the native context menu by holding the
* *Ctrl* key when right-clicking if {@link #browserContextMenuOnCtrl}
* is enabled or you are simply not using the
* [context menu](http://ckeditor.com/addon/contextmenu) plugin.
*
* config.disableNativeSpellChecker = false;
*
* @cfg
* @member CKEDITOR.config
*/
CKEDITOR.config.disableNativeSpellChecker = true;
/**
* Language code of the writing language which is used to author the editor
* content. This option accepts one single entry value in the format defined in the
* [Tags for Identifying Languages (BCP47)](http://www.ietf.org/rfc/bcp/bcp47.txt)
* IETF document and is used in the `lang` attribute.
*
* config.contentsLanguage = 'fr';
*
* @cfg {String} [contentsLanguage=same value with editor's UI language]
* @member CKEDITOR.config
*/
/**
* The base href URL used to resolve relative and absolute URLs in the
* editor content.
*
* config.baseHref = 'http://www.example.com/path/';
*
* @cfg {String} [baseHref='']
* @member CKEDITOR.config
*/
/**
* Whether to automatically create wrapping blocks around inline content inside the document body.
* This helps to ensure the integrity of the block *Enter* mode.
*
* **Note:** This option is deprecated. Changing the default value might introduce unpredictable usability issues and is
* highly unrecommended.
*
* config.autoParagraph = false;
*
* @deprecated
* @since 3.6
* @cfg {Boolean} [autoParagraph=true]
* @member CKEDITOR.config
*/
/**
* Fired when some elements are added to the document.
*
* @event ariaWidget
* @member CKEDITOR.editor
* @param {CKEDITOR.editor} editor This editor instance.
* @param {CKEDITOR.dom.element} data The element being added.
*/

View File

@ -116,7 +116,7 @@ table#table {
min-width: 80%;
width: 80%;
min-height: 5em;
min-height: 7em;
font-size: 20px;
font-weight: bold;
border: 1px solid black;

View File

@ -39,7 +39,6 @@ define([
'account': [
'infoBlock',
'displayName',
'indentUnit',
'languageSelector',
'logoutEverywhere',
'resetTips',
@ -49,6 +48,10 @@ define([
'backupDrive',
'importLocalPads',
'resetDrive'
],
'code': [
'indentUnit',
'indentType'
]
};
@ -129,10 +132,10 @@ define([
var createIndentUnitSelector = function (obj) {
var proxy = obj.proxy;
console.log('create indent unit selector');
var $div = $('<div>', {
'class': 'indentUnit element'
});
$('<label>').text(Messages.settings_codeIndentation).appendTo($div);
var $inputBlock = $('<div>', {
'class': 'inputBlock',
@ -144,7 +147,6 @@ define([
type: 'number',
}).on('change', function () {
var val = parseInt($input.val());
console.log(val, typeof(val));
if (typeof(val) !== 'number') { return; }
proxy['cryptpad.indentUnit'] = val;
}).appendTo($inputBlock);
@ -162,6 +164,41 @@ define([
return $div;
};
var createIndentTypeSelector = function (obj) {
var proxy = obj.proxy;
var key = 'cryptpad.indentWithTabs';
var $div = $('<div>', {
'class': 'indentType element'
});
$('<label>').text(Messages.settings_codeUseTabs).appendTo($div);
var $inputBlock = $('<div>', {
'class': 'inputBlock',
}).appendTo($div);
var $input = $('<input>', {
type: 'checkbox',
}).on('change', function () {
var val = $input.is(':checked');
if (typeof(val) !== 'boolean') { return; }
proxy[key] = val;
}).appendTo($inputBlock);
proxy.on('change', [key], function (o, n) { $input.val(n); });
Cryptpad.getAttribute('indentUnit', function (e, val) {
if (e) { return void console.error(e); }
if (typeof(val) !== 'number') {
$input.val(2);
} else {
$input.val(val);
}
});
return $div;
};
var createResetTips = function () {
var $div = $('<div>', {'class': 'resetTips element'});
$('<label>', {'for' : 'resetTips'}).text(Messages.settings_resetTips).appendTo($div);
@ -380,6 +417,7 @@ define([
var $category = $('<div>', {'class': 'category'}).appendTo($categories);
if (key === 'account') { $category.append($('<span>', {'class': 'fa fa-user-o'})); }
if (key === 'drive') { $category.append($('<span>', {'class': 'fa fa-hdd-o'})); }
if (key === 'code') { $category.append($('<span>', {'class': 'fa fa-file-code-o' })); }
if (key === active) {
$category.addClass('active');
@ -421,6 +459,7 @@ define([
$rightside.append(createDisplayNameInput(obj));
$rightside.append(createLanguageSelector());
$rightside.append(createIndentUnitSelector(obj));
$rightside.append(createIndentTypeSelector(obj));
if (Cryptpad.isLoggedIn()) {
$rightside.append(createLogoutEverywhere(obj));

View File

@ -31,6 +31,9 @@
&[type="number"] {
border-right: 1px solid #adadad;
}
&[type="checkbox"] {
margin-right: 100%;
}
}
}
.infoBlock {

View File

@ -36,6 +36,7 @@ h6 { font-size: 24px; }
body {
.CodeMirror {
height: 100%;
font-size: initial;
}
.CodeMirror-focused .cm-matchhighlight {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);