Merge branch 'newtoolbar' into staging

pull/1/head
yflory 8 years ago
commit 6661eedacf

@ -0,0 +1,5 @@
data
Dockerfile
docker-compose.yml
.git
.gitignore

@ -0,0 +1,4 @@
VERSION=latest
USE_SSL=true
STORAGE='./storage/file'
LOG_TO_STDOUT=true

1
.gitignore vendored

@ -9,3 +9,4 @@ customization
messages.log messages.log
.DS_Store .DS_Store
www/scratch www/scratch
data

@ -6,9 +6,8 @@ env:
branches: branches:
only: only:
- master - master
- diffdom - soon
- beta - staging
- netflux
node_js: node_js:
- "4.2.1" - "4.2.1"
before_script: before_script:

@ -0,0 +1,43 @@
FROM ubuntu:16.04
RUN apt-get update && apt-get install -y \
vim \
wget \
git \
curl \
npm \
nodejs-legacy
ARG VERSION=0.3.0
# Download stable version
# RUN wget https://github.com/xwiki-labs/cryptpad/archive /${VERSION}.tar.gz -O /cryptpad.tar.gz \
# && mkdir -p /cryptpad \
# && tar -xzf /cryptpad.tar.gz -C /cryptpad --strip-components=1 \
# && rm /cryptpad.tar.gz
# Download from github
# RUN git clone https://github.com/xwiki-labs/cryptpad.git
# Add code directly
ADD . /cryptpad
WORKDIR /cryptpad
RUN npm install \
&& npm install -g bower \
&& bower install --allow-root
ADD container-start.sh /container-start.sh
RUN chmod u+x /container-start.sh
EXPOSE 3000
VOLUME /cryptpad/datastore
VOLUME /cryptpad/customize
ENV USE_SSL=false
ENV STORAGE='./storage/file'
ENV LOG_TO_STDOUT=true
CMD /container-start.sh

@ -1,6 +1,11 @@
/* global process */ /* global process */
var WebDriver = require("selenium-webdriver"); var WebDriver = require("selenium-webdriver");
if (process.env.TRAVIS_PULL_REQUEST && process.env.TRAVIS_PULL_REQUEST !== 'false') {
// We can't do saucelabs on pull requests so don't fail.
return;
}
var driver; var driver;
if (process.env.SAUCE_USERNAME !== undefined) { if (process.env.SAUCE_USERNAME !== undefined) {
var browserArray = process.env.BROWSER.split(':'); var browserArray = process.env.BROWSER.split(':');
@ -19,12 +24,15 @@ if (process.env.SAUCE_USERNAME !== undefined) {
driver.get('http://localhost:3000/assert/'); driver.get('http://localhost:3000/assert/');
var report = driver.wait(WebDriver.until.elementLocated(WebDriver.By.className("report")), 5000); var report = driver.wait(WebDriver.until.elementLocated(WebDriver.By.className("report")), 5000);
report.getAttribute("class").then(function (cls) { report.getAttribute("class").then(function (cls) {
driver.quit(); report.getText().then(function (text) {
if (!cls) { console.log("\n-----\n" + text + "\n-----");
throw new Error("cls is null"); driver.quit();
} else if (cls.indexOf("failure") !== -1) { if (!cls) {
throw new Error("cls contains the word failure"); throw new Error("cls is null");
} else if (cls.indexOf("success") === -1) { } else if (cls.indexOf("failure") !== -1) {
throw new Error("cls does not contain the word success"); throw new Error("cls contains the word failure");
} } else if (cls.indexOf("success") === -1) {
throw new Error("cls does not contain the word success");
}
});
}); });

@ -1,61 +0,0 @@
'use strict'
let WebSocketServer = require('ws').Server
const UNSUPPORTED_DATA = 1007
const POLICY_VIOLATION = 1008
const CLOSE_UNSUPPORTED = 1003
var run = module.exports.run = function(server) {
server.on('connection', (socket) => {
if(socket.upgradeReq.url !== '/cryptpad_webrtc') { return; }
socket.on('message', (data) => {
try {
let msg = JSON.parse(data)
console.log(msg)
if (msg.hasOwnProperty('key')) {
for (let master of server.clients) {
if (master.key === msg.key) {
socket.close(POLICY_VIOLATION, 'The key already exists')
return
}
}
socket.key = msg.key
socket.joiningClients = []
} else if (msg.hasOwnProperty('id')) {
for (let index in socket.joiningClients) {
if (index == msg.id) {
socket.joiningClients[index].send(JSON.stringify({data: msg.data}))
return
}
}
socket.close(POLICY_VIOLATION, 'Unknown id')
} else if (msg.hasOwnProperty('join')) {
for (let master of server.clients) {
if (master.key === msg.join) {
socket.master = master
master.joiningClients.push(socket)
let id = master.joiningClients.length - 1
master.send(JSON.stringify({id, data: msg.data}))
return
}
}
socket.close(POLICY_VIOLATION, 'Unknown key')
} else if (msg.hasOwnProperty('data') && socket.hasOwnProperty('master')) {
let id = socket.master.joiningClients.indexOf(socket)
socket.master.send(JSON.stringify({id, data: msg.data}))
} else {
socket.close(UNSUPPORTED_DATA, 'Unsupported message format')
}
} catch (event) {
socket.close(CLOSE_UNSUPPORTED, 'Server accepts only JSON')
}
})
socket.on('close', (event) => {
if (socket.hasOwnProperty('joiningClients')) {
for (let client of socket.joiningClients) {
client.close(POLICY_VIOLATION, 'The peer is no longer available')
}
}
});
})
}

@ -42,6 +42,6 @@
"alertifyjs": "^1.0.11", "alertifyjs": "^1.0.11",
"spin.js": "^2.3.2", "spin.js": "^2.3.2",
"scrypt-async": "^1.2.0", "scrypt-async": "^1.2.0",
"bootstrap": "^3.3.7" "bootstrap": "#v4.0.0-alpha.6"
} }
} }

@ -17,7 +17,7 @@ module.exports = {
/* /*
httpHeaders: { httpHeaders: {
"Content-Security-Policy": [ "Content-Security-Policy": [
"default-serc 'none'", "default-src 'none'",
"style-src 'unsafe-inline' 'self'", "style-src 'unsafe-inline' 'self'",
"script-src 'self' 'unsafe-eval' 'unsafe-inline'", "script-src 'self' 'unsafe-eval' 'unsafe-inline'",
"child-src 'self' cryptpad.fr *.cryptpad.fr", "child-src 'self' cryptpad.fr *.cryptpad.fr",
@ -50,6 +50,12 @@ module.exports = {
*/ */
//websocketPort: 3000, //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. /* If Cryptpad is proxied without using https, the server needs to know.
* Specify 'useSecureWebsockets: true' so that it can send * Specify 'useSecureWebsockets: true' so that it can send
* Content Security Policy Headers that prevent http and https from mixing * Content Security Policy Headers that prevent http and https from mixing
@ -75,6 +81,7 @@ module.exports = {
'privacy', 'privacy',
'terms', 'terms',
'about', 'about',
'contact',
], ],
/* /*

@ -0,0 +1,23 @@
#!/bin/bash
# Creating customize folder
mkdir -p customize
[[ ! "$(ls -A customize)" ]] && echo "Creating customize folder" \
&& cp -R customize.dist/* customize/ \
&& cp config.js.dist customize/config.js
# Linking config.js
[[ ! -h config.js ]] && echo "Linking config.js" && ln -s customize/config.js config.js
# Configure
[[ -n "$USE_SSL" ]] && echo "Using secure websockets: $USE_SSL" \
&& sed -i "s/useSecureWebsockets: .*/useSecureWebsockets: ${USE_SSL},/g" customize/config.js
[[ -n "$USE_SSL" ]] && echo "Using storage adapter: $STORAGE" \
&& sed -i "s/storage: .*/storage: ${STORAGE},/g" customize/config.js
[[ -n "$LOG_TO_STDOUT" ]] && echo "Logging to stdout: $LOG_TO_STDOUT" \
&& sed -i "s/logToStdout: .*/logToStdout: ${LOG_TO_STDOUT},/g" customize/config.js
exec node ./server.js

@ -0,0 +1,69 @@
# Cryptpad Docker Image
- Configuration via .env file
- Ready for use with traffic
- Using github master for now, release 0.3.0 too old
- Creating customize folder
- Adding config.js to customize folder
- Persistance for datastore and customize folder
## TODO
```
cryptpad_1 | Linking config.js
cryptpad_1 | Using secure websockets: true
cryptpad_1 | Using storage adapter: './storage/file'
cryptpad_1 | sed: -e expression #1, char 27: unknown option to `s'
```
## Configuration
Set configurations Dockerfile or in .env (using docker-compose) file.
- VERSION=latest
- USE_SSL=false
- STORAGE='./storage/file'
- LOG_TO_STDOUT=true
The .env variables are read by docker-compose and forwarded to docker container.
On runtime, in `bin/container-start.sh` the settings are written to the `config.js` file.
## Run
With docker
```
docker build -t xwiki/cryptpad .
docker -d --name cryptpad -p 3000:3000 -v ${PWD}/data:/cryptpad/datastore xwiki/cryptpad
```
With docker-compose
```
docker-compose up -d
```
## Persistance
The docker-compose file is preconfigured to persist folders
- cryptpad/datastore --> ./data/customize
- cryptpad/customize --> ./data/customize
In customize included find your configuration in `config.js`.
The data folder is ignored by git, so if you want to add your customizations to git versioning change the volume:
```
./customize:/cryptpad/customize:rw
```
## SSL Proxy
The [traefik](https://traefik.io/) proxy has builtin Let'sEncrypt for easy SSL setup.
In the docker-compose file you can find preset lables for usage with traefik.
[Traefik Docker Image](https://hub.docker.com/_/traefik/)
Alternativly just use plain old nginx.

@ -1,39 +0,0 @@
/*
globals define
*/
define([
'/customize/languageSelector.js',
'/customize/messages.js',
'/bower_components/jquery/dist/jquery.min.js'
], function (LS, Messages) {
var $ = window.jQuery;
var main = function () {
var url = window.location.pathname;
var isHtml = /\.html/.test(url) || url === '/' || url === '';
if (!isHtml) {
Messages._applyTranslation();
return;
}
$.ajax({
url: isHtml ? '/customize/BottomBar.html' : '/customize/Header.html',
success: function (ret) {
var $bar = $(ret);
$('body').append($bar);
var $sel = $bar.find('#language-selector');
Object.keys(Messages._languages).forEach(function (code) {
$sel.append($('<option>', {
value: code,
}).text(Messages._languages[code]));
});
LS.main();
Messages._applyTranslation();
}
});
};
return {
main: main
};
});

@ -3,8 +3,12 @@
<head> <head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title> <title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/customize/main.css" /> <link rel="stylesheet" type="text/css" href="/customize/main.css" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/> <link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<script data-main="/customize/main" src="/bower_components/requirejs/require.js"></script> <script data-main="/customize/main" src="/bower_components/requirejs/require.js"></script>
<script src="/bower_components/requirejs/require.js"></script> <script src="/bower_components/requirejs/require.js"></script>
<script> <script>
@ -13,18 +17,30 @@
}); });
</script> </script>
</head> </head>
<body> <body class="html">
<a data-localization-title="github_ribbon" href="https://github.com/xwiki-labs/cryptpad" class="github-corner" aria-label="View source on Github"><svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 0; border: 0; left: 0; transform: scale(-1, 1);" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style> <div id="cryptpadTopBar">
<!-- Thanks! http://tholman.com/github-corners/ --> <span>
<a class="gotoMain" href="/">
<img src="customize/cryptofist_mini.png" class="cryptpad-logo" alt="" /> CryptPad
</a>
</span>
<!--<span class="slogan" data-localization="main_slogan"></span>-->
<span id="language-selector" class="right dropdown-bar"></span>
<select id="language-selector" style="display: none;"></select> <span class="right">
<a href="/about.html" data-localization="about">About</a>
<div id="main"> </span>
<center> <span class="right">
<a href="/"><img class="imgcenter cryptofist" src="/customize/cryptofist_small.png" /></a> <a href="/privacy.html" data-localization="privacy">Privacy</a>
</center> </span>
<span class="right">
<a href="/terms.html" data-localization="terms">ToS</a>
</span>
<span class="right">
<a href="/contact.html" data-localization="contact">Contact</a>
</span>
</div>
<center> <center>
@ -41,18 +57,18 @@
<div id="main_other">
<center> <center>
<h1>About</h1> <h1 data-localization="about">About</h1>
</center> </center>
<p data-localization="main_p2"><!-- CkEditor, CodeMirror, Chainpad --></p> <p data-localization="main_p2"><!-- CkEditor, CodeMirror, Chainpad --></p>
<h2 id="howitworks" data-localization="main_howitworks"></h2> <h2 id="howitworks" data-localization="main_howitworks"></h2>
<p data-localization="main_howitworks_p1"><!-- Operational transform, Nakamoto blockchain, server kept unaware of the content--></p> <p data-localization="main_howitworks_p1"><!-- Operational transform, Nakamoto blockchain, server kept unaware of the content--></p>
</div>
</div>
</body> </body>
</html> </html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html class="cp">
<head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/customize/main.css" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<script data-main="/customize/main" src="/bower_components/requirejs/require.js"></script>
<script src="/bower_components/requirejs/require.js"></script>
<script>
require.config({
waitSeconds: 60,
});
</script>
</head>
<body class="html">
<div id="cryptpadTopBar">
<span>
<a class="gotoMain" href="/">
<img src="customize/cryptofist_mini.png" class="cryptpad-logo" alt="" /> CryptPad
</a>
</span>
<!--<span class="slogan" data-localization="main_slogan"></span>-->
<span id="language-selector" class="right dropdown-bar"></span>
<span class="right">
<a href="/about.html" data-localization="about">About</a>
</span>
<span class="right">
<a href="/privacy.html" data-localization="privacy">Privacy</a>
</span>
<span class="right">
<a href="/terms.html" data-localization="terms">ToS</a>
</span>
<span class="right">
<a href="/contact.html" data-localization="contact">Contact</a>
</span>
</div>
<center>
<noscript>
<p>
<strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.
</p>
<hr>
<p>
<strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.
</p>
</noscript>
</center>
<div id="main_other">
<center>
<h1 data-localization="contact">Contact</h1>
</center>
<p data-localization="main_about_p2"><!-- Contact us--></p>
</div>
</body>
</html>

@ -4,7 +4,7 @@ define([
'/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.amd.js', '/bower_components/textpatcher/TextPatcher.amd.js',
'/file/fileObject.js' '/common/fileObject.js'
], function (Config, Messages, Listmap, Crypto, TextPatcher, FO) { ], function (Config, Messages, Listmap, Crypto, TextPatcher, FO) {
/* /*
This module uses localStorage, which is synchronous, but exposes an This module uses localStorage, which is synchronous, but exposes an
@ -39,6 +39,11 @@ define([
cb(void 0, map); cb(void 0, map);
}; };
Store.setDrive = function (key, val, cb) {
storeObj.drive[key] = val;
cb();
};
var safeGet = window.safeGet = function (key) { var safeGet = window.safeGet = function (key) {
return storeObj[key]; return storeObj[key];
}; };
@ -56,6 +61,10 @@ define([
cb(void 0, res); cb(void 0, res);
}; };
Store.getDrive = function (key, cb) {
cb(void 0, storeObj.drive[key]);
};
var safeRemove = function (key) { var safeRemove = function (key) {
delete storeObj[key]; delete storeObj[key];
}; };
@ -98,6 +107,10 @@ define([
return exp; return exp;
}; };
Store.getLoginName = function () {
return storeObj.login_name;
};
var changeHandlers = Store.changeHandlers = []; var changeHandlers = Store.changeHandlers = [];
Store.change = function (f) { Store.change = function (f) {
@ -123,7 +136,7 @@ define([
}; };
var onReady = function (f, proxy, storageKey) { var onReady = function (f, proxy, storageKey) {
filesOp = FO.init(proxy, { filesOp = FO.init(proxy.drive, {
storageKey: storageKey storageKey: storageKey
}); });
storeObj = proxy; storeObj = proxy;
@ -138,7 +151,10 @@ define([
var init = function (f, Cryptpad) { var init = function (f, Cryptpad) {
if (!Cryptpad || initialized) { return; } if (!Cryptpad || initialized) { return; }
initialized = true; initialized = true;
var hash = Cryptpad.getUserHash() || localStorage.FS_hash; var hash = Cryptpad.getUserHash() || localStorage.FS_hash || Cryptpad.createRandomHash();
if (!hash) {
throw new Error('[Store.init] Unable to find or create a drive hash. Aborting...');
}
var secret = Cryptpad.getSecrets(hash); var secret = Cryptpad.getSecrets(hash);
var listmapConfig = { var listmapConfig = {
data: {}, data: {},
@ -151,19 +167,34 @@ define([
logLevel: 1, logLevel: 1,
}; };
window.addEventListener('storage', function (e) {
var key = e.key;
if (e.key !== Cryptpad.userHashKey) { return; }
var o = e.oldValue;
var n = e.newValue;
if (!o && n) {
window.location.reload();
} else if (o && !n) {
window.location.reload();
}
});
var rt = window.rt = Listmap.create(listmapConfig); var rt = window.rt = Listmap.create(listmapConfig);
exp.proxy = rt.proxy; exp.proxy = rt.proxy;
rt.proxy.on('create', function (info) { rt.proxy.on('create', function (info) {
exp.info = info; exp.info = info;
var realtime = info.realtime;
if (!Cryptpad.getUserHash()) { if (!Cryptpad.getUserHash()) {
localStorage.FS_hash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); localStorage.FS_hash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
} }
}).on('ready', function () { }).on('ready', function () {
if (!rt.proxy[Cryptpad.storageKey] || !Cryptpad.isArray(rt.proxy[Cryptpad.storageKey])) { if (ready) { return; }
if (!rt.proxy.drive || typeof(rt.proxy.drive) !== 'object') { rt.proxy.drive = {}; }
var drive = rt.proxy.drive;
// Creating a new anon drive: import anon pads from localStorage
if (!drive[Cryptpad.storageKey] || !Cryptpad.isArray(drive[Cryptpad.storageKey])) {
var oldStore = Cryptpad.getStore(true); var oldStore = Cryptpad.getStore(true);
oldStore.get(Cryptpad.storageKey, function (err, s) { oldStore.get(Cryptpad.storageKey, function (err, s) {
rt.proxy[Cryptpad.storageKey] = s; drive[Cryptpad.storageKey] = s;
onReady(f, rt.proxy, Cryptpad.storageKey); onReady(f, rt.proxy, Cryptpad.storageKey);
}); });
return; return;

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

@ -3,8 +3,12 @@
<head> <head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title> <title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/customize/main.css" /> <link rel="stylesheet" type="text/css" href="/customize/main.css" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/> <link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<script data-main="/customize/main" src="/bower_components/requirejs/require.js"></script> <script data-main="/customize/main" src="/bower_components/requirejs/require.js"></script>
<script src="/bower_components/requirejs/require.js"></script> <script src="/bower_components/requirejs/require.js"></script>
<script> <script>
@ -13,18 +17,30 @@
}); });
</script> </script>
</head> </head>
<body> <body class="html">
<a data-localization-title="github_ribbon" href="https://github.com/xwiki-labs/cryptpad" class="github-corner" aria-label="View source on Github"><svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 0; border: 0; left: 0; transform: scale(-1, 1);" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style> <div id="cryptpadTopBar">
<!-- Thanks! http://tholman.com/github-corners/ --> <span>
<a class="gotoMain" href="/">
<img src="customize/cryptofist_mini.png" class="cryptpad-logo" alt="" /> CryptPad
</a>
<select id="language-selector" style="display: none;"></select> </span>
<div id="main"> <!--<span class="slogan" data-localization="main_slogan"></span>-->
<center>
<a href="/"><img class="imgcenter cryptofist" src="/customize/cryptofist_small.png" /></a> <span id="language-selector" class="right dropdown-bar"></span>
</center> <span class="right">
<a href="/about.html" data-localization="about">About</a>
</span>
<span class="right">
<a href="/privacy.html" data-localization="privacy">Privacy</a>
</span>
<span class="right">
<a href="/terms.html" data-localization="terms">ToS</a>
</span>
<span class="right">
<a href="/contact.html" data-localization="contact">Contact</a>
</span>
</div>
<center> <center>
@ -41,50 +57,158 @@
<center> <a data-localization-title="github_ribbon" href="https://github.com/xwiki-labs/cryptpad" class="github-corner" aria-label="View source on Github"><svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 50px; border: 0; left: 0; transform: scale(-1, 1);z-index:2;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<h1 data-localization="main_slogan"></h1> <!-- Thanks! http://tholman.com/github-corners/ -->
</center>
<p data-localization="main_p1"><!-- Zero Knowledge collaborative realtime editor. Protected from the NSA. --></p>
<center>
<h5 id="tryit" data-localization="tryIt"></h5>
<!--
(<a href="/file" target="_blank" data-localization="main_openFileManager">Open in a new tab</a>)
<iframe src="/file/#iframe" id="fileManagerIframe"></iframe>
<br />
-->
<table class="recent scroll" style="display:none">
<tbody>
<tr> <div id="main">
<th data-localization="table_type"></th> <div id="overlay"></div>
<th data-localization="table_link"></th> <div id="main-container">
<th data-localization="table_created"></th> <div id="data">
<th data-localization="table_last"></th> <p class="left" data-localization="main_p1"><!-- Zero Knowledge collaborative realtime editor. Protected from the NSA. --></p>
<th></th>
</tr>
</tbody> <div id="buttons" class="buttons">
</table> <button class="btn btn-secondary knowmore">Know more</button>
<button class="btn btn-secondary tryit">Discover our tools</button>
<br>
<button class="btn btn-success nologin" data-localization="login_nologin"></button>
</div>
<div id="buttons" class="buttons">
<a id="create-pad" class="button create" href="/pad/" data-localization="button_newpad"></a>
<a id="create-code" class="button create" href="/code/" data-localization="button_newcode"></a>
<a id="create-poll" class="button create" href="/poll/" data-localization="button_newpoll"></a>
<a id="create-slide" class="button create" href="/slide/" data-localization="button_newslide"></a>
</div> </div>
</center> <div id="userForm" class="form-group">
<!--<center>
<h1 data-localization="form_title"></h1>
</center>-->
<!--<label for="name" data-localization="form_username"></label>-->
<input type="text" id="name" name="name" class="form-control" data-localization-placeholder="login_username" autofocus><br>
<!--<label for="password" data-localization="form_password"></label>-->
<input type="password" id="password" name="password" class="form-control" data-localization-placeholder="login_password"><br>
<span class="remember form-check"><label for="rememberme" class="form-check-label" data-localization-append="login_remember"><input type="checkbox" id="rememberme" class="form-check-input" checked="checked"></label></span><br>
<button class="btn btn-secondary login half first" data-localization="login_login"></button> <button class="btn btn-primary register half" data-localization="login_register"></button><br>
</div>
</div>
</div>
<div class="page category first" id="knowmore">
<center>
<h1>Know more</h1>
</center>
</div>
<div class="page">
<div class="info-container">
<div class="left image">
<img src="customize/images/zk.png" alt="Zero Knowledge" />
</div>
<div class="right">
<h2>Zero Knowledge</h2>
<p>
Every tool provided by Cryptpad are based on a Zero Knowledge technology. It means that our server stored only encrypted data, and we have no way to decrypt it. Even data about your account, including your username, are encrypted.<!-- Only people with whom you have shared the URL are allowed to decrypt the data and join the collaborative session. The encryption is carried out by your browser, which means that our server has no knowledge of the encryption key.-->
</p>
</div>
</div>
</div>
<div class="page even">
<div class="info-container">
<div class="left">
<h2>Realtime</h2>
<p>
Our tools also use a Realtime technology, which means that you can edit the documents, slides or polls collaboratively with other people. Our unique technology has the advantage of combining both realtime and zero-knowledge at the same time.<!-- Other realtime solutions work with a main server containing the document and managing conflicts (when two users want to realize contradictory operations). Our Zero Knowledge principle exclude that solution since the server doesn't know the content of the document, so the conflicts are managed by your browser in a way that all users in the session resolve the conflict with the same result.-->
</p>
</div>
<div class="right image">
<img src="customize/images/realtime.png" alt="User account" />
</div>
</div>
</div>
<div class="page">
<div class="info-container">
<div class="left image">
<img src="customize/images/hash.png" alt="User account" />
</div>
<div class="right">
<h2>Share documents</h2>
<p>
When you want to edit a document with other users or invite them to answer to a poll, you just have to share the URL of that document with them. All the information needed to access and edit it are contained in the "hash" of the URL.<!-- A first part of that hash represents the location of the data in our server, the second part allows your browser to decrypt the content of that document and encrypt new modifications you may want to make.-->
</p>
</div>
</div>
</div>
<div class="page even">
<div class="info-container">
<div class="left">
<h2>User Account</h2>
<p>
In order to have Zero Knowledge user accounts, we needed a way to store your settings without knowing your username and password. To do so, our tools generate a unique key from your username and password, directly in your browser.<!-- The entire key is never sent to our server and there is no way to determine either your username or your password from it. A small part of that key is used to locate your account data in our server, and the other part is used to encrypt and decrypt it (in your browser). That way, the server never has access to your decrypted data or to your decryption key.-->
</p>
</div>
<div class="right image">
<img src="customize/images/useraccount.png" alt="User account" />
</div>
</div>
</div>
<div class="page category" id="tryit">
<center>
<h1 data-localization="tryIt">Try it out!</h1>
</center>
</div>
<div class="page">
<div class="app-container">
<div class="app-row">
<div class="app">
<center>
<h2>Rich Text editor</h2>
<img src="customize/images/pad.png" alt="Rich Text application" />
</center>
<p>
Edit rich text documents collaboratively with our realtime Zero Knowledge <a href="http://ckeditor.com" target="_blank">CkEditor</a> application.
</p>
<p class="buttons">
<a id="create-pad" href="/pad/"><button class="btn btn-secondary" data-localization="button_newpad"></button></a>
</p>
</div><div class="app">
<center>
<h2>Code editor</h2>
<img src="customize/images/code.png" alt="Code application" />
</center>
<p>
Edit code from your software collaboratively with our realtime Zero Knowledge <a href="https://www.codemirror.net" target="_blank">CodeMirror</a> application.
</p>
<p class="buttons">
<a id="create-code" href="/code/"><button class="btn btn-secondary" data-localization="button_newcode"></button></a>
</p>
</div><!--
--></div><!--
--><div class="app-row"><!--
--><div class="app">
<center>
<h2>Slide editor</h2>
<img src="customize/images/slide.png" alt="Slide applcation" />
</center>
<p>
Create your presentations using the Markdown syntax with our CryptSlide application, and display them in your browser.
</p>
<p class="buttons">
<a id="create-slide" href="/slide/"><button class="btn btn-secondary" data-localization="button_newslide"></button></a>
</p>
</div><div class="app">
<center>
<h2>Polls</h2>
<img src="customize/images/poll.png" alt="Poll application" />
</center>
<p>
Plan your meeting or your event, or vote for the best solution regarding your problem using our poll application.
</p>
<p class="buttons">
<a id="create-poll" href="/poll/"><button class="btn btn-secondary" data-localization="button_newpoll"></button></a>
</p>
</div>
</div>
</div>
</div>
<p data-localization="main_about_p1"><!-- Privacy policy, terms of service --></p>
<p data-localization="main_about_p2"><!-- Contact us--></p>
</div>
</body> </body>
</html> </html>

@ -22,27 +22,28 @@ define(['/bower_components/jquery/dist/jquery.min.js'], function() {
var main = out.main = function ($select) { var main = out.main = function ($select) {
var selector = $select || $('#language-selector'); var selector = $select || $('#language-selector');
if (!selector.length) { return; } if (!selector.length) { return; }
var $button = $(selector).find('button .buttonTitle');
// Select the current language in the list // Select the current language in the list
var language = getLanguage(); var language = getLanguage();
var option = $(selector).find('option[value="' + language + '"]'); var option = $(selector).find('[data-value="' + language + '"]');
if ($(option).length) { if ($(option).length) {
$(selector).val(language); $button.text($(option).text());
} }
else { else {
$(selector).val('en'); $button.text('English');
} }
// Listen for language change // Listen for language change
$(selector).on('change', function () { $(selector).find('a.languageValue').on('click', function () {
var newLanguage = $(selector).val(); var newLanguage = $(this).attr('data-value');
storeLanguage(newLanguage); storeLanguage(newLanguage);
if (newLanguage !== language) { if (newLanguage !== language) {
window.location.reload(); window.location.reload();
} }
}); });
}; };
return out; return out;

@ -14,15 +14,16 @@
background: rgba(0, 0, 0, 0.8); background: rgba(0, 0, 0, 0.8);
} }
.alertify-logs > *.error { .alertify-logs > *.error {
background: #FF0073; background: #FA5858;
} }
.alertify-logs > *.success { .alertify-logs > *.success {
background: #46E981; background: #46E981;
color: #302B28; color: #fff;
} }
.alertify { .alertify {
position: fixed; position: fixed;
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
color: #fafafa;
left: 0; left: 0;
right: 0; right: 0;
top: 0; top: 0;
@ -79,8 +80,8 @@
} }
.alertify .dialog input:not(.form-control), .alertify .dialog input:not(.form-control),
.alertify .alert input:not(.form-control) { .alertify .alert input:not(.form-control) {
background-color: #302B28; background-color: #fff;
color: #fafafa; color: #555;
border: 0px; border: 0px;
border-radius: 5px; border-radius: 5px;
margin-bottom: 15px; margin-bottom: 15px;
@ -210,7 +211,7 @@
height: 2.5em; height: 2.5em;
display: inline-block; display: inline-block;
width: 100%; width: 100%;
background: #302B28; background: #fff;
border-top: 1px solid #444; border-top: 1px solid #444;
} }
.top-bar a, .top-bar a,
@ -226,7 +227,7 @@
display: block; display: block;
margin-left: 10px; margin-left: 10px;
padding-top: 3px; padding-top: 3px;
color: #fafafa; color: #555;
} }
.top-bar img, .top-bar img,
.bottom-bar img { .bottom-bar img {
@ -306,6 +307,7 @@
left: 0px; left: 0px;
right: 0px; right: 0px;
background: #302B28; background: #302B28;
color: #fafafa;
text-align: center; text-align: center;
font-size: 1.5em; font-size: 1.5em;
} }
@ -329,19 +331,153 @@
.cp #loading .spinnerContainer > div { .cp #loading .spinnerContainer > div {
height: 100px; height: 100px;
} }
/* The container <div> - needed to position the dropdown content */
.dropdown-bar {
position: relative;
display: inline-block;
}
.dropdown-bar .fa {
font-family: FontAwesome;
}
.dropdown-bar button .fa-caret-down {
margin-left: 5px;
}
.dropdown-bar .dropdown-bar-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 200px;
box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2);
z-index: 1000;
max-height: 300px;
overflow-y: auto;
}
.dropdown-bar .dropdown-bar-content.left {
right: 0;
}
.dropdown-bar .dropdown-bar-content:hover {
display: block;
}
.dropdown-bar .dropdown-bar-content a {
color: black;
padding: 5px 16px;
text-decoration: none;
display: block;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
float: none;
text-align: left;
}
.dropdown-bar .dropdown-bar-content a:hover {
background-color: #f1f1f1;
}
.dropdown-bar .dropdown-bar-content hr {
margin: 5px 0px;
height: 1px;
background: #bbb;
}
.dropdown-bar .dropdown-bar-content p {
min-width: 160px;
padding: 5px;
margin: 0;
white-space: normal;
text-align: left;
}
.dropdown-bar .dropdown-bar-content p.cryptpad-dropdown-users {
text-align: baseline;
}
.dropdown-bar .dropdown-bar-content p.cryptpad-dropdown-users .yourself,
.dropdown-bar .dropdown-bar-content p.cryptpad-dropdown-users .anonymous,
.dropdown-bar .dropdown-bar-content p.cryptpad-dropdown-users .viewer {
font-style: italic;
}
.dropdown-bar .dropdown-bar-content p h2 {
font-weight: bold;
text-align: center;
background-color: #EEEEEE;
padding: 5px 0px;
margin: 5px 0px;
font-size: 16px;
white-space: normal;
}
#cryptpadTopBar {
background: #fff;
position: relative;
top: 0;
left: 0;
right: 0;
height: 50px;
color: #000;
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
padding: 5px;
box-sizing: border-box;
font-size: 30px;
}
#cryptpadTopBar > span {
vertical-align: middle;
display: inline-block;
height: 100%;
}
#cryptpadTopBar .cryptpad-logo {
height: 40px;
vertical-align: middle;
}
#cryptpadTopBar .slogan {
font-size: 20px;
color: #000;
line-height: 40px;
}
#cryptpadTopBar .gotoMain {
color: #000;
height: 40px;
line-height: 40px;
}
#cryptpadTopBar .gotoMain:hover {
text-decoration: none;
color: #558;
}
#cryptpadTopBar .right {
float: right;
font-size: 20px;
margin: 0px 10px;
line-height: 40px;
}
#cryptpadTopBar .right a {
font-weight: 500;
font-size: 0.75em;
color: #558;
/* text-align: center;
min-width: 100px;
font-weight: bold;
height: 70px;
padding: 0 10px;
line-height: 70px;
display: inline-block;
color: @topbar-button-color;*/
}
#cryptpadTopBar .right a:hover {
text-decoration: none;
color: #000;
}
html.cp, html.cp,
.cp body { .cp body {
font-size: .875em; font-size: .875em;
background-color: #302B28; background-color: #fff;
color: #fafafa; color: #555;
font-family: Georgia,Cambria,serif; font-family: Georgia,Cambria,serif;
height: 100; height: 100%;
} }
.cp:not(.poll) #language-selector { .cp:not(.poll) {
position: absolute; /*#language-selector {
top: 0px; position: absolute;
right: 0px; top: @topbar-height;
display: inline-block; right: 0px;
display: inline-block;
z-index: 2;
}*/
} }
.cp { .cp {
/* buttons */ /* buttons */
@ -353,6 +489,7 @@ html.cp,
font-size: 1rem; font-size: 1rem;
font-weight: 400; font-weight: 400;
line-height: 2rem; line-height: 2rem;
margin: 0;
} }
.cp a.github-corner > svg { .cp a.github-corner > svg {
fill: #00ADEE; fill: #00ADEE;
@ -368,7 +505,7 @@ html.cp,
.cp h4, .cp h4,
.cp h5, .cp h5,
.cp h6 { .cp h6 {
color: #fafafa; color: #555;
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
-webkit-font-feature-settings: 'dlig' 1, 'liga' 1, 'lnum' 1, 'kern' 1; -webkit-font-feature-settings: 'dlig' 1, 'liga' 1, 'lnum' 1, 'kern' 1;
-moz-font-feature-settings: 'dlig' 1, 'liga' 1, 'lnum' 1, 'kern' 1; -moz-font-feature-settings: 'dlig' 1, 'liga' 1, 'lnum' 1, 'kern' 1;
@ -414,11 +551,11 @@ html.cp,
} }
.cp a { .cp a {
cursor: pointer; cursor: pointer;
color: #46E981; color: #3333ff;
text-decoration: none; text-decoration: none;
} }
.cp a:hover { .cp a:hover {
color: #a1f4bf; color: #0000cc;
} }
.cp img { .cp img {
height: auto; height: auto;
@ -441,28 +578,311 @@ html.cp,
font-family: lato, Helvetica, sans-serif; font-family: lato, Helvetica, sans-serif;
font-size: 1.02em; font-size: 1.02em;
} }
.cp .page {
width: 100%;
margin-left: auto;
margin-right: auto;
background: #fff;
padding: 10px 0;
position: relative;
}
.cp .page .info-container {
width: 900px;
max-width: 100%;
margin: 0 auto;
}
.cp .page .info-container > div {
padding: 10px;
width: 400px;
max-width: 100%;
position: relative;
display: inline-block;
vertical-align: middle;
}
@media screen and (max-width: 800px) {
.cp .page .info-container > div:not(.image) {
width: 100%;
left: 0;
}
}
.cp .page .info-container > div.image {
text-align: center;
}
@media screen and (max-width: 800px) {
.cp .page .info-container > div.image {
display: none;
}
}
@media screen and (max-width: 800px) {
}
.cp .page.category {
background: #f7f7f7;
}
.cp .page .app {
display: inline-block;
width: 300px;
vertical-align: middle;
margin: 0px 25px;
white-space: normal;
max-width: calc(50% - 50px);
}
@media screen and (max-width: 500px) {
.cp .page .app {
display: block;
max-width: 100%;
margin: 0 auto;
}
}
.cp .page .app-container {
width: 1400px;
max-width: 100%;
margin: 0 auto;
}
.cp .page .app-row {
display: inline-block;
white-space: nowrap;
width: 700px;
max-width: 100%;
margin: 0 auto;
}
@media screen and (max-width: 1399px) {
.cp .page .app-row {
display: block;
}
}
@media screen and (max-width: 800px) {
.cp .page .app-row img {
display: none;
}
}
.cp .page .right {
left: 100px;
}
.cp .page h1,
.cp .page h2,
.cp .page h3,
.cp .page h4,
.cp .page h5,
.cp .page h6 {
padding: 0;
}
@media screen and (max-width: 800px) {
.cp .page {
padding: 10px 5vh;
}
}
.cp .page p {
font-size: 18px;
text-align: justify;
}
.cp .btn-default:hover {
background-color: #d8d8d8;
}
.cp #main { .cp #main {
width: 70vw; background-image: url('/customize/bg3.jpg');
background-size: cover;
background-position: center center;
}
.cp #main_other {
padding: 0 15vw;
}
.cp #main,
.cp #main_other {
position: relative;
left: 0;
right: 0;
height: calc(100vh - 150px);
min-height: 450px;
margin: auto; margin: auto;
font-size: medium; font-size: medium;
padding-bottom: 1em; }
.cp #main #overlay,
.cp #main_other #overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #000;
opacity: 0.3;
}
.cp #main #main-container,
.cp #main_other #main-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%);
margin-left: auto;
margin-right: auto;
display: inline-block;
width: 1000px;
max-width: 90%;
}
.cp #main #data,
.cp #main_other #data {
width: 600px;
max-width: 60%;
color: #fff;
padding: 15px;
box-sizing: border-box;
position: absolute;
display: inline-block;
top: 50%;
left: 0;
transform: translateY(-50%);
}
.cp #main #data p,
.cp #main_other #data p {
margin: 0;
padding: 0;
font-size: 20px;
line-height: 1.5em;
}
.cp #main #data h2,
.cp #main_other #data h2 {
font-weight: normal;
font-size: 48px;
line-height: 1.2em;
color: #fff;
}
.cp #main #data h5,
.cp #main_other #data h5 {
font-size: 1em;
color: #fff;
}
.cp #main #data #tryit,
.cp #main_other #data #tryit {
margin-top: 20px;
margin-bottom: 5px;
}
.cp #main #userForm,
.cp #main_other #userForm {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
margin-left: 50px;
display: inline-block;
width: 350px;
max-width: 35%;
padding: 10px;
box-sizing: border-box;
font-family: lato, Helvetica, sans-serif;
color: #fff;
/*.remember {
vertical-align: middle;
line-height: 28px;
height: 28px;
display: inline-block;
margin: 10px 0 20px 0;
}
[type="checkbox"] {
vertical-align: text-top;
margin: 0;
//margin-top: 10px;
margin-right: 5px;
}
*/
}
.cp #main #userForm h1,
.cp #main_other #userForm h1 {
color: #fff;
padding: 0;
}
.cp #main #userForm [type="text"],
.cp #main_other #userForm [type="text"],
.cp #main #userForm [type="password"],
.cp #main_other #userForm [type="password"] {
width: 100%;
}
.cp #main #userForm label,
.cp #main_other #userForm label {
margin-bottom: 0;
}
.cp #main #userForm [type="checkbox"],
.cp #main_other #userForm [type="checkbox"] {
margin-right: 5px;
}
.cp #main #userForm button,
.cp #main_other #userForm button {
font-weight: bold;
width: 100%;
margin: 2px 0px;
cursor: pointer;
}
.cp #main #userForm button.half,
.cp #main_other #userForm button.half {
width: calc(50% - 4px);
}
.cp #main #userForm button.half:not(.first),
.cp #main_other #userForm button.half:not(.first) {
float: right;
}
@media screen and (max-width: 800px) {
.cp #main #main-container,
.cp #main_other #main-container {
transform: initial;
position: relative;
display: block;
width: 90%;
left: 0;
}
}
@media screen and (max-width: 800px) {
.cp #main #userForm,
.cp #main_other #userForm,
.cp #main #data,
.cp #main_other #data {
transform: initial;
position: relative;
display: block;
width: 100%;
max-width: 100%;
margin: 10px 0;
box-sizing: border-box;
}
}
@media screen and (max-width: 800px) {
.cp #main #userForm,
.cp #main_other #userForm {
border: 1px solid #888;
}
}
@media screen and (max-width: 800px) {
.cp #main,
.cp #main_other {
position: relative;
height: auto;
top: -10px;
}
}
.cp #main .buttons,
.cp #main_other .buttons {
margin-top: 15px;
}
.cp p.buttons,
.cp div.buttons {
text-align: center;
}
.cp p.buttons button,
.cp div.buttons button {
font-weight: bold;
cursor: pointer;
margin-top: 10px;
} }
.cp #fileManagerIframe { .cp #fileManagerIframe {
width: 100%; width: 100%;
height: 500px; height: 500px;
margin-top: 15px; margin-top: 15px;
} }
.cp .buttons {
margin-bottom: 50px;
margin-top: 20px;
line-height: 2.5em;
}
.cp .create, .cp .create,
.cp .action { .cp .action {
display: inline-block; display: inline-block;
border: 2px solid #46E981; border: 0;
background-color: #302B28; background-color: #3333ff;
color: #46E981; color: #fff;
font-weight: bold; font-weight: bold;
font-size: large; font-size: large;
margin-right: 5px; margin-right: 5px;
@ -470,18 +890,16 @@ html.cp,
} }
.cp .create:hover, .cp .create:hover,
.cp .action:hover { .cp .action:hover {
border: 2px solid #a1f4bf; color: #cccccc;
color: #46E981;
} }
.cp .button { .cp .button {
padding: 4px 12px 4px 12px; padding: 4px 12px 4px 12px;
margin-top: 12px; margin: 2px 0;
margin-bottom: 12px;
display: inline-block; display: inline-block;
line-height: 1.5em; line-height: 1.5em;
} }
.cp .panel { .cp .panel {
background-color: #333; background-color: #cccccc;
} }
.cp table { .cp table {
border-collapse: collapse; border-collapse: collapse;
@ -489,7 +907,7 @@ html.cp,
margin: 20px; margin: 20px;
} }
.cp tbody { .cp tbody {
border: 2px solid black; border: 1px solid #555;
} }
.cp tbody tr { .cp tbody tr {
text-align: center; text-align: center;
@ -507,18 +925,18 @@ html.cp,
cursor: pointer; cursor: pointer;
} }
.cp tbody tr:nth-child(odd) { .cp tbody tr:nth-child(odd) {
background-color: #685d56; background-color: #ffffff;
} }
.cp tbody tr th:first-of-type { .cp tbody tr th:first-of-type {
border-left: 0px; border-left: 0px;
} }
.cp tbody tr th { .cp tbody tr th {
box-sizing: border-box; box-sizing: border-box;
border: 1px solid black; border: 1px solid #555;
} }
.cp tbody tr th, .cp tbody tr th,
.cp tbody tr td { .cp tbody tr td {
color: #fafafa; color: #555;
} }
.cp tbody tr th.remove, .cp tbody tr th.remove,
.cp tbody tr td.remove { .cp tbody tr td.remove {
@ -528,7 +946,7 @@ html.cp,
border-right: 0px; border-right: 0px;
} }
.cp tbody td { .cp tbody td {
border-right: 1px solid black; border-right: 1px solid #555;
padding: 12px; padding: 12px;
padding-top: 0px; padding-top: 0px;
padding-bottom: 0px; padding-bottom: 0px;
@ -543,7 +961,7 @@ html.cp,
border-top-left-radius: 5px; border-top-left-radius: 5px;
} }
.cp .remove { .cp .remove {
color: #FF0073; color: #FA5858;
cursor: pointer !important; cursor: pointer !important;
} }
.cp form.realtime, .cp form.realtime,
@ -589,7 +1007,7 @@ html.cp,
.cp form.realtime table tr td div.text-cell input[disabled], .cp form.realtime table tr td div.text-cell input[disabled],
.cp div.realtime table tr td div.text-cell input[disabled] { .cp div.realtime table tr td div.text-cell input[disabled] {
background-color: transparent; background-color: transparent;
color: #fafafa; color: #000;
font-weight: bold; font-weight: bold;
} }
.cp form.realtime table tr td.checkbox-cell, .cp form.realtime table tr td.checkbox-cell,
@ -623,8 +1041,8 @@ html.cp,
.cp form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover, .cp form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover,
.cp div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover { .cp div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover {
font-weight: bold; font-weight: bold;
background-color: #FF0073; background-color: #FA5858;
color: #302B28; color: #000;
display: block; display: block;
} }
.cp form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover:after, .cp form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover:after,
@ -654,13 +1072,13 @@ html.cp,
.cp form.realtime table input[type="text"], .cp form.realtime table input[type="text"],
.cp div.realtime table input[type="text"] { .cp div.realtime table input[type="text"] {
height: auto; height: auto;
border: 1px solid #302B28; border: 1px solid #fff;
width: 80%; width: 80%;
} }
.cp form.realtime table thead td, .cp form.realtime table thead td,
.cp div.realtime table thead td { .cp div.realtime table thead td {
padding: 0px 5px; padding: 0px 5px;
background: #4c443f; background: #aaa;
border-radius: 20px 20px 0 0; border-radius: 20px 20px 0 0;
text-align: center; text-align: center;
} }
@ -671,13 +1089,13 @@ html.cp,
} }
.cp form.realtime table thead td input[type="text"][disabled], .cp form.realtime table thead td input[type="text"][disabled],
.cp div.realtime table thead td input[type="text"][disabled] { .cp div.realtime table thead td input[type="text"][disabled] {
color: white; color: #000;
padding: 1px 5px; padding: 1px 5px;
border: none; border: none;
} }
.cp form.realtime table tbody .text-cell, .cp form.realtime table tbody .text-cell,
.cp div.realtime table tbody .text-cell { .cp div.realtime table tbody .text-cell {
background: #4c443f; background: #aaa;
} }
.cp form.realtime table tbody .text-cell input[type="text"], .cp form.realtime table tbody .text-cell input[type="text"],
.cp div.realtime table tbody .text-cell input[type="text"] { .cp div.realtime table tbody .text-cell input[type="text"] {
@ -693,6 +1111,10 @@ html.cp,
float: left; float: left;
margin: 0 0 0 10px; margin: 0 0 0 10px;
} }
.cp form.realtime table tbody td label,
.cp div.realtime table tbody td label {
border: 0.5px solid #555;
}
.cp form.realtime table .edit, .cp form.realtime table .edit,
.cp div.realtime table .edit { .cp div.realtime table .edit {
color: #46E981; color: #46E981;
@ -710,7 +1132,7 @@ html.cp,
.cp form.realtime table thead tr th input[type="text"][disabled], .cp form.realtime table thead tr th input[type="text"][disabled],
.cp div.realtime table thead tr th input[type="text"][disabled] { .cp div.realtime table thead tr th input[type="text"][disabled] {
background-color: transparent; background-color: transparent;
color: #fafafa; color: #555;
font-weight: bold; font-weight: bold;
} }
.cp form.realtime table thead tr th .remove, .cp form.realtime table thead tr th .remove,
@ -750,6 +1172,29 @@ html.cp,
.cp div.realtime #addoption { .cp div.realtime #addoption {
border-bottom-left-radius: 5px; border-bottom-left-radius: 5px;
} }
.cp.slide #modal .button {
position: absolute;
cursor: pointer;
font-size: 30px;
opacity: 0.6;
display: none;
}
.cp.slide #modal .button:hover {
opacity: 1;
display: block !important;
}
.cp.slide #modal #button_exit {
left: 20px;
top: 20px;
}
.cp.slide #modal #button_left {
left: 6vw;
bottom: 10vh;
}
.cp.slide #modal #button_right {
right: 6vw;
bottom: 10vh;
}
.cp.slide #modal #content p, .cp.slide #modal #content p,
.cp.slide #modal #content ul, .cp.slide #modal #content ul,
.cp.slide #modal #content ol { .cp.slide #modal #content ol {
@ -773,7 +1218,7 @@ html.cp,
width: 100%; width: 100%;
height: 100vh; height: 100vh;
display: none; display: none;
background-color: #302B28; background-color: #000;
} }
.cp div.modal #content, .cp div.modal #content,
.cp div#modal #content { .cp div#modal #content {
@ -840,6 +1285,20 @@ html.cp,
font-size: 1.6vw; font-size: 1.6vw;
line-height: 1.76vw; line-height: 1.76vw;
} }
.cp div.modal #content h1,
.cp div#modal #content h1,
.cp div.modal #content h2,
.cp div#modal #content h2,
.cp div.modal #content h3,
.cp div#modal #content h3,
.cp div.modal #content h4,
.cp div#modal #content h4,
.cp div.modal #content h5,
.cp div#modal #content h5,
.cp div.modal #content h6,
.cp div#modal #content h6 {
color: inherit;
}
.cp div.modal #content pre > code, .cp div.modal #content pre > code,
.cp div#modal #content pre > code { .cp div#modal #content pre > code {
display: block; display: block;
@ -864,7 +1323,7 @@ html.cp,
width: 80%; width: 80%;
height: 80%; height: 80%;
margin: auto; margin: auto;
border: 1px solid #685d56; border: 1px solid #ffffff;
text-align: center; text-align: center;
} }
.cp div.modal.shown, .cp div.modal.shown,
@ -880,7 +1339,7 @@ html.cp,
.cp div#modal table input { .cp div#modal table input {
height: 100%; height: 100%;
width: 90%; width: 90%;
border: 3px solid #302B28; border: 3px solid #fff;
} }
.cp div.modal table tfoot tr td, .cp div.modal table tfoot tr td,
.cp div#modal table tfoot tr td { .cp div#modal table tfoot tr td {

@ -33,22 +33,21 @@ define([
var displayCreateButtons = function () { var displayCreateButtons = function () {
var $parent = $('#buttons'); var $parent = $('#buttons');
Config.availablePadTypes.forEach(function (el) { Config.availablePadTypes.forEach(function (el) {
$('#create-' + el).detach().appendTo($parent).attr('target', '_blank').show(); $('#create-' + el)//.detach().appendTo($parent)
.attr('target', '_blank').show();
}); });
}; };
// Language selector
var $sel = $('#language-selector'); var $sel = $('#language-selector');
Cryptpad.createLanguageSelector(undefined, $sel);
$sel.find('button').addClass('btn').addClass('btn-secondary');
$sel.show();
Object.keys(Messages._languages).forEach(function (code) { $(window).click(function () {
$sel.append($('<option>', { $sel.find('.cryptpad-dropdown').hide();
value: code,
}).text(Messages._languages[code]));
}); });
LS.main();
Messages._applyTranslation();
$sel.show();
var makeRecentPadsTable = function (recentPads) { var makeRecentPadsTable = function (recentPads) {
if (!recentPads.length) { return; } if (!recentPads.length) { return; }
@ -158,17 +157,64 @@ define([
}); });
}; };
var addButtonHandlers = function () {
$('button.login').click(function (e) {
var username = $('#name').val();
var passwd = $('#password').val();
var remember = $('#rememberme').is(':checked');
sessionStorage.login_user = username;
sessionStorage.login_pass = passwd;
sessionStorage.login_rmb = remember;
sessionStorage.login = 1;
document.location.href = '/user';
});
$('button.register').click(function (e) {
var username = $('#name').val();
var passwd = $('#password').val();
var remember = $('#rememberme').is(':checked');
sessionStorage.login_user = username;
sessionStorage.login_pass = passwd;
sessionStorage.login_rmb = remember;
sessionStorage.register = 1;
document.location.href = '/user';
});
$('button.nologin').click(function (e) {
document.location.href = '/drive';
});
$('button.knowmore').click(function (e) {
e.preventDefault();
$('html, body').animate({
scrollTop: $('#knowmore').offset().top
}, 500);
});
$('button.tryit').click(function (e) {
e.preventDefault();
$('html, body').animate({
scrollTop: $('#tryit').offset().top
}, 500);
});
};
displayCreateButtons(); displayCreateButtons();
Cryptpad.ready(function () { //Cryptpad.ready(function () {
console.log("ready"); console.log("ready");
if (Cryptpad.isLoggedIn()) {
document.location.href = '/drive';
}
addButtonHandlers();
$table = $('table.scroll'); $table = $('table.scroll');
$tbody = $table.find('tbody'); $tbody = $table.find('tbody');
$tryit = $('#tryit'); $tryit = $('#tryit');
Cryptpad.styleAlerts(); Cryptpad.styleAlerts();
refreshTable(); //refreshTable();
/*
if (Cryptpad.store && Cryptpad.store.change) { if (Cryptpad.store && Cryptpad.store.change) {
Cryptpad.store.change(function (data) { Cryptpad.store.change(function (data) {
if (data.key === 'CryptPad_RECENTPADS') { if (data.key === 'CryptPad_RECENTPADS') {
@ -176,6 +222,7 @@ define([
} }
}); });
} }
}); */
//});
}); });

@ -81,6 +81,11 @@ define(['/customize/languageSelector.js',
var key = $el.data('localization'); var key = $el.data('localization');
$el.html(messages[key]); $el.html(messages[key]);
}; };
var translateAppend = function (i, e) {
var $el = $(e);
var key = $el.data('localization-append');
$el.append(messages[key]);
};
var translateTitle = function (i, e) { var translateTitle = function (i, e) {
var $el = $(this); var $el = $(this);
var key = $el.data('localization-title'); var key = $el.data('localization-title');
@ -93,6 +98,7 @@ define(['/customize/languageSelector.js',
}; };
messages._applyTranslation = function () { messages._applyTranslation = function () {
$('[data-localization]').each(translateText); $('[data-localization]').each(translateText);
$('[data-localization-append]').each(translateAppend);
$('#pad-iframe').contents().find('[data-localization]').each(translateText); $('#pad-iframe').contents().find('[data-localization]').each(translateText);
$('[data-localization-title]').each(translateTitle); $('[data-localization-title]').each(translateTitle);
$('[data-localization-placeholder]').each(translatePlaceholder); $('[data-localization-placeholder]').each(translatePlaceholder);

@ -3,8 +3,12 @@
<head> <head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title> <title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/customize/main.css" /> <link rel="stylesheet" type="text/css" href="/customize/main.css" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/> <link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<script data-main="/customize/main" src="/bower_components/requirejs/require.js"></script> <script data-main="/customize/main" src="/bower_components/requirejs/require.js"></script>
<script src="/bower_components/requirejs/require.js"></script> <script src="/bower_components/requirejs/require.js"></script>
<script> <script>
@ -13,18 +17,30 @@
}); });
</script> </script>
</head> </head>
<body> <body class="html">
<a data-localization-title="github_ribbon" href="https://github.com/xwiki-labs/cryptpad" class="github-corner" aria-label="View source on Github"><svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 0; border: 0; left: 0; transform: scale(-1, 1);" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style> <div id="cryptpadTopBar">
<!-- Thanks! http://tholman.com/github-corners/ --> <span>
<a class="gotoMain" href="/">
<img src="customize/cryptofist_mini.png" class="cryptpad-logo" alt="" /> CryptPad
</a>
<select id="language-selector" style="display: none;"></select> </span>
<div id="main"> <!--<span class="slogan" data-localization="main_slogan"></span>-->
<center>
<a href="/"><img class="imgcenter cryptofist" src="/customize/cryptofist_small.png" /></a> <span id="language-selector" class="right dropdown-bar"></span>
</center> <span class="right">
<a href="/about.html" data-localization="about">About</a>
</span>
<span class="right">
<a href="/privacy.html" data-localization="privacy">Privacy</a>
</span>
<span class="right">
<a href="/terms.html" data-localization="terms">ToS</a>
</span>
<span class="right">
<a href="/contact.html" data-localization="contact">Contact</a>
</span>
</div>
<center> <center>
@ -41,6 +57,7 @@
<div id="main_other">
<center> <center>
<h1 data-localization="policy_title"></h1> <h1 data-localization="policy_title"></h1>
</center> </center>
@ -67,11 +84,10 @@
<p data-localization="policy_choices_ads"></p> <p data-localization="policy_choices_ads"></p>
<br /> <br />
</div>
</div>
</body> </body>
</html> </html>

@ -24,7 +24,10 @@
onload(void 0, iframe, e); onload(void 0, iframe, e);
window.clearTimeout(to); window.clearTimeout(to);
}; };
iframe.setAttribute('src', src); // We must pass a unique parameter here to avoid cache problems in Firefox with
// the NoScript plugin: if the iframe's content is taken from the cache, the JS
// is not executed with NoScript....
iframe.setAttribute('src', src + '?t=' + new Date().getTime());
parent.appendChild(iframe); parent.appendChild(iframe);
}; };

@ -24,9 +24,11 @@ var template = read('./template.html');
var fragments = {}; var fragments = {};
[ 'index', [ 'index',
'fork', 'fork',
'topbar',
'terms', 'terms',
'privacy', 'privacy',
'about', 'about',
'contact',
'logo', 'logo',
'noscript', 'noscript',
].forEach(function (name) { ].forEach(function (name) {
@ -34,10 +36,16 @@ var fragments = {};
}); });
// build static pages // build static pages
['index', 'privacy', 'terms', 'about',].forEach(function (page) { ['index', 'privacy', 'terms', 'about', 'contact',].forEach(function (page) {
var source = swap(template, { var source = swap(template, {
topbar: fragments.topbar,
fork: fragments.fork, fork: fragments.fork,
main: fragments[page], main: swap(fragments[page], {
topbar: fragments.topbar,
fork: fragments.fork,
logo: fragments.logo,
noscript: fragments.noscript,
}),
logo: fragments.logo, logo: fragments.logo,
noscript: fragments.noscript, noscript: fragments.noscript,
}); });

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

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

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

@ -1,42 +1,148 @@
<center> {{fork}}
<h1 data-localization="main_slogan"></h1>
</center>
<p data-localization="main_p1"><!-- Zero Knowledge collaborative realtime editor. Protected from the NSA. --></p>
<center>
<h5 id="tryit" data-localization="tryIt"></h5>
<!-- <div id="main">
(<a href="/file" target="_blank" data-localization="main_openFileManager">Open in a new tab</a>) <div id="overlay"></div>
<div id="main-container">
<div id="data">
<p class="left" data-localization="main_p1"><!-- Zero Knowledge collaborative realtime editor. Protected from the NSA. --></p>
<iframe src="/file/#iframe" id="fileManagerIframe"></iframe> <div id="buttons" class="buttons">
<br /> <button class="btn btn-secondary knowmore">Know more</button>
--> <button class="btn btn-secondary tryit">Discover our tools</button>
<table class="recent scroll" style="display:none"> <br>
<tbody> <button class="btn btn-success nologin" data-localization="login_nologin"></button>
</div>
<tr> </div>
<th data-localization="table_type"></th>
<th data-localization="table_link"></th>
<th data-localization="table_created"></th>
<th data-localization="table_last"></th>
<th></th>
</tr>
</tbody> <div id="userForm" class="form-group">
</table> <!--<center>
<h1 data-localization="form_title"></h1>
</center>-->
<!--<label for="name" data-localization="form_username"></label>-->
<input type="text" id="name" name="name" class="form-control" data-localization-placeholder="login_username" autofocus><br>
<div id="buttons" class="buttons"> <!--<label for="password" data-localization="form_password"></label>-->
<a id="create-pad" class="button create" href="/pad/" data-localization="button_newpad"></a> <input type="password" id="password" name="password" class="form-control" data-localization-placeholder="login_password"><br>
<a id="create-code" class="button create" href="/code/" data-localization="button_newcode"></a> <span class="remember form-check"><label for="rememberme" class="form-check-label" data-localization-append="login_remember"><input type="checkbox" id="rememberme" class="form-check-input" checked="checked"></label></span><br>
<a id="create-poll" class="button create" href="/poll/" data-localization="button_newpoll"></a> <button class="btn btn-secondary login half first" data-localization="login_login"></button> <button class="btn btn-primary register half" data-localization="login_register"></button><br>
<a id="create-slide" class="button create" href="/slide/" data-localization="button_newslide"></a>
</div> </div>
</div>
</div>
<div class="page category first" id="knowmore">
<center>
<h1>Know more</h1>
</center>
</div>
<div class="page">
<div class="info-container">
<div class="left image">
<img src="customize/images/zk.png" alt="Zero Knowledge" />
</div>
<div class="right">
<h2>Zero Knowledge</h2>
<p>
Every tool provided by Cryptpad are based on a Zero Knowledge technology. It means that our server stored only encrypted data, and we have no way to decrypt it. Even data about your account, including your username, are encrypted.<!-- Only people with whom you have shared the URL are allowed to decrypt the data and join the collaborative session. The encryption is carried out by your browser, which means that our server has no knowledge of the encryption key.-->
</p>
</div>
</div>
</div>
<div class="page even">
<div class="info-container">
<div class="left">
<h2>Realtime</h2>
<p>
Our tools also use a Realtime technology, which means that you can edit the documents, slides or polls collaboratively with other people. Our unique technology has the advantage of combining both realtime and zero-knowledge at the same time.<!-- Other realtime solutions work with a main server containing the document and managing conflicts (when two users want to realize contradictory operations). Our Zero Knowledge principle exclude that solution since the server doesn't know the content of the document, so the conflicts are managed by your browser in a way that all users in the session resolve the conflict with the same result.-->
</p>
</div>
<div class="right image">
<img src="customize/images/realtime.png" alt="User account" />
</div>
</div>
</div>
<div class="page">
<div class="info-container">
<div class="left image">
<img src="customize/images/hash.png" alt="User account" />
</div>
<div class="right">
<h2>Share documents</h2>
<p>
When you want to edit a document with other users or invite them to answer to a poll, you just have to share the URL of that document with them. All the information needed to access and edit it are contained in the "hash" of the URL.<!-- A first part of that hash represents the location of the data in our server, the second part allows your browser to decrypt the content of that document and encrypt new modifications you may want to make.-->
</p>
</div>
</div>
</div>
<div class="page even">
<div class="info-container">
<div class="left">
<h2>User Account</h2>
<p>
In order to have Zero Knowledge user accounts, we needed a way to store your settings without knowing your username and password. To do so, our tools generate a unique key from your username and password, directly in your browser.<!-- The entire key is never sent to our server and there is no way to determine either your username or your password from it. A small part of that key is used to locate your account data in our server, and the other part is used to encrypt and decrypt it (in your browser). That way, the server never has access to your decrypted data or to your decryption key.-->
</p>
</div>
<div class="right image">
<img src="customize/images/useraccount.png" alt="User account" />
</div>
</div>
</div>
<div class="page category" id="tryit">
<center>
<h1 data-localization="tryIt">Try it out!</h1>
</center>
</div>
<div class="page">
<div class="app-container">
<div class="app-row">
<div class="app">
<center>
<h2>Rich Text editor</h2>
<img src="customize/images/pad.png" alt="Rich Text application" />
</center>
<p>
Edit rich text documents collaboratively with our realtime Zero Knowledge <a href="http://ckeditor.com" target="_blank">CkEditor</a> application.
</p>
<p class="buttons">
<a id="create-pad" href="/pad/"><button class="btn btn-secondary" data-localization="button_newpad"></button></a>
</p>
</div><div class="app">
<center>
<h2>Code editor</h2>
<img src="customize/images/code.png" alt="Code application" />
</center>
<p>
Edit code from your software collaboratively with our realtime Zero Knowledge <a href="https://www.codemirror.net" target="_blank">CodeMirror</a> application.
</p>
<p class="buttons">
<a id="create-code" href="/code/"><button class="btn btn-secondary" data-localization="button_newcode"></button></a>
</p>
</div><!--
--></div><!--
--><div class="app-row"><!--
--><div class="app">
<center>
<h2>Slide editor</h2>
<img src="customize/images/slide.png" alt="Slide applcation" />
</center>
<p>
Create your presentations using the Markdown syntax with our CryptSlide application, and display them in your browser.
</p>
<p class="buttons">
<a id="create-slide" href="/slide/"><button class="btn btn-secondary" data-localization="button_newslide"></button></a>
</p>
</div><div class="app">
<center>
<h2>Polls</h2>
<img src="customize/images/poll.png" alt="Poll application" />
</center>
<p>
Plan your meeting or your event, or vote for the best solution regarding your problem using our poll application.
</p>
<p class="buttons">
<a id="create-poll" href="/poll/"><button class="btn btn-secondary" data-localization="button_newpoll"></button></a>
</p>
</div>
</div>
</div>
</div>
</center>
<p data-localization="main_about_p1"><!-- Privacy policy, terms of service --></p>
<p data-localization="main_about_p2"><!-- Contact us--></p>

@ -1,3 +1,4 @@
<div id="main_other">
<center> <center>
<h1 data-localization="policy_title"></h1> <h1 data-localization="policy_title"></h1>
</center> </center>
@ -24,6 +25,6 @@
<p data-localization="policy_choices_ads"></p> <p data-localization="policy_choices_ads"></p>
<br /> <br />
</div>

@ -1,3 +1,4 @@
<div id="main_other">
<center> <center>
<h1 data-localization="tos_title"></h1> <h1 data-localization="tos_title"></h1>
</center> </center>
@ -7,4 +8,5 @@
<p data-localization="tos_e2ee"></p> <p data-localization="tos_e2ee"></p>
<p data-localization="tos_logs"></p> <p data-localization="tos_logs"></p>
<p data-localization="tos_3rdparties"></p> <p data-localization="tos_3rdparties"></p>
</div>

@ -0,0 +1,23 @@
<div id="cryptpadTopBar">
<span>
<a class="gotoMain" href="/">
<img src="customize/cryptofist_mini.png" class="cryptpad-logo" alt="" /> CryptPad
</a>
</span>
<!--<span class="slogan" data-localization="main_slogan"></span>-->
<span id="language-selector" class="right dropdown-bar"></span>
<span class="right">
<a href="/about.html" data-localization="about">About</a>
</span>
<span class="right">
<a href="/privacy.html" data-localization="privacy">Privacy</a>
</span>
<span class="right">
<a href="/terms.html" data-localization="terms">ToS</a>
</span>
<span class="right">
<a href="/contact.html" data-localization="contact">Contact</a>
</span>
</div>

@ -6,7 +6,7 @@
.alertify-logs { .alertify-logs {
> * { > * {
padding: @padding-base @padding-base * 4; padding: @padding-base @padding-base * 4;
color: @fore; color: @alertify-fore;
font-weight: bold; font-weight: bold;
font-size: large; font-size: large;
@ -30,6 +30,7 @@
.alertify { .alertify {
position: fixed; position: fixed;
background-color: @alertify-bg; background-color: @alertify-bg;
color: @alertify-fg;
left: 0; left: 0;
right: 0; right: 0;
top: 0; top: 0;
@ -128,7 +129,7 @@
cursor: pointer; cursor: pointer;
color: @alertify-btn-fg; color: @alertify-btn-fg;
border: 1px solid @base; border: 1px solid @alertify-base;
border-radius: 5px; border-radius: 5px;
&:hover, &:active { &:hover, &:active {
@ -136,7 +137,7 @@
} }
&:focus { &:focus {
border: 1px dotted @base; border: 1px dotted @alertify-base;
} }
&::-moz-focus-inner { &::-moz-focus-inner {
border:0; border:0;

@ -4,6 +4,8 @@
@import "./alertify.less"; @import "./alertify.less";
@import "./bar.less"; @import "./bar.less";
@import "./loading.less"; @import "./loading.less";
@import "./dropdown.less";
@import "./topbar.less";
html.cp, .cp body { html.cp, .cp body {
font-size: .875em; font-size: .875em;
@ -11,16 +13,17 @@ html.cp, .cp body {
color: @fore; color: @fore;
font-family: Georgia,Cambria,serif; font-family: Georgia,Cambria,serif;
height: 100; height: 100%;
} }
.cp:not(.poll) { .cp:not(.poll) {
#language-selector { /*#language-selector {
position: absolute; position: absolute;
top: 0px; top: @topbar-height;
right: 0px; right: 0px;
display: inline-block; display: inline-block;
} z-index: 2;
}*/
} }
.cp { .cp {
@ -29,11 +32,12 @@ body {
font-size: 1rem; font-size: 1rem;
font-weight: 400; font-weight: 400;
line-height: 2rem; line-height: 2rem;
margin: 0;
} }
a.github-corner > svg { a.github-corner > svg {
fill: @cp-blue; fill: @cp-blue;
color: @base; color: @old-base;
} }
.lato { .lato {
@ -96,11 +100,11 @@ h6 {
a { a {
cursor: pointer; cursor: pointer;
color: @cp-green; color: @cp-darkblue;
text-decoration: none; text-decoration: none;
&:hover { &:hover {
color: @cp-accent; color: @cp-accent2;
} }
} }
@ -122,12 +126,298 @@ p, pre, td, a, table, tr {
.lato; .lato;
} }
body.html {
}
// Main page
.page {
width: 100%;
margin-left: auto;
margin-right: auto;
background: #fff; //@base;
padding: 10px 0;//@main-border-width;
position: relative;
.info-container {
width: 900px;
max-width: 100%;
margin: 0 auto;
&>div{
padding: 10px;
width: 400px;
max-width: 100%;
position: relative;
display: inline-block;
vertical-align: middle;
&:not(.image) {
@media screen and (max-width: @media-not-big) {
width: 100%;
left: 0;
}
}
&.image {
text-align: center;
@media screen and (max-width: @media-not-big) {
display: none;
}
}
}
}
&.first {
//margin-top: ~"min(calc(100vh - 150px), 650px)";
@media screen and (max-width: @media-not-big) {
//margin-top: 0;
}
}
&.even {
//background: darken(@base, 1%);
}
&.category {
background: darken(@base, 3%);
}
.app {
display: inline-block;
width: 300px;
vertical-align: middle;
margin: 0px 25px;
white-space: normal;
max-width: ~"calc(50% - 50px)";
@media screen and (max-width: 500px) {
display: block;
max-width: 100%;
margin: 0 auto;
}
}
.app-container {
width: 1400px;
max-width: 100%;
margin: 0 auto;
}
.app-row {
display: inline-block;
white-space: nowrap;
width: 700px;
max-width: 100%;
margin: 0 auto;
@media screen and (max-width: 1399px) {
display: block;
}
img {
@media screen and (max-width: @media-not-big) {
display: none;
}
}
}
.left {
//left: 10%; //@main-border-width;
}
.right {
left: 100px; //@main-border-width;
}
h1, h2, h3, h4, h5, h6 {
padding: 0;
}
@media screen and (max-width: @media-not-big) {
padding: 10px 5vh;
}
p {
font-size: 18px;
text-align: justify;
}
}
.btn-default {
&:hover {
background-color: #d8d8d8;
}
}
#main { #main {
width: 70vw; background-image: url('/customize/bg3.jpg');
background-size: cover;
background-position: center center;
}
#main_other {
padding: 0 @main-border-width;
}
#main, #main_other {
#overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #000;
opacity: 0.3;
}
position: relative;
left: 0;
right: 0;
//padding: 0;
height: ~"calc(100vh - 150px)";
min-height: 450px;
margin: auto; margin: auto;
//margin-top: 100px;
font-size: medium; font-size: medium;
padding-bottom: 1em;
#main-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%);
margin-left: auto;
margin-right: auto;
display: inline-block;
width: 1000px;
max-width: 90%;
}
#data {
p {
margin: 0;
padding: 0;
font-size: 20px;
line-height: 1.5em;
}
h2 {
font-weight: normal;
font-size: 48px;
line-height: 1.2em;
color: @main-color;
}
h5 {
font-size: 1em;
color: @main-color;
}
width: 600px;
max-width: 60%;
color: @main-color;
padding: 15px;
box-sizing: border-box;
position: absolute;
display: inline-block;
top: 50%;
left: 0;
transform: translateY(-50%);
//width: ~"calc(100% - 450px - 30vw)";
//background-color: @main-block-bg;
#tryit {
margin-top: 20px;
margin-bottom: 5px;
}
}
#userForm {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
margin-left: 50px;
display: inline-block;
width: 350px;
max-width: 35%;
//background-color: @main-block-bg;
padding: 10px;
box-sizing: border-box;
font-family: lato, Helvetica, sans-serif;
color: @main-color;
h1 {
color: @main-color;
padding: 0;
}
[type="text"], [type="password"] {
width: 100%;
}
label {
margin-bottom: 0;
//margin-top: 0.5em;
}
/*.remember {
vertical-align: middle;
line-height: 28px;
height: 28px;
display: inline-block;
margin: 10px 0 20px 0;
}
[type="checkbox"] {
vertical-align: text-top;
margin: 0;
//margin-top: 10px;
margin-right: 5px;
}
*/
[type="checkbox"] {
margin-right: 5px;
}
button {
font-weight: bold;
width: 100%;
margin: 2px 0px;
cursor: pointer;
&.half {
width: ~"calc(50% - 4px)";
&:not(.first) {
float: right;
}
}
}
}
#main-container {
@media screen and (max-width: @media-not-big) {
transform: initial;
position: relative;
display: block;
width: 90%;
left: 0;
}
}
#userForm, #data {
@media screen and (max-width: @media-not-big) {
transform: initial;
position: relative;
display: block;
width: 100%;
max-width: 100%;
margin: 10px 0;
box-sizing: border-box;
}
}
#userForm {
@media screen and (max-width: @media-not-big) {
border: 1px solid #888;
}
}
@media screen and (max-width: @media-not-big) {
position: relative;
height: auto;
//background: #aaa;
top: -10px;
}
.buttons {
margin-top: 15px;
}
}
p.buttons, div.buttons {
text-align: center;
button {
font-weight: bold;
cursor: pointer;
margin-top: 10px;
}
} }
#fileManagerIframe { #fileManagerIframe {
@ -138,27 +428,22 @@ p, pre, td, a, table, tr {
/* buttons */ /* buttons */
// home page button container
.buttons {
margin-bottom: 50px;
margin-top: 20px;
line-height: 2.5em;
}
.create, .action { .create, .action {
display: inline-block; display: inline-block;
@thick: 2px; @thick: 2px;
border: @thick solid @cp-green; //border: @thick solid @cp-darkblue;
background-color: @base; border: 0;
color: @cp-green; background-color: @cp-darkblue;
color: @topbar-button-color;
font-weight: bold; font-weight: bold;
font-size: large; font-size: large;
margin-right: 5px; margin-right: 5px;
margin-left: 5px; margin-left: 5px;
&:hover { &:hover {
border: @thick solid @cp-accent; color: darken(@topbar-button-color, 20%);
color: @cp-green; //border: @thick solid @cp-accent2;
//color: @cp-darkblue;
} }
} }
@ -167,15 +452,16 @@ p, pre, td, a, table, tr {
@vpad: 2 * 2px; @vpad: 2 * 2px;
padding: @vpad @hpad @vpad @hpad; padding: @vpad @hpad @vpad @hpad;
margin-top: 2 * 6px; //margin-top: 2 * 6px;
margin-bottom: 2 * 6px; //margin-bottom: 2 * 6px;
margin: 2px 0;
display: inline-block; display: inline-block;
line-height: 1.5em; line-height: 1.5em;
} }
// currently only used in /user/ // currently only used in /user/
.panel { .panel {
background-color: #333; background-color: @dark-base;
} }
/* Tables /* Tables
@ -187,7 +473,7 @@ table {
margin: 20px; margin: 20px;
} }
tbody { tbody {
border: 2px solid black; border: 1px solid @poll-border-color;
tr { tr {
text-align: center; text-align: center;
&:first-of-type th{ &:first-of-type th{
@ -211,7 +497,7 @@ tbody {
} }
th { th {
box-sizing: border-box; box-sizing: border-box;
border: 1px solid black; border: 1px solid @poll-border-color;
} }
th, td { th, td {
color: @fore; color: @fore;
@ -226,7 +512,7 @@ tbody {
} }
td { td {
border-right: 1px solid black; border-right: 1px solid @poll-border-color;
padding: 12px; padding: 12px;
padding-top: 0px; padding-top: 0px;
padding-bottom: 0px; padding-bottom: 0px;
@ -291,7 +577,7 @@ form.realtime, div.realtime {
border: 0px; border: 0px;
&[disabled] { &[disabled] {
background-color: transparent; background-color: transparent;
color: @fore; color: @poll-fg;
font-weight: bold; font-weight: bold;
} }
} }
@ -329,7 +615,7 @@ form.realtime, div.realtime {
font-weight: bold; font-weight: bold;
background-color: @cp-red; background-color: @cp-red;
color: @base; color: @poll-cover-color;
&:after { &:after {
height: 100%; height: 100%;
@ -370,7 +656,7 @@ form.realtime, div.realtime {
thead { thead {
td { td {
padding: 0px 5px; padding: 0px 5px;
background: @less-light-base; background: @poll-th-bg;
border-radius: 20px 20px 0 0; border-radius: 20px 20px 0 0;
text-align: center; text-align: center;
input { input {
@ -378,7 +664,7 @@ form.realtime, div.realtime {
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
&[disabled] { &[disabled] {
color: white; color: @poll-fg;
padding: 1px 5px; padding: 1px 5px;
border: none; border: none;
} }
@ -389,7 +675,7 @@ form.realtime, div.realtime {
tbody { tbody {
.text-cell { .text-cell {
background: @less-light-base; background: @poll-td-bg;
//border-radius: 20px 0 0 20px; //border-radius: 20px 0 0 20px;
input[type="text"] { input[type="text"] {
width: ~"calc(100% - 50px)"; width: ~"calc(100% - 50px)";
@ -403,6 +689,11 @@ form.realtime, div.realtime {
margin: 0 0 0 10px; margin: 0 0 0 10px;
} }
} }
td {
label {
border: .5px solid @poll-border-color;
}
}
} }
.edit { .edit {
color: @cp-green; color: @cp-green;
@ -477,6 +768,31 @@ form.realtime, div.realtime {
} }
&.slide { &.slide {
#modal {
.button {
position: absolute;
cursor: pointer;
font-size: 30px;
opacity: 0.6;
display: none;
}
.button:hover {
opacity: 1;
display: block !important;
}
#button_exit {
left: 20px;
top: 20px;
}
#button_left {
left: 6vw;
bottom: 10vh;
}
#button_right {
right: 6vw;
bottom: 10vh;
}
}
#modal #content { #modal #content {
p, ul, ol { font-size: 26px; } p, ul, ol { font-size: 26px; }
@ -531,6 +847,10 @@ div.modal, div#modal {
h5 { .size(2.2); } h5 { .size(2.2); }
h6 { .size(1.6); } h6 { .size(1.6); }
h1, h2, h3, h4, h5, h6 {
color: inherit;
}
pre > code { pre > code {
display: block; display: block;
position: relative; position: relative;
@ -559,7 +879,7 @@ div.modal, div#modal {
height: 100vh; height: 100vh;
display: none; display: none;
background-color: @base; background-color: @slide-default-bg;
.center { .center {
position: relative; position: relative;

@ -0,0 +1,91 @@
/* The container <div> - needed to position the dropdown content */
.dropdown-bar {
position: relative;
display: inline-block;
.dropbtn {
}
&:hover {
.dropbtn {
}
}
.fa {
font-family: FontAwesome;
}
button {
.fa-caret-down{
margin-left: 5px;
}
}
.dropdown-bar-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 200px;
box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2);
z-index: 1000;
max-height: 300px;
overflow-y: auto;
&.left {
right: 0;
}
&:hover {
display: block;
}
a {
color: black;
padding: 5px 16px;
text-decoration: none;
display: block;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
float: none;
text-align: left;
&:hover {
background-color: #f1f1f1;
}
}
hr {
margin: 5px 0px;
height: 1px;
background: #bbb;
}
p {
min-width: 160px;
padding: 5px;
margin: 0;
white-space: normal;
text-align: left;
&.cryptpad-dropdown-users {
text-align:baseline;
.yourself, .anonymous, .viewer {
font-style: italic;
}
}
h2 {
font-weight: bold;
text-align: center;
background-color: #EEEEEE;
padding: 5px 0px;
margin: 5px 0px;
font-size: 16px;
white-space: normal;
}
}
}
}

@ -6,6 +6,7 @@
left: 0px; left: 0px;
right: 0px; right: 0px;
background: @bg-loading; background: @bg-loading;
color: @color-loading;
text-align: center; text-align: center;
font-size: 1.5em; font-size: 1.5em;
.loadingContainer { .loadingContainer {

@ -1,6 +1,8 @@
@import "./variables.less"; @import "./variables.less";
@import "./mixins.less"; @import "./mixins.less";
@import "./dropdown.less";
.unselectable { .unselectable {
-webkit-touch-callout: none; -webkit-touch-callout: none;
-webkit-user-select: none; -webkit-user-select: none;
@ -24,11 +26,12 @@
box-sizing: border-box; box-sizing: border-box;
padding: 0px 6px; padding: 0px 6px;
.fa {font-family: FontAwesome;}
.unselectable; .unselectable;
font: normal normal normal 12px Arial,Helvetica,Tahoma,Verdana,Sans-Serif; font: normal normal normal 12px Arial,Helvetica,Tahoma,Verdana,Sans-Serif;
color: #000; color: #000;
display: inline-block;
width: 100%; width: 100%;
z-index: 9001; z-index: 9001;
@ -38,12 +41,11 @@
.cryptpad-lag { .cryptpad-lag {
box-sizing: content-box; box-sizing: content-box;
vertical-align: top;
height: 16px; height: 16px;
width: 16px; width: 16px;
display: inline-block; display: inline-block;
margin: 2px 0px;
padding: 5px; padding: 5px;
margin: 3px 0;
div { div {
margin: auto; margin: auto;
} }
@ -62,7 +64,12 @@
height: 26px; height: 26px;
padding-right: 5px; padding-right: 5px;
padding-left: 5px; padding-left: 5px;
margin: 2px; margin: 3px 2px;
}
.dropdown-bar-content {
margin-top: -3px;
margin-right: 2px;
} }
button { button {
@ -74,27 +81,27 @@
&:hover { &:hover {
background-image:linear-gradient(to bottom,#f2f2f2,#ccc); background-image:linear-gradient(to bottom,#f2f2f2,#ccc);
} }
&.userlist { }
@media screen and (max-width: 800px) {
display: none; .large {
} @media screen and (max-width: 800px) {
@media screen and (min-width: 801px) { display: none;
display: inline-block; }
} @media screen and (min-width: 801px) {
&.small { display: inline-block;
@media screen and (max-width: 800px) { }
display: inline-block; }
} .small {
@media screen and (min-width: 801px) { @media screen and (max-width: 800px) {
display: none; display: inline-block;
} }
} @media screen and (min-width: 801px) {
display: none;
} }
} }
.cryptpad-state { .cryptpad-state {
line-height: 30px; /* equivalent to 26px + 2*2px margin used for buttons */ line-height: 32px; /* equivalent to 26px + 2*2px margin used for buttons */
float: left;
} }
.rightside-button { .rightside-button {
@ -244,6 +251,11 @@
vertical-align: top; vertical-align: top;
display: inline-block; display: inline-block;
} }
button {
span.fa {
vertical-align: baseline;
}
}
@media screen and (max-width: @media-narrow-screen) { @media screen and (max-width: @media-narrow-screen) {
top: 3em; top: 3em;
} }
@ -258,72 +270,52 @@
.cryptpad-user-list { .cryptpad-user-list {
float: right; float: right;
} }
.cryptpad-dropdown-container {
position: relative;
display: inline-block;
padding: 0px;
.cryptpad-dropdown {
z-index:1000;
display:none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
overflow: auto;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
height: auto;
padding: 5px;
white-space: normal;
p {
width: 210px;
padding: 0;
margin: 0;
white-space: normal;
&.cryptpad-dropdown-users {
text-align:baseline;
.yourself, .anonymous, .viewer {
font-style: italic;
}
}
h2 {
font-weight: bold;
text-align: center;
background-color: #EEEEEE;
padding: 5px 0px;
margin: 5px 0px;
font-size: 16px;
white-space: normal;
}
}
button {
white-space: normal;
margin: 2px 0px;
}
}
}
button { button {
margin: 2px 4px 2px 0px; margin: 2px 4px 2px 0px;
} }
.cryptpad-userbuttons-container {
/*display: none;*/
}
} }
.cryptpad-toolbar-rightside { .cryptpad-toolbar-rightside {
text-align: right; text-align: right;
} }
.cryptpad-spinner { .cryptpad-spinner {
display: inline-block; height: 16px;
height: 26px; width: 16px;
margin: 2px; margin: 8px;
line-height: 26px; line-height: 16px;
font-size: 20px; font-size: 16px;
} }
.cryptpad-readonly { .cryptpad-readonly {
margin-right: 5px; margin-right: 5px;
font-weight: bold; font-weight: bold;
text-transform: uppercase; text-transform: uppercase;
} }
.cryptpad-toolbar-username { .cryptpad-user {
p.accountData {
background: #f0f0f0;
&> span {
font-weight: bold;
span {
font-weight: normal;
}
}
}
.buttonTitle {
.fa:not(.fa-caret-down) {
margin-right: 5px;
}
.account-name {
margin-left: 5px;
}
}
}
.cryptpad-dropdown-share {
a {
.fa {
margin-right: 5px;
}
}
} }
.lag { .lag {
height: 15px !important; height: 15px !important;
width: 15px !important; width: 15px !important;

@ -0,0 +1,67 @@
@import "./variables.less";
#cryptpadTopBar {
background: @topbar-back;
position: relative;
top: 0;
left: 0;
right: 0;
height: @topbar-height;
color: @topbar-color;
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
padding: 5px;
box-sizing: border-box;
font-size: 30px;
&> span {
vertical-align: middle;
display: inline-block;
height: 100%;
}
.cryptpad-logo {
height: 40px;
vertical-align: middle;
}
.slogan {
font-size: 20px;
color: @topbar-color;
line-height: 40px;
}
.gotoMain {
color: @topbar-color;
height: 40px;
line-height: 40px;
&:hover {
text-decoration: none;
color: #558;
}
}
.right {
float: right;
font-size: 20px;
margin: 0px 10px;
line-height: 40px;
a {
font-weight: 500;
font-size: 0.75em;
color: #558;
&:hover {
text-decoration: none;
color: #000;
}
/* text-align: center;
min-width: 100px;
font-weight: bold;
height: 70px;
padding: 0 10px;
line-height: 70px;
display: inline-block;
color: @topbar-button-color;*/
}
}
}

@ -1,12 +1,17 @@
@base: #302B28; @base: #fff; //#f5f5f5;
@dark-base: darken(@base, 20%);
@less-dark-base: darken(@base, 10%);
@light-base: lighten(@base, 20%); @light-base: lighten(@base, 20%);
@less-light-base: lighten(@base, 10%); @less-light-base: lighten(@base, 10%);
@fore: #fafafa; @fore: #555;
@old-base: #302B28;
@old-fore: #fafafa;
@cp-green: #46E981; @cp-green: #46E981;
@cp-accent: lighten(@cp-green, 20%); @cp-accent: lighten(@cp-green, 20%);
@cp-red: #FF0073; // remove red //@cp-red: #FF0073; // remove red
@cp-red: #FA5858; // remove red
@cp-outline: #444; @cp-outline: #444;
@cp-orange: #FE9A2E; @cp-orange: #FE9A2E;
@ -27,20 +32,33 @@
@text-color: rgba(0, 0, 0, .8); @text-color: rgba(0, 0, 0, .8);
@border-radius: 1px; @border-radius: 1px;
@alertify-fore: @old-fore;
@alertify-base: @old-base;
@alertify-dialog-bg: #444; @alertify-dialog-bg: #444;
@alertify-dialog-fg: @fore; @alertify-dialog-fg: @old-fore;
@alertify-btn-fg: @fore; @alertify-btn-fg: @old-fore;
@alertify-btn-bg: rgba(200, 200, 200, 0.05); @alertify-btn-bg: rgba(200, 200, 200, 0.05);
@alertify-btn-bg-hover: rgba(200, 200, 200, .15); @alertify-btn-bg-hover: rgba(200, 200, 200, .15);
@alertify-bg: rgba(0, 0, 0, .3); @alertify-bg: rgba(0, 0, 0, .3);
@alertify-fg: @old-fore;
@alertify-input-bg: @base; @alertify-input-bg: @base;
@alertify-input-fg: @fore; @alertify-input-fg: @fore;
@bg-loading: @base; @slide-default-bg: #000;
@poll-th-bg: #aaa;
@poll-td-bg: #aaa;
@poll-border-color: #555;
@poll-cover-color: #000;
@poll-fg: #000;
@bg-loading: @old-base;
@color-loading: @old-fore;
@media-not-big: 800px; @media-not-big: 800px;
@media-not-small: 801px; @media-not-small: 801px;
@ -51,3 +69,15 @@
@toolbar-gradient-start: #f5f5f5; @toolbar-gradient-start: #f5f5f5;
@toolbar-gradient-end: #DDDDDD; @toolbar-gradient-end: #DDDDDD;
@topbar-back: #fff;
@topbar-color: #000;
@topbar-button-bg: #2E9AFE;
@topbar-button-color: #fff;
@topbar-height: 50px;
@main-border-width: 15vw;
@cp-darkblue: #3333ff;
@cp-accent2: darken(@cp-darkblue, 20%);
@main-block-bg: rgba(200, 200, 200, 0.3);
@main-color: #fff;

@ -3,8 +3,12 @@
<head> <head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title> <title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/customize/main.css" /> <link rel="stylesheet" type="text/css" href="/customize/main.css" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/> <link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<script data-main="/customize/main" src="/bower_components/requirejs/require.js"></script> <script data-main="/customize/main" src="/bower_components/requirejs/require.js"></script>
<script src="/bower_components/requirejs/require.js"></script> <script src="/bower_components/requirejs/require.js"></script>
<script> <script>
@ -13,18 +17,12 @@
}); });
</script> </script>
</head> </head>
<body> <body class="html">
{{fork}} {{topbar}}
<select id="language-selector" style="display: none;"></select>
<div id="main">
{{logo}}
{{noscript}} {{noscript}}
{{main}} {{main}}
</div>
</body> </body>
</html> </html>

@ -3,8 +3,12 @@
<head> <head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title> <title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/customize/main.css" /> <link rel="stylesheet" type="text/css" href="/customize/main.css" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/> <link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<script data-main="/customize/main" src="/bower_components/requirejs/require.js"></script> <script data-main="/customize/main" src="/bower_components/requirejs/require.js"></script>
<script src="/bower_components/requirejs/require.js"></script> <script src="/bower_components/requirejs/require.js"></script>
<script> <script>
@ -13,18 +17,30 @@
}); });
</script> </script>
</head> </head>
<body> <body class="html">
<a data-localization-title="github_ribbon" href="https://github.com/xwiki-labs/cryptpad" class="github-corner" aria-label="View source on Github"><svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 0; border: 0; left: 0; transform: scale(-1, 1);" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style> <div id="cryptpadTopBar">
<!-- Thanks! http://tholman.com/github-corners/ --> <span>
<a class="gotoMain" href="/">
<img src="customize/cryptofist_mini.png" class="cryptpad-logo" alt="" /> CryptPad
</a>
</span>
<!--<span class="slogan" data-localization="main_slogan"></span>-->
<span id="language-selector" class="right dropdown-bar"></span>
<select id="language-selector" style="display: none;"></select> <span class="right">
<a href="/about.html" data-localization="about">About</a>
<div id="main"> </span>
<center> <span class="right">
<a href="/"><img class="imgcenter cryptofist" src="/customize/cryptofist_small.png" /></a> <a href="/privacy.html" data-localization="privacy">Privacy</a>
</center> </span>
<span class="right">
<a href="/terms.html" data-localization="terms">ToS</a>
</span>
<span class="right">
<a href="/contact.html" data-localization="contact">Contact</a>
</span>
</div>
<center> <center>
@ -41,6 +57,7 @@
<div id="main_other">
<center> <center>
<h1 data-localization="tos_title"></h1> <h1 data-localization="tos_title"></h1>
</center> </center>
@ -50,9 +67,9 @@
<p data-localization="tos_e2ee"></p> <p data-localization="tos_e2ee"></p>
<p data-localization="tos_logs"></p> <p data-localization="tos_logs"></p>
<p data-localization="tos_3rdparties"></p> <p data-localization="tos_3rdparties"></p>
</div>
</div>
</body> </body>
</html> </html>

@ -1,3 +1,75 @@
/* The container <div> - needed to position the dropdown content */
.dropdown-bar {
position: relative;
display: inline-block;
}
.dropdown-bar .fa {
font-family: FontAwesome;
}
.dropdown-bar button .fa-caret-down {
margin-left: 5px;
}
.dropdown-bar .dropdown-bar-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 200px;
box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2);
z-index: 1000;
max-height: 300px;
overflow-y: auto;
}
.dropdown-bar .dropdown-bar-content.left {
right: 0;
}
.dropdown-bar .dropdown-bar-content:hover {
display: block;
}
.dropdown-bar .dropdown-bar-content a {
color: black;
padding: 5px 16px;
text-decoration: none;
display: block;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
float: none;
text-align: left;
}
.dropdown-bar .dropdown-bar-content a:hover {
background-color: #f1f1f1;
}
.dropdown-bar .dropdown-bar-content hr {
margin: 5px 0px;
height: 1px;
background: #bbb;
}
.dropdown-bar .dropdown-bar-content p {
min-width: 160px;
padding: 5px;
margin: 0;
white-space: normal;
text-align: left;
}
.dropdown-bar .dropdown-bar-content p.cryptpad-dropdown-users {
text-align: baseline;
}
.dropdown-bar .dropdown-bar-content p.cryptpad-dropdown-users .yourself,
.dropdown-bar .dropdown-bar-content p.cryptpad-dropdown-users .anonymous,
.dropdown-bar .dropdown-bar-content p.cryptpad-dropdown-users .viewer {
font-style: italic;
}
.dropdown-bar .dropdown-bar-content p h2 {
font-weight: bold;
text-align: center;
background-color: #EEEEEE;
padding: 5px 0px;
margin: 5px 0px;
font-size: 16px;
white-space: normal;
}
.unselectable { .unselectable {
-webkit-touch-callout: none; -webkit-touch-callout: none;
-webkit-user-select: none; -webkit-user-select: none;
@ -31,21 +103,22 @@
user-select: none; user-select: none;
font: normal normal normal 12px Arial, Helvetica, Tahoma, Verdana, Sans-Serif; font: normal normal normal 12px Arial, Helvetica, Tahoma, Verdana, Sans-Serif;
color: #000; color: #000;
display: inline-block;
width: 100%; width: 100%;
z-index: 9001; z-index: 9001;
} }
.cryptpad-toolbar .fa {
font-family: FontAwesome;
}
.cryptpad-toolbar a { .cryptpad-toolbar a {
float: right; float: right;
} }
.cryptpad-toolbar .cryptpad-lag { .cryptpad-toolbar .cryptpad-lag {
box-sizing: content-box; box-sizing: content-box;
vertical-align: top;
height: 16px; height: 16px;
width: 16px; width: 16px;
display: inline-block; display: inline-block;
margin: 2px 0px;
padding: 5px; padding: 5px;
margin: 3px 0;
} }
.cryptpad-toolbar .cryptpad-lag div { .cryptpad-toolbar .cryptpad-lag div {
margin: auto; margin: auto;
@ -65,7 +138,11 @@
height: 26px; height: 26px;
padding-right: 5px; padding-right: 5px;
padding-left: 5px; padding-left: 5px;
margin: 2px; margin: 3px 2px;
}
.cryptpad-toolbar .dropdown-bar-content {
margin-top: -3px;
margin-right: 2px;
} }
.cryptpad-toolbar button { .cryptpad-toolbar button {
background-color: inherit; background-color: inherit;
@ -78,29 +155,28 @@
background-image: linear-gradient(to bottom, #f2f2f2, #ccc); background-image: linear-gradient(to bottom, #f2f2f2, #ccc);
} }
@media screen and (max-width: 800px) { @media screen and (max-width: 800px) {
.cryptpad-toolbar button.userlist { .cryptpad-toolbar .large {
display: none; display: none;
} }
} }
@media screen and (min-width: 801px) { @media screen and (min-width: 801px) {
.cryptpad-toolbar button.userlist { .cryptpad-toolbar .large {
display: inline-block; display: inline-block;
} }
} }
@media screen and (max-width: 800px) { @media screen and (max-width: 800px) {
.cryptpad-toolbar button.userlist.small { .cryptpad-toolbar .small {
display: inline-block; display: inline-block;
} }
} }
@media screen and (min-width: 801px) { @media screen and (min-width: 801px) {
.cryptpad-toolbar button.userlist.small { .cryptpad-toolbar .small {
display: none; display: none;
} }
} }
.cryptpad-toolbar .cryptpad-state { .cryptpad-toolbar .cryptpad-state {
line-height: 30px; line-height: 32px;
/* equivalent to 26px + 2*2px margin used for buttons */ /* equivalent to 26px + 2*2px margin used for buttons */
float: left;
} }
.cryptpad-toolbar .rightside-button { .cryptpad-toolbar .rightside-button {
float: right; float: right;
@ -249,6 +325,9 @@
vertical-align: top; vertical-align: top;
display: inline-block; display: inline-block;
} }
.cryptpad-toolbar-top .cryptpad-user button span.fa {
vertical-align: baseline;
}
@media screen and (max-width: 400px) { @media screen and (max-width: 400px) {
.cryptpad-toolbar-top .cryptpad-user { .cryptpad-toolbar-top .cryptpad-user {
top: 3em; top: 3em;
@ -266,71 +345,42 @@
.cryptpad-toolbar-leftside .cryptpad-user-list { .cryptpad-toolbar-leftside .cryptpad-user-list {
float: right; float: right;
} }
.cryptpad-toolbar-leftside .cryptpad-dropdown-container {
position: relative;
display: inline-block;
padding: 0px;
}
.cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown {
z-index: 1000;
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
overflow: auto;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
height: auto;
padding: 5px;
white-space: normal;
}
.cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p {
width: 210px;
padding: 0;
margin: 0;
white-space: normal;
}
.cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p.cryptpad-dropdown-users {
text-align: baseline;
}
.cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p.cryptpad-dropdown-users .yourself,
.cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p.cryptpad-dropdown-users .anonymous,
.cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p.cryptpad-dropdown-users .viewer {
font-style: italic;
}
.cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown p h2 {
font-weight: bold;
text-align: center;
background-color: #EEEEEE;
padding: 5px 0px;
margin: 5px 0px;
font-size: 16px;
white-space: normal;
}
.cryptpad-toolbar-leftside .cryptpad-dropdown-container .cryptpad-dropdown button {
white-space: normal;
margin: 2px 0px;
}
.cryptpad-toolbar-leftside button { .cryptpad-toolbar-leftside button {
margin: 2px 4px 2px 0px; margin: 2px 4px 2px 0px;
} }
.cryptpad-toolbar-leftside .cryptpad-userbuttons-container {
/*display: none;*/
}
.cryptpad-toolbar-rightside { .cryptpad-toolbar-rightside {
text-align: right; text-align: right;
} }
.cryptpad-spinner { .cryptpad-spinner {
display: inline-block; height: 16px;
height: 26px; width: 16px;
margin: 2px; margin: 8px;
line-height: 26px; line-height: 16px;
font-size: 20px; font-size: 16px;
} }
.cryptpad-readonly { .cryptpad-readonly {
margin-right: 5px; margin-right: 5px;
font-weight: bold; font-weight: bold;
text-transform: uppercase; text-transform: uppercase;
} }
.cryptpad-user p.accountData {
background: #f0f0f0;
}
.cryptpad-user p.accountData > span {
font-weight: bold;
}
.cryptpad-user p.accountData > span span {
font-weight: normal;
}
.cryptpad-user .buttonTitle .fa:not(.fa-caret-down) {
margin-right: 5px;
}
.cryptpad-user .buttonTitle .account-name {
margin-left: 5px;
}
.cryptpad-dropdown-share a .fa {
margin-right: 5px;
}
.lag { .lag {
height: 15px !important; height: 15px !important;
width: 15px !important; width: 15px !important;
@ -341,7 +391,7 @@
background-color: #46E981; background-color: #46E981;
} }
.lag-red { .lag-red {
background-color: #FF0073; background-color: #FA5858;
} }
.lag-orange { .lag-orange {
background-color: #FE9A2E; background-color: #FE9A2E;

@ -1,13 +1,15 @@
define(function () { define(function () {
var out = {}; var out = {};
// translations must set this key for their language to be available in
// the language dropdowns that are shown throughout Cryptpad's interface
out._languageName = "Français"; out._languageName = "Français";
out.main_title = "Cryptpad: Éditeur collaboratif en temps réel, zero knowledge"; out.main_title = "Cryptpad: Éditeur collaboratif en temps réel, zero knowledge";
out.main_slogan = "L'unité est la force, la collaboration est la clé"; out.main_slogan = "L'unité est la force, la collaboration est la clé";
out.type = {}; out.type = {};
out.type.pad = 'Pad'; out.type.pad = 'Texte';
out.type.code = 'Code'; out.type.code = 'Code';
out.type.poll = 'Sondage'; out.type.poll = 'Sondage';
out.type.slide = 'Présentation'; out.type.slide = 'Présentation';
@ -42,6 +44,8 @@ define(function () {
out.editor = "éditeur"; out.editor = "éditeur";
out.editors = "éditeurs"; out.editors = "éditeurs";
out.language = "Langue";
out.greenLight = "Tout fonctionne bien"; out.greenLight = "Tout fonctionne bien";
out.orangeLight = "Votre connexion est lente, ce qui réduit la qualité de l'éditeur"; out.orangeLight = "Votre connexion est lente, ce qui réduit la qualité de l'éditeur";
out.redLight = "Vous êtes déconnectés de la session"; out.redLight = "Vous êtes déconnectés de la session";
@ -59,6 +63,9 @@ define(function () {
out.userButton = 'UTILISATEUR'; out.userButton = 'UTILISATEUR';
out.userButtonTitle = "Changer votre nom d'utilisateur"; out.userButtonTitle = "Changer votre nom d'utilisateur";
out.changeNamePrompt = 'Changer votre nom (laisser vide pour rester anonyme) : '; out.changeNamePrompt = 'Changer votre nom (laisser vide pour rester anonyme) : ';
out.user_rename = "Changer le nom affiché";
out.user_displayName = "Nom affiché";
out.user_accountName = "Nom d'utilisateur";
out.renameButton = 'RENOMMER'; out.renameButton = 'RENOMMER';
out.renameButtonTitle = 'Changer le titre utilisé par ce document dans la page d\'accueil de Cryptpad'; out.renameButtonTitle = 'Changer le titre utilisé par ce document dans la page d\'accueil de Cryptpad';
@ -75,6 +82,9 @@ define(function () {
out.shareSuccess = 'URL copiée dans le presse-papiers'; out.shareSuccess = 'URL copiée dans le presse-papiers';
out.shareFailed = "Échec de la copie de l'URL dans le presse-papiers"; out.shareFailed = "Échec de la copie de l'URL dans le presse-papiers";
out.newPadButton = 'Nouveau';
out.newPadButtonTitle = 'Créer un nouveau document';
out.presentButton = 'PRÉSENTER'; out.presentButton = 'PRÉSENTER';
out.presentButtonTitle = "Entrer en mode présentation"; out.presentButtonTitle = "Entrer en mode présentation";
out.presentSuccess = 'Appuyer sur Échap pour quitter le mode présentation'; out.presentSuccess = 'Appuyer sur Échap pour quitter le mode présentation';
@ -111,10 +121,12 @@ define(function () {
out.recentPadsIframe = 'Vos documents récents'; out.recentPadsIframe = 'Vos documents récents';
out.okButton = 'OK (Entrée)'; out.okButton = 'OK (Entrée)';
out.cancel = "Annuler";
out.cancelButton = 'Annuler (Echap)'; out.cancelButton = 'Annuler (Echap)';
out.loginText = '<p>Votre nom d\'utilisateur et votre mot de passe sont utilisés pour générer une clé unique qui reste inconnue de notre serveur.</p>\n' + out.loginText = '<p>Votre nom d\'utilisateur et votre mot de passe sont utilisés pour générer une clé unique qui reste inconnue de notre serveur.</p>\n' +
'<p>Faites attention de ne pas oublier vos identifiants puisqu\'ils seront impossible à récupérer.</p>'; '<p>Faites attention de ne pas oublier vos identifiants puisqu\'ils seront impossible à récupérer.</p>'; //TODO
out.forget = "Oublier"; out.forget = "Oublier";
@ -170,12 +182,14 @@ define(function () {
// File manager // File manager
out.fm_rootName = "Mes documents"; out.fm_rootName = "Documents";
out.fm_trashName = "Corbeille"; out.fm_trashName = "Corbeille";
out.fm_unsortedName = "Fichiers non triés"; out.fm_unsortedName = "Fichiers non triés";
out.fm_filesDataName = "Tous les fichiers"; out.fm_filesDataName = "Tous les fichiers";
out.fm_templateName = "Modèles";
out.fm_newButton = "Nouveau";
out.fm_newFolder = "Nouveau dossier"; out.fm_newFolder = "Nouveau dossier";
out.fm_newFolderButton = "NOUVEAU DOSSIER"; out.fm_folder = "Dossier";
out.fm_folderName = "Nom du dossier"; out.fm_folderName = "Nom du dossier";
out.fm_numberOfFolders = "# de dossiers"; out.fm_numberOfFolders = "# de dossiers";
out.fm_numberOfFiles = "# de fichiers"; out.fm_numberOfFiles = "# de fichiers";
@ -185,9 +199,10 @@ define(function () {
out.fm_creation = "Création"; out.fm_creation = "Création";
out.fm_forbidden = "Action interdite"; out.fm_forbidden = "Action interdite";
out.fm_originalPath = "Chemin d'origine"; out.fm_originalPath = "Chemin d'origine";
out.fm_noname = "Document sans titre";
out.fm_emptyTrashDialog = "Êtes-vous sûr de vouloir vider la corbeille ?"; out.fm_emptyTrashDialog = "Êtes-vous sûr de vouloir vider la corbeille ?";
out.fm_removeSeveralPermanentlyDialog = "Êtes-vous sûr de vouloir supprimer ces {0} éléments de manière permanente ?"; out.fm_removeSeveralPermanentlyDialog = "Êtes-vous sûr de vouloir supprimer ces {0} éléments de manière permanente ?";
out.fm_removePermanentlyDialog = "Êtes-vous sûr de vouloir supprimer {0} de manière permanente ?"; out.fm_removePermanentlyDialog = "Êtes-vous sûr de vouloir supprimer cet élément de manière permanente ?";
out.fm_restoreDialog = "Êtes-vous sûr de vouloir restaurer {0} à son emplacement précédent ?"; out.fm_restoreDialog = "Êtes-vous sûr de vouloir restaurer {0} à son emplacement précédent ?";
out.fm_removeSeveralDialog = "Êtes-vous sûr de vouloir déplacer ces {0} éléments vers la corbeille ?"; out.fm_removeSeveralDialog = "Êtes-vous sûr de vouloir déplacer ces {0} éléments vers la corbeille ?";
out.fm_removeDialog = "Êtes-vous sûr de vouloir déplacer {0} vers la corbeille ?"; out.fm_removeDialog = "Êtes-vous sûr de vouloir déplacer {0} vers la corbeille ?";
@ -195,13 +210,20 @@ define(function () {
out.fm_contextMenuError = "Impossible d'ouvrir le menu contextuel pour cet élément. Si le problème persiste, essayez de rechercher la page."; out.fm_contextMenuError = "Impossible d'ouvrir le menu contextuel pour cet élément. Si le problème persiste, essayez de rechercher la page.";
out.fm_selectError = "Impossible de sélectionner l'élément ciblé. Si le problème persiste, essayez de recharger la page."; out.fm_selectError = "Impossible de sélectionner l'élément ciblé. Si le problème persiste, essayez de recharger la page.";
out.fm_info_root = "Créez ici autant de dossiers que vous le souhaitez pour trier vos fichiers."; out.fm_info_root = "Créez ici autant de dossiers que vous le souhaitez pour trier vos fichiers.";
out.fm_info_unsorted = 'Contient tous les documents que vous avez ouvert et qui ne sont pas triés dans "Mes documents" ou déplacés vers la "Corbeille".'; // "My Documents" should match with the "out.fm_rootName" key, and "Trash" with "out.fm_trashName" out.fm_info_unsorted = 'Contient tous les documents que vous avez ouvert et qui ne sont pas triés dans "Documents" ou déplacés vers la "Corbeille".'; // "My Documents" should match with the "out.fm_rootName" key, and "Trash" with "out.fm_trashName"
out.fm_info_template = "Contient tous les fichiers que vous avez sauvés en tant que modèle afin de les réutiliser lors de la création d'un nouveau document.";
out.fm_info_trash = 'Les fichiers supprimés dans la corbeille sont également enlevés de "Tous les fichiers" et il est impossible de les récupérer depuis l\'explorateur de fichiers.'; // Same here for "All files" and "out.fm_filesDataName" out.fm_info_trash = 'Les fichiers supprimés dans la corbeille sont également enlevés de "Tous les fichiers" et il est impossible de les récupérer depuis l\'explorateur de fichiers.'; // Same here for "All files" and "out.fm_filesDataName"
out.fm_info_allFiles = 'Contient tous les fichiers de "Mes documents", "Fichiers non triés" et "Corbeille". Vous ne pouvez pas supprimer ou déplacer des fichiers d\'ici.'; // Same here out.fm_info_allFiles = 'Contient tous les fichiers de "Documents", "Fichiers non triés" et "Corbeille". Vous ne pouvez pas supprimer ou déplacer des fichiers depuis cet endroit.'; // Same here
// File - Context menu out.fm_alert_backupUrl = "URL de secours pour ce contenu.<br>" +
"Il est <strong>fortement recommandé</strong> de garder cette URL pour vous-même.<br>" +
"Elle vous servira en cas de perte des données de votre navigateur afin de retrouver vos fichiers.<br>" +
"Quiconque se trouve en possession de celle-ci peut modifier ou supprimer tous les fichiers de ce gestionnaire.<br>" +
'<input type="text" id="fm_backupUrl" value="{0}"/>';
// File - Context menu
out.fc_newfolder = "Nouveau dossier"; out.fc_newfolder = "Nouveau dossier";
out.fc_rename = "Renommer"; out.fc_rename = "Renommer";
out.fc_open = "Ouvrir"; out.fc_open = "Ouvrir";
out.fc_open_ro = "Ouvrir (lecture seule)";
out.fc_delete = "Supprimer"; out.fc_delete = "Supprimer";
out.fc_restore = "Restaurer"; out.fc_restore = "Restaurer";
out.fc_remove = "Supprimer définitivement"; out.fc_remove = "Supprimer définitivement";
@ -218,9 +240,47 @@ define(function () {
out.fo_unableToRestore = "Impossible de restaurer ce fichier à son emplacement d'origine. Vous pouvez essayer de le déplacer à un nouvel emplacement."; out.fo_unableToRestore = "Impossible de restaurer ce fichier à son emplacement d'origine. Vous pouvez essayer de le déplacer à un nouvel emplacement.";
out.fo_unavailableName = "Un fichier ou dossier avec le même nom existe déjà au nouvel emplacement. Renommez cet élément avant d'essayer à nouveau."; out.fo_unavailableName = "Un fichier ou dossier avec le même nom existe déjà au nouvel emplacement. Renommez cet élément avant d'essayer à nouveau.";
// login
out.login_login = "Connexion";
out.login_nologin = "Documents récents de ce navigateur";
out.login_register = "Inscription";
out.logoutButton = "Déconnexion";
out.login_migrate = "Souhaitez-vous importer les données existantes de votre session anonyme ?";
out.username_label = "Nom d'utilisateur : ";
out.displayname_label = "Nom affiché : ";
out.login_username = "votre nom d'utilisateur";
out.login_password = "votre mot de passe";
out.login_confirm = "confirmer votre mot de passe";
out.login_remember = "Se souvenir de moi";
out.login_cancel_prompt = "...ou si vous avez entré le mauvais nom d'utilisateur ou mot de passe, annulez pour essayer à nouveau.";
out.login_registerSuccess = "Inscription réalisée avec succès. Prenez soin de ne pas oublier votre mot de passe !";
out.login_passwordMismatch = "Les deux mots de passe entrés sont différents. Essayez à nouveau.";
out.login_warning = [
'<h1 id="warning">ATTENTION</h1>',
'<p>Cryptpad sauve vos données personnelles dans un document temps-réel chiffré, comme pour tous les autres types de documents temps-réel.</p>',
'<p>Votre nom d\'utilisateur et votre mot de passe ne sont jamais envoyés au serveur de manière non-chiffré.</p>',
'<p>Ainsi, si vous oubliez votre nom d\'utilisateur ou votre mot de passe, il n\'y a absolument rien que nous puissions faire pour retrouver vos informations perdues.</p>',
'<p><strong>Prenez soin de ne surtout pas oublier votre nom d\'utilisateur OU votre mot de passe !</strong></p>',
].join('\n');
out.login_hashing = "Traitement de vos identifiants, cela peut nécessiter quelques instants.";
out.login_no_user = "Il n'y a aucun utilisateur associé au nom et au mot de passe que vous avez entré.";
out.login_confirm_password = "Veuillez taper de nouveau votre mot de passe pour vous inscrire...";
out.loginText = '<p>Votre nom d\'utilisateur et votre mot d epasse sont utilisés pour générer une clé unique qui reste inconnue de notre serveur.</p>\n' +
'<p>Faîtes attention de ne pas perdre vos identifiants, puisqu\'il est impossible de les récupérer</p>';
// index.html // index.html
out.main_p1 = 'CryptPad est l\'éditeur collaboratif en temps réel <strong>zero knowledge</strong>. Le chiffrement est effectué depuis votre navigateur, ce qui protège les données contre le serveur, le cloud, et la NSA. La clé de chiffrement est stockée dans l\'<a href="https://fr.wikipedia.org/wiki/Identificateur_de_fragment">identifieur de fragment</a> de l\'URL qui n\'est jamais envoyée au serveur mais est accessible depuis javascript, de sorte qu\'en partageant l\'URL, vous donnez l\'accès au pad à ceux qui souhaitent participer.'; //out.main_p1 = 'CryptPad est l\'éditeur collaboratif en temps réel <strong>zero knowledge</strong>. Le chiffrement est effectué depuis votre navigateur, ce qui protège les données contre le serveur, le cloud, et la NSA. La clé de chiffrement est stockée dans l\'<a href="https://fr.wikipedia.org/wiki/Identificateur_de_fragment">identifieur de fragment</a> de l\'URL qui n\'est jamais envoyée au serveur mais est accessible depuis javascript, de sorte qu\'en partageant l\'URL, vous donnez l\'accès au pad à ceux qui souhaitent participer.';
out.main_p1 = "<h2>Collaborez en tout confiance</h2><br>Développez vos idées collaborativement grâce à des documents partagés en temps-réel, tout en gardant vos données personnelles invisibles, même pour nous, avec la technologie <strong>Zero Knowledge</strong>.";
out.main_p2 = 'Ce projet utilise l\'éditeur visuel (WYSIWYG) <a href="http://ckeditor.com/">CKEditor</a>, l\'éditeur de code source <a href="https://codemirror.net/">CodeMirror</a>, et le moteur temps-réel <a href="https://github.com/xwiki-contrib/chainpad">ChainPad</a>.'; out.main_p2 = 'Ce projet utilise l\'éditeur visuel (WYSIWYG) <a href="http://ckeditor.com/">CKEditor</a>, l\'éditeur de code source <a href="https://codemirror.net/">CodeMirror</a>, et le moteur temps-réel <a href="https://github.com/xwiki-contrib/chainpad">ChainPad</a>.';
out.main_howitworks = 'Comment ça fonctionne'; out.main_howitworks = 'Comment ça fonctionne';
out.main_howitworks_p1 = 'CryptPad utilise une variante de l\'algorithme d\'<a href="https://en.wikipedia.org/wiki/Operational_transformation">Operational transformation</a> qui est capable de trouver un consensus distribué en utilisant <a href="https://bitcoin.org/bitcoin.pdf">une chaîne de bloc Nakamoto</a>, un outil popularisé par le <a href="https://fr.wikipedia.org/wiki/Bitcoin">Bitcoin</a>. De cette manière, l\'algorithme évite la nécessité d\'utiliser un serveur central pour résoudre les conflits d\'édition de l\'Operational Transformation, et sans ce besoin de résolution des conflits le serveur peut rester ignorant du contenu qui est édité dans le pad.'; out.main_howitworks_p1 = 'CryptPad utilise une variante de l\'algorithme d\'<a href="https://en.wikipedia.org/wiki/Operational_transformation">Operational transformation</a> qui est capable de trouver un consensus distribué en utilisant <a href="https://bitcoin.org/bitcoin.pdf">une chaîne de bloc Nakamoto</a>, un outil popularisé par le <a href="https://fr.wikipedia.org/wiki/Bitcoin">Bitcoin</a>. De cette manière, l\'algorithme évite la nécessité d\'utiliser un serveur central pour résoudre les conflits d\'édition de l\'Operational Transformation, et sans ce besoin de résolution des conflits le serveur peut rester ignorant du contenu qui est édité dans le pad.';
@ -234,10 +294,19 @@ define(function () {
out.table_created = 'Créé le'; out.table_created = 'Créé le';
out.table_last = 'Dernier accès'; out.table_last = 'Dernier accès';
out.button_newpad = 'CRÉER UN PAD WYSIWYG'; out.button_newpad = 'Nouveau document texte';
out.button_newcode = 'CRÉER UN PAD DE CODE'; out.button_newcode = 'Nouvelle page de code';
out.button_newpoll = 'CRÉER UN SONDAGE'; out.button_newpoll = 'Nouveau sondage';
out.button_newslide = 'CRÉER UNE PRÉSENTATION'; out.button_newslide = 'Nouvelle présentation';
out.form_title = "Tous vos pads, partout où vous allez !";
out.form_username = "Nom d'utilisateur";
out.form_password = "Mot de passe";
out.about = "À propos";
out.privacy = "Vie privée";
out.contact = "Contact";
out.terms = "Conditions";
// privacy.html // privacy.html

@ -9,7 +9,7 @@ define(function () {
out.main_slogan = "Unity is Strength - Collaboration is Key"; out.main_slogan = "Unity is Strength - Collaboration is Key";
out.type = {}; out.type = {};
out.type.pad = 'Pad'; out.type.pad = 'Rich text';
out.type.code = 'Code'; out.type.code = 'Code';
out.type.poll = 'Poll'; out.type.poll = 'Poll';
out.type.slide = 'Presentation'; out.type.slide = 'Presentation';
@ -44,6 +44,8 @@ define(function () {
out.editor = "editor"; out.editor = "editor";
out.editors = "editors"; out.editors = "editors";
out.language = "Language";
out.greenLight = "Everything is working fine"; out.greenLight = "Everything is working fine";
out.orangeLight = "Your slow connection may impact your experience"; out.orangeLight = "Your slow connection may impact your experience";
out.redLight = "You are disconnected from the session"; out.redLight = "You are disconnected from the session";
@ -61,6 +63,9 @@ define(function () {
out.userButton = 'USER'; out.userButton = 'USER';
out.userButtonTitle = 'Change your username'; out.userButtonTitle = 'Change your username';
out.changeNamePrompt = 'Change your name (leave empty to be anonymous): '; out.changeNamePrompt = 'Change your name (leave empty to be anonymous): ';
out.user_rename = "Change display name";
out.user_displayName = "Display name";
out.user_accountName = "Account name";
out.renameButton = 'RENAME'; out.renameButton = 'RENAME';
out.renameButtonTitle = 'Change the title under which this document is listed on your home page'; out.renameButtonTitle = 'Change the title under which this document is listed on your home page';
@ -77,6 +82,9 @@ define(function () {
out.shareSuccess = 'Copied URL to clipboard'; out.shareSuccess = 'Copied URL to clipboard';
out.shareFailed = "Failed to copy URL to clipboard"; out.shareFailed = "Failed to copy URL to clipboard";
out.newPadButton = 'New';
out.newPadButtonTitle = 'Create a new document';
out.presentButton = 'PRESENT'; out.presentButton = 'PRESENT';
out.presentButtonTitle = "Enter presentation mode"; out.presentButtonTitle = "Enter presentation mode";
out.presentSuccess = 'Hit ESC to exit presentation mode'; out.presentSuccess = 'Hit ESC to exit presentation mode';
@ -114,7 +122,7 @@ define(function () {
out.okButton = 'OK (enter)'; out.okButton = 'OK (enter)';
out.cancel = "cancel"; out.cancel = "Cancel";
out.cancelButton = 'Cancel (esc)'; out.cancelButton = 'Cancel (esc)';
out.forget = "Forget"; out.forget = "Forget";
@ -171,13 +179,14 @@ define(function () {
// File manager // File manager
out.fm_rootName = "My documents"; out.fm_rootName = "Documents";
out.fm_trashName = "Trash"; out.fm_trashName = "Trash";
out.fm_unsortedName = "Unsorted files"; out.fm_unsortedName = "Unsorted files";
out.fm_filesDataName = "All files"; out.fm_filesDataName = "All files";
out.fm_templateName = "Templates"; out.fm_templateName = "Templates";
out.fm_newButton = "New";
out.fm_newFolder = "New folder"; out.fm_newFolder = "New folder";
out.fm_newFolderButton = "NEW FOLDER"; out.fm_folder = "Folder";
out.fm_folderName = "Folder name"; out.fm_folderName = "Folder name";
out.fm_numberOfFolders = "# of folders"; out.fm_numberOfFolders = "# of folders";
out.fm_numberOfFiles = "# of files"; out.fm_numberOfFiles = "# of files";
@ -187,9 +196,10 @@ define(function () {
out.fm_creation = "Creation"; out.fm_creation = "Creation";
out.fm_forbidden = "Forbidden action"; out.fm_forbidden = "Forbidden action";
out.fm_originalPath = "Original path"; out.fm_originalPath = "Original path";
out.fm_noname = "Untitled Document";
out.fm_emptyTrashDialog = "Are you sure you want to empty the trash?"; out.fm_emptyTrashDialog = "Are you sure you want to empty the trash?";
out.fm_removeSeveralPermanentlyDialog = "Are you sure you want to remove these {0} elements from the trash permanently?"; out.fm_removeSeveralPermanentlyDialog = "Are you sure you want to remove these {0} elements from the trash permanently?";
out.fm_removePermanentlyDialog = "Are you sure you want to remove {0} permanently?"; out.fm_removePermanentlyDialog = "Are you sure you want to remove that element permanently?";
out.fm_removeSeveralDialog = "Are you sure you want to move these {0} elements to the trash?"; out.fm_removeSeveralDialog = "Are you sure you want to move these {0} elements to the trash?";
out.fm_removeDialog = "Are you sure you want to move {0} to the trash?"; out.fm_removeDialog = "Are you sure you want to move {0} to the trash?";
out.fm_restoreDialog = "Are you sure you want to restore {0} to its previous location?"; out.fm_restoreDialog = "Are you sure you want to restore {0} to its previous location?";
@ -197,13 +207,20 @@ define(function () {
out.fm_contextMenuError = "Unable to open the context menu for that element. If the problem persist, try to reload the page."; out.fm_contextMenuError = "Unable to open the context menu for that element. If the problem persist, try to reload the page.";
out.fm_selectError = "Unable to select the targetted element. If the problem persist, try to reload the page."; out.fm_selectError = "Unable to select the targetted element. If the problem persist, try to reload the page.";
out.fm_info_root = "Create as many nested folders here as you want to sort your files."; out.fm_info_root = "Create as many nested folders here as you want to sort your files.";
out.fm_info_unsorted = 'Contains all the files you\'ve visited that are not yet sorted in "My Documents" or moved to the "Trash".'; // "My Documents" should match with the "out.fm_rootName" key, and "Trash" with "out.fm_trashName" out.fm_info_unsorted = 'Contains all the files you\'ve visited that are not yet sorted in "Documents" or moved to the "Trash".'; // "My Documents" should match with the "out.fm_rootName" key, and "Trash" with "out.fm_trashName"
out.fm_info_template = 'Contains all the pads stored as templates and that you can re-use when you create a new document.';
out.fm_info_trash = 'Files deleted from the trash are also removed from "All files" and it is impossible to recover them from the file manager.'; // Same here for "All files" and "out.fm_filesDataName" out.fm_info_trash = 'Files deleted from the trash are also removed from "All files" and it is impossible to recover them from the file manager.'; // Same here for "All files" and "out.fm_filesDataName"
out.fm_info_allFiles = 'Contains all the files from "My Documents", "Unsorted" and "Trash". You can\'t move or remove files from here.'; // Same here 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_alert_backupUrl = "Backup URL for this drive.<br>" +
"It is <strong>highly recommended</strong> that you keep ip for yourself only.<br>" +
"You can use it to retrieve all your files in case your browser memory got erased.<br>" +
"Anybody with that URL can edit or remove all the files in your file manager.<br>" +
'<input type="text" id="fm_backupUrl" value="{0}"/>';
// File - Context menu // File - Context menu
out.fc_newfolder = "New folder"; out.fc_newfolder = "New folder";
out.fc_rename = "Rename"; out.fc_rename = "Rename";
out.fc_open = "Open"; out.fc_open = "Open";
out.fc_open_ro = "Open (read-only)";
out.fc_delete = "Delete"; out.fc_delete = "Delete";
out.fc_restore = "Restore"; out.fc_restore = "Restore";
out.fc_remove = "Delete permanently"; out.fc_remove = "Delete permanently";
@ -221,9 +238,10 @@ define(function () {
out.fo_unavailableName = "A file or a folder with the same name already exist at the new location. Rename the element and try again."; out.fo_unavailableName = "A file or a folder with the same name already exist at the new location. Rename the element and try again.";
// login // login
out.login_login = "log in"; out.login_login = "Log in";
out.login_register = "register"; out.login_nologin = "Your browser's recent pads";
out.logoutButton = "log out"; out.login_register = "Sign up";
out.logoutButton = "Log out";
out.login_migrate = "Would you like to migrate existing data from your anonymous session?"; out.login_migrate = "Would you like to migrate existing data from your anonymous session?";
@ -233,11 +251,11 @@ define(function () {
out.login_username = "your username"; out.login_username = "your username";
out.login_password = "your password"; out.login_password = "your password";
out.login_confirm = "confirm your password"; out.login_confirm = "confirm your password";
out.login_remember = "remember me"; out.login_remember = "Remember me";
out.login_cancel_prompt = "...or if you may have entered the wrong username or password, cancel to try again."; out.login_cancel_prompt = "...or if you may have entered the wrong username or password, cancel to try again.";
out.login_registerSuccess = "registered successfully. Make sure you don't forget your password!" out.login_registerSuccess = "registered successfully. Make sure you don't forget your password!";
out.login_passwordMismatch = "The two passwords you entered do not match. Try again"; out.login_passwordMismatch = "The two passwords you entered do not match. Try again";
out.login_warning = [ out.login_warning = [
@ -248,11 +266,6 @@ define(function () {
'<p><strong>Make sure you do not forget your username and password!</strong></p>', '<p><strong>Make sure you do not forget your username and password!</strong></p>',
].join('\n'); ].join('\n');
out.login_logout = [
//'<p>It seems you are already logged in</p>',
//'<p>Would you like to log out and authenticate as another user?</p>',
].join('\n');
out.login_hashing = "Hashing your password, this might take some time."; out.login_hashing = "Hashing your password, this might take some time.";
out.login_no_user = "There is no user associated with the username and password that you entered."; out.login_no_user = "There is no user associated with the username and password that you entered.";
@ -263,7 +276,10 @@ define(function () {
// index.html // index.html
out.main_p1 = 'CryptPad is the <strong>zero knowledge</strong> realtime collaborative editor. Encryption carried out in your web browser protects the data from the server, the cloud, and the NSA. The secret encryption key is stored in the URL <a href="https://en.wikipedia.org/wiki/Fragment_identifier">fragment identifier</a> which is never sent to the server but is available to javascript so by sharing the URL, you give authorization to others who want to participate.'; //out.main_p1 = 'CryptPad is the <strong>zero knowledge</strong> realtime collaborative editor. Encryption carried out in your web browser protects the data from the server, the cloud, and the NSA. The secret encryption key is stored in the URL <a href="https://en.wikipedia.org/wiki/Fragment_identifier">fragment identifier</a> which is never sent to the server but is available to javascript so by sharing the URL, you give authorization to others who want to participate.';
//out.main_p1 = "Type quick documents with friends and colleagues.<br>With <strong>Zero Knowledge</strong> technology, the server doesn't know what you're doing.";
out.main_p1 = "<h2>Collaborate in Confidence</h2><br> Grow your ideas together with shared documents while <strong>Zero Knowledge</strong> technology secures your privacy; even from us.";
out.main_p2 = 'This project uses the <a href="http://ckeditor.com/">CKEditor</a> Visual Editor, <a href="https://codemirror.net/">CodeMirror</a>, and the <a href="https://github.com/xwiki-contrib/chainpad">ChainPad</a> realtime engine.'; out.main_p2 = 'This project uses the <a href="http://ckeditor.com/">CKEditor</a> Visual Editor, <a href="https://codemirror.net/">CodeMirror</a>, and the <a href="https://github.com/xwiki-contrib/chainpad">ChainPad</a> realtime engine.';
out.main_howitworks = 'How It Works'; out.main_howitworks = 'How It Works';
out.main_howitworks_p1 = 'CryptPad uses a variant of the <a href="https://en.wikipedia.org/wiki/Operational_transformation">Operational transformation</a> algorithm which is able to find distributed consensus using a <a href="https://bitcoin.org/bitcoin.pdf">Nakamoto Blockchain</a>, a construct popularized by <a href="https://en.wikipedia.org/wiki/Bitcoin">Bitcoin</a>. This way the algorithm can avoid the need for a central server to resolve Operational Transform Edit Conflicts and without the need for resolving conflicts, the server can be kept unaware of the content which is being edited on the pad.'; out.main_howitworks_p1 = 'CryptPad uses a variant of the <a href="https://en.wikipedia.org/wiki/Operational_transformation">Operational transformation</a> algorithm which is able to find distributed consensus using a <a href="https://bitcoin.org/bitcoin.pdf">Nakamoto Blockchain</a>, a construct popularized by <a href="https://en.wikipedia.org/wiki/Bitcoin">Bitcoin</a>. This way the algorithm can avoid the need for a central server to resolve Operational Transform Edit Conflicts and without the need for resolving conflicts, the server can be kept unaware of the content which is being edited on the pad.';
@ -277,10 +293,19 @@ define(function () {
out.table_created = 'Created'; out.table_created = 'Created';
out.table_last = 'Last Accessed'; out.table_last = 'Last Accessed';
out.button_newpad = 'NEW RICH TEXT PAD'; out.button_newpad = 'New Rich Text pad';
out.button_newcode = 'NEW CODE PAD'; out.button_newcode = 'New Code pad';
out.button_newpoll = 'NEW POLL'; out.button_newpoll = 'New Poll';
out.button_newslide = 'NEW PRESENTATION'; out.button_newslide = 'New Presentation';
out.form_title = "All your pads, everywhere!";
out.form_username = "Username";
out.form_password = "Password";
out.about = "About";
out.privacy = "Privacy";
out.contact = "Contact";
out.terms = "ToS";
// privacy.html // privacy.html

@ -74,7 +74,7 @@ define(function () {
out.forgetButton = 'ESQUECER'; out.forgetButton = 'ESQUECER';
out.forgetButtonTitle = 'Remova este documento da listagem da sua página'; out.forgetButtonTitle = 'Remova este documento da listagem da sua página';
out.forgetPrompt = 'Cliando OK você irá remover o endereço deste bloco de notas do armazenamento local, você tem certeza?'; out.forgetPrompt = 'Clicando OK você irá remover o endereço deste bloco de notas do armazenamento local, você tem certeza?';
out.shareButton = 'Compartilhar'; out.shareButton = 'Compartilhar';
out.shareButtonTitle = "Copiar endereço do clipboard"; out.shareButtonTitle = "Copiar endereço do clipboard";
@ -181,7 +181,7 @@ define(function () {
out.table_type = 'Tipo'; out.table_type = 'Tipo';
out.table_link = 'Link'; out.table_link = 'Link';
out.table_created = 'Criado'; out.table_created = 'Criado';
out.table_last = 'Último acessado'; out.table_last = 'Último acesso';
out.button_newpad = 'NOVO BLOCO WYSIWYG'; out.button_newpad = 'NOVO BLOCO WYSIWYG';
out.button_newcode = 'NOVO BLOCO DE NOTAS'; out.button_newcode = 'NOVO BLOCO DE NOTAS';
@ -192,7 +192,7 @@ define(function () {
out.policy_title = 'Política de privacidade do Cryptpad'; out.policy_title = 'Política de privacidade do Cryptpad';
out.policy_whatweknow = 'O que nós sabemos sobre você'; out.policy_whatweknow = 'O que nós sabemos sobre você';
out.policy_whatweknow_p1 = 'Por der uma aplicação hospedada na web, O Cryptpad tem acesso ao metadados expostos pelo protocolo HTTP. Isso inclui seu endereço IP, e vários cabeçalhos do HTTP que podem ser usados para identificar seu browser particular. Você pode ver que informações seu navegador está compartilhando ao visitar <a target="_blank" rel="noopener noreferrer" href="https://www.whatismybrowser.com/detect/what-http-headers-is-my-browser-sending" title="Que cabeçalhos meu navegador está disponibilizando">WhatIsMyBrowser.com</a>.'; out.policy_whatweknow_p1 = 'Por ser uma aplicação hospedada na web, O Cryptpad tem acesso aos metadados expostos pelo protocolo HTTP. Isso inclui seu endereço IP, e vários cabeçalhos do HTTP que podem ser usados para identificar seu browser particular. Você pode ver que informações seu navegador está compartilhando ao visitar <a target="_blank" rel="noopener noreferrer" href="https://www.whatismybrowser.com/detect/what-http-headers-is-my-browser-sending" title="Que cabeçalhos meu navegador está disponibilizando">WhatIsMyBrowser.com</a>.';
out.policy_whatweknow_p2 = 'Nós usamos a plataforma de análise <a href="https://piwik.org/" target="_blank" rel="noopener noreferrer" title="plataforma analítica open source">Piwik</a>, uma plataforma analítica open source, para aprender mais sobre nossos usos. Piwik nos informa como você encontrou o Cryptpad, via digitação direta, através de mecanismos de busca, ou via link de outro serviço web como o Reddit ou o Twitter. Nós também aprendemos com suas visitas, que links você clica enquanto está em nossas páginas de informações, e quanto tempo você fica nestas páginas.'; out.policy_whatweknow_p2 = 'Nós usamos a plataforma de análise <a href="https://piwik.org/" target="_blank" rel="noopener noreferrer" title="plataforma analítica open source">Piwik</a>, uma plataforma analítica open source, para aprender mais sobre nossos usos. Piwik nos informa como você encontrou o Cryptpad, via digitação direta, através de mecanismos de busca, ou via link de outro serviço web como o Reddit ou o Twitter. Nós também aprendemos com suas visitas, que links você clica enquanto está em nossas páginas de informações, e quanto tempo você fica nestas páginas.';
out.policy_whatweknow_p3 = 'Estas ferramentas de análise são utilizadas apenas com fins de informação. Nós não coletamos nenhuma informação sobre sua utilização em nossas aplicações de zero-knowledge.'; out.policy_whatweknow_p3 = 'Estas ferramentas de análise são utilizadas apenas com fins de informação. Nós não coletamos nenhuma informação sobre sua utilização em nossas aplicações de zero-knowledge.';
out.policy_howweuse = 'Como utilizamos o que nós aprendemos'; out.policy_howweuse = 'Como utilizamos o que nós aprendemos';
@ -203,17 +203,17 @@ define(function () {
out.policy_links = 'Links para outros sites'; out.policy_links = 'Links para outros sites';
out.policy_links_p1 = 'Este site contém ligações para outros sites, incluindo aqueles produzidos por terceiros. Nós não nos responsabilizamos pelas práticas de privacidade ou o conteúdo destes sites. Como regra geral, links para páginas fora de nosso domínio são lançadas em novas janelas ou abas, para deixar claro a todos os visitantes que eles estão deixando o site Cryptpad.fr.'; out.policy_links_p1 = 'Este site contém ligações para outros sites, incluindo aqueles produzidos por terceiros. Nós não nos responsabilizamos pelas práticas de privacidade ou o conteúdo destes sites. Como regra geral, links para páginas fora de nosso domínio são lançadas em novas janelas ou abas, para deixar claro a todos os visitantes que eles estão deixando o site Cryptpad.fr.';
out.policy_ads = 'Publicidade'; out.policy_ads = 'Publicidade';
out.policy_ads_p1 = 'Nós não disponibilizamos publicidade online, porém podemos prover links de acesso para obtenção de financiamento para auxiliar em nossa pesquisa.'; out.policy_ads_p1 = 'Nós não disponibilizamos publicidade online, porém podemos prover links de acesso para obtenção de financiamento para auxiliar em nossa pesquisa e desenvolvimento.';
out.policy_choices = 'As escolhas que você tem'; out.policy_choices = 'As escolhas que você tem';
out.policy_choices_open = 'Nosso código fonte é open source, portanto você sempre tem a opção de hospedar sua própria instância do Cryptpad.'; out.policy_choices_open = 'Nosso código fonte é open source, portanto você sempre tem a opção de hospedar sua própria instância do Cryptpad.';
out.policy_choices_vpn = 'Se você deseja usar nosso site principal, porém não deseja expor seu endereço IP, Você pode se proteger utilizando o <a href="https://www.torproject.org/projects/torbrowser.html.en" title="Baixe o tor" target="_blank" rel="noopener noreferrer">Nvegador seguro Tor</a>, ou uma <a href="https://riseup.net/en/vpn" title="VPNs providas pelo Riseup" target="_blank" rel="noopener noreferrer">VPN</a>.'; out.policy_choices_vpn = 'Se você deseja usar nosso site principal, porém não deseja expor seu endereço IP, Você pode se proteger utilizando o <a href="https://www.torproject.org/projects/torbrowser.html.en" title="Baixe o tor" target="_blank" rel="noopener noreferrer">Navegador seguro Tor</a>, ou uma <a href="https://riseup.net/en/vpn" title="VPNs providas pelo Riseup" target="_blank" rel="noopener noreferrer">VPN</a>.';
out.policy_choices_ads = 'Se você deseja apenas bloquear nossa plataforma analítica, você pode utilizar ferramentas de bloqueio de propagandas como o <a href="https://www.eff.org/privacybadger" title="baixe o privacy badger" target="_blank" rel="noopener noreferrer">Privacy Badger</a>.'; out.policy_choices_ads = 'Se você deseja apenas bloquear nossa plataforma analítica, você pode utilizar ferramentas de bloqueio de propagandas como o <a href="https://www.eff.org/privacybadger" title="baixe o privacy badger" target="_blank" rel="noopener noreferrer">Privacy Badger</a>.';
// terms.html // terms.html
out.tos_title = "Termos de serviço doCryptpad"; out.tos_title = "Termos de serviço doCryptpad";
out.tos_legal = "Pedimos encarecidamente que, como usuário desta plataforma, você evite a prática de quaisquer atos ilegais e que evite a utilização maliciosa e/ou abusiva desta plataforma."; out.tos_legal = "Pedimos encarecidamente que, como usuário desta plataforma, você evite a prática de quaisquer atos ilegais e que evite a utilização maliciosa e/ou abusiva desta plataforma.";
out.tos_availability = "Nós esperamos que você ache este serviço útil, porém não não podemos garantir a disponibilidade constante ou a alta performance do mesmo. Por favor, mantenha um backup dos seus dados como forma de segurança adicional."; out.tos_availability = "Nós esperamos que você ache este serviço útil, porém nós não podemos garantir a disponibilidade constante ou a alta performance do mesmo. Por favor, mantenha um backup dos seus dados como forma de segurança adicional.";
out.tos_e2ee = "Os documentos do CryptPad podem ser modificados por qualquer um que conseguir adivinhar ou obter de qualquer forma o seu identificador único. Nós recomendamos que você utilize criptografia ponto a ponto de mensagens (e2ee) sempre que possível para compartilhar suas URL's. Nós não assumimos qualquer responsabilidade sobre chaves e/ou URLs e seus respectivos conteúdos vazadas para o público."; out.tos_e2ee = "Os documentos do CryptPad podem ser modificados por qualquer um que conseguir adivinhar ou obter de qualquer forma o seu identificador único. Nós recomendamos que você utilize criptografia ponto a ponto de mensagens (e2ee) sempre que possível para compartilhar suas URL's. Nós não assumimos qualquer responsabilidade sobre chaves e/ou URLs e seus respectivos conteúdos vazadas para o público.";
out.tos_logs = "Os Metadados providos pelo seu navegador para nosso servidor podem ser armazenados com o propósito de manter o serviço em funcionamento"; out.tos_logs = "Os Metadados providos pelo seu navegador para nosso servidor podem ser armazenados com o propósito de manter o serviço em funcionamento";
out.tos_3rdparties = "Nós não disponibilizamos dados individuais para terceiros, salvo quando requisitado legalmente."; out.tos_3rdparties = "Nós não disponibilizamos dados individuais para terceiros, salvo quando requisitado legalmente.";

@ -0,0 +1,23 @@
version: '2'
services:
cryptpad:
build:
context: .
args:
- VERSION=${VERSION}
image: "xwiki/cryptpad:${VERSION}"
hostname: cryptpad
labels:
- traefik.port=3000
- traefik.frontend.passHostHeader=true
environment:
- USE_SSL=${USE_SSL}
- STORAGE=${STORAGE}
- LOG_TO_STDOUT=${LOG_TO_STDOUT}
restart: always
volumes:
- ./data/files:/cryptpad/datastore:rw
- ./data/customize:/cryptpad/customize:rw

@ -82,6 +82,10 @@ To test CryptPad, go to http://your.server:3000/assert/
You can use WebDriver to run this test automatically by running TestSelenium.js but you will need chromedriver installed. You can use WebDriver to run this test automatically by running TestSelenium.js but you will need chromedriver installed.
If you use Mac, you can `brew install chromedriver`. If you use Mac, you can `brew install chromedriver`.
# Setup using Docker
See [Cryptpad-Docker](cryptpad-docker.md)
## Security ## Security
CryptPad is *private*, not *anonymous*. Privacy protects your data, anonymity protects you. CryptPad is *private*, not *anonymous*. Privacy protects your data, anonymity protects you.

@ -7,10 +7,10 @@ var Https = require('https');
var Fs = require('fs'); var Fs = require('fs');
var WebSocketServer = require('ws').Server; var WebSocketServer = require('ws').Server;
var NetfluxSrv = require('./NetfluxWebsocketSrv'); var NetfluxSrv = require('./NetfluxWebsocketSrv');
var WebRTCSrv = require('./WebRTCSrv');
var config = require('./config'); var config = require('./config');
var websocketPort = config.websocketPort || config.httpPort; var websocketPort = config.websocketPort || config.httpPort;
var useSecureWebsockets = config.useSecureWebsockets || false;
// support multiple storage back ends // support multiple storage back ends
var Storage = require(config.storage||'./storage/file'); var Storage = require(config.storage||'./storage/file');
@ -46,7 +46,7 @@ Fs.exists(__dirname + "/customize", function (e) {
// FIXME I think this is a regression caused by a recent PR // FIXME I think this is a regression caused by a recent PR
// correct this hack without breaking the contributor's intended behaviour. // correct this hack without breaking the contributor's intended behaviour.
var mainPages = config.mainPages || ['index', 'privacy', 'terms', 'about']; var mainPages = config.mainPages || ['index', 'privacy', 'terms', 'about', 'contact'];
var mainPagePattern = new RegExp('^\/(' + mainPages.join('|') + ').html$'); var mainPagePattern = new RegExp('^\/(' + mainPages.join('|') + ').html$');
app.get(mainPagePattern, Express.static(__dirname + '/customize.dist')); app.get(mainPagePattern, Express.static(__dirname + '/customize.dist'));
@ -82,8 +82,8 @@ app.get('/api/config', function(req, res){
var host = req.headers.host.replace(/\:[0-9]+/, ''); var host = req.headers.host.replace(/\:[0-9]+/, '');
res.setHeader('Content-Type', 'text/javascript'); res.setHeader('Content-Type', 'text/javascript');
res.send('define(' + JSON.stringify({ res.send('define(' + JSON.stringify({
websocketPath: config.websocketPath, websocketPath: config.useExternalWebsocket ? undefined : config.websocketPath,
websocketURL:'ws' + ((httpsOpts) ? 's' : '') + '://' + host + ':' + websocketURL:'ws' + ((useSecureWebsockets) ? 's' : '') + '://' + host + ':' +
websocketPort + '/cryptpad_websocket', websocketPort + '/cryptpad_websocket',
}) + ');'); }) + ');');
}); });
@ -95,12 +95,14 @@ httpServer.listen(config.httpPort,config.httpAddress,function(){
}); });
var wsConfig = { server: httpServer }; var wsConfig = { server: httpServer };
if (websocketPort !== config.httpPort) {
console.log("setting up a new websocket server"); if(!config.useExternalWebsocket) {
wsConfig = { port: websocketPort}; if (websocketPort !== config.httpPort) {
console.log("setting up a new websocket server");
wsConfig = { port: websocketPort};
}
var wsSrv = new WebSocketServer(wsConfig);
Storage.create(config, function (store) {
NetfluxSrv.run(store, wsSrv, config);
});
} }
var wsSrv = new WebSocketServer(wsConfig);
Storage.create(config, function (store) {
NetfluxSrv.run(store, wsSrv, config);
WebRTCSrv.run(wsSrv);
});

@ -1,58 +0,0 @@
# This is Cryptpad
There are quite a few realtime editors packed into this installation.
Most are prototypes that could use a lot of work.
All editors make use of Cryptpad's end to end encryption.
Some of them have much better UI.
## /pad/
Pad is the main feature of Cryptpad. It features a CKEditor for realtime WYSIWYG editing.
## /code/
Code has syntax highlighting features.
## /sheet/
Sheet is under development. It will feature realtime collaborative spreadsheets.
## /text/
Text is a very simple encrypted plain text editor with no highlighting.
## /render/
Render takes advantage of the fact that multiple editors can both use the same 'channel' at once.
Channel, in this sense, refers to part of the unique hash of a page which groups messages together.
If you visit a /text/ and a /render/ page simultaneously, the changes you make in /text/ will be
rendered as markdown in /render/. You can't edit in /render/ directly, but it adds value to other
editors by allowing a realtime preview of your work.
## /vdom/
Vdom is under heavy development, and features an alternative approach to the realtime WYSIWYG
editor. It syncs a representation of a virtual-dom instead of syncing the HTML itself. In practice,
this means that there are fewer inconsistencies between different browsers' representations of the dom.
This makes the codebase much simpler, and eliminates many classes of bugs. It's still far from perfect,
but it is quite promising.
## /hack/
Hack leaves it to the user to decide whether XSS (Cross site scripting) is a bug or a feature.
It exposes a realtime text pad to multiple users, and provides a button which will cause the
contents of the pad to be passed to an `eval` call. Anyone with the hash of the page can edit
the contents, so make sure you read the code you're about to run. If you can't read it, you
probably shouldn't run it. In any case, it might be useful for pair programming or when you want
to sketch out and prototype simple demos.
## Coming soon
* style
- live editing of CSS as applied to some Lorum Ipsum
* polyweb
- a multi-featured editor which connects to multiple channels at once, for:
1. live rendered markdown
2. live style editing
3. live javascript

@ -149,7 +149,8 @@ define([
console.log('* ' + msg); console.log('* ' + msg);
}); });
return false; // No, this is crappy, it's going to cause tests to fail basically all of the time.
//return false;
} }
return true; return true;
}, "expected all translation keys in default language to be present in all translations. See console for details."); }, "expected all translation keys in default language to be present in all translations. See console for details.");

@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="cp code"> <html class="cp code">
<head> <head>
<title>CryptPad</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script data-main="main" src="/bower_components/requirejs/require.js"></script> <script data-main="main" src="/bower_components/requirejs/require.js"></script>
<script> <script>
@ -46,7 +47,10 @@
</head> </head>
<body> <body>
<div id="iframe-container"> <div id="iframe-container">
<iframe id="pad-iframe" src="inner.html"></iframe> <iframe id="pad-iframe"></iframe>
<script>
document.getElementById('pad-iframe').setAttribute('src', 'inner.html?' + new Date().getTime());
</script>
</div> </div>
</body> </body>
</html> </html>

@ -77,7 +77,10 @@ define([
} }
CodeMirror.autoLoadMode(editor, mode); CodeMirror.autoLoadMode(editor, mode);
editor.setOption('mode', mode); editor.setOption('mode', mode);
if ($select && $select.val) { $select.val(mode); } if ($select) {
var name = $select.find('a[data-value="' + mode + '"]').text() || 'Mode';
$select.find('.buttonTitle').text(name);
}
}; };
editor.setValue(Messages.codeInitialState); // HERE editor.setValue(Messages.codeInitialState); // HERE
@ -107,7 +110,7 @@ define([
} }
editor.setOption('theme', theme); editor.setOption('theme', theme);
} }
if ($select && $select.val) { $select.val(theme || 'default'); } if ($select) { $select.find('.buttonTitle').text(theme || 'Theme'); }
}; };
}()); }());
@ -175,7 +178,9 @@ define([
defaultTitle: defaultName defaultTitle: defaultName
} }
}; };
obj.metadata.title = document.title; if (!initializing) {
obj.metadata.title = document.title;
}
// set mode too... // set mode too...
obj.highlightMode = module.highlightMode; obj.highlightMode = module.highlightMode;
@ -349,6 +354,7 @@ define([
var updateMetadata = function(shjson) { var updateMetadata = function(shjson) {
// Extract the user list (metadata) from the hyperjson // Extract the user list (metadata) from the hyperjson
var json = (shjson === "") ? "" : JSON.parse(shjson); var json = (shjson === "") ? "" : JSON.parse(shjson);
var titleUpdated = false;
if (json && json.metadata) { if (json && json.metadata) {
if (json.metadata.users) { if (json.metadata.users) {
var userData = json.metadata.users; var userData = json.metadata.users;
@ -359,14 +365,27 @@ define([
updateDefaultTitle(json.metadata.defaultTitle); updateDefaultTitle(json.metadata.defaultTitle);
} }
if (typeof json.metadata.title !== "undefined") { if (typeof json.metadata.title !== "undefined") {
updateTitle(json.metadata.title); updateTitle(json.metadata.title || defaultName);
titleUpdated = true;
} }
} }
if (!titleUpdated) {
updateTitle(defaultName);
}
}; };
var onInit = config.onInit = function (info) { var onInit = config.onInit = function (info) {
userList = info.userList; userList = info.userList;
module.userName = {};
// The lastName is stored in an object passed to the toolbar so that when the user clicks on
// the "change display name" button, the prompt already knows his current name
getLastName(function (err, lastName) {
module.userName.lastName = lastName;
});
var config = { var config = {
displayed: ['useradmin', 'language', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad'],
userData: userData, userData: userData,
readOnly: readOnly, readOnly: readOnly,
ifrw: ifrw, ifrw: ifrw,
@ -375,6 +394,10 @@ define([
defaultName: defaultName, defaultName: defaultName,
suggestName: suggestName suggestName: suggestName
}, },
userName: {
setName: setName,
lastName: module.userName
},
common: Cryptpad common: Cryptpad
}; };
if (readOnly) {delete config.changeNameID; } if (readOnly) {delete config.changeNameID; }
@ -384,6 +407,7 @@ define([
var $userBlock = $bar.find('.' + Toolbar.constants.username); var $userBlock = $bar.find('.' + Toolbar.constants.username);
var $editShare = $bar.find('.' + Toolbar.constants.editShare); var $editShare = $bar.find('.' + Toolbar.constants.editShare);
var $viewShare = $bar.find('.' + Toolbar.constants.viewShare); var $viewShare = $bar.find('.' + Toolbar.constants.viewShare);
var $usernameButton = module.$userNameButton = $($bar.find('.' + Toolbar.constants.changeUsername));
var editHash; var editHash;
var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys); var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys);
@ -392,15 +416,6 @@ define([
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
} }
// Store the object sent for the "change username" button so that we can update the field value correctly
var userNameButtonObject = module.userName = {};
/* add a "change username" button */
getLastName(function (err, lastName) {
userNameButtonObject.lastName = lastName;
var $username = module.$userNameButton = Cryptpad.createButton('username', false, userNameButtonObject, setName).hide();
$userBlock.append($username);
});
/* add an export button */ /* add an export button */
var $export = Cryptpad.createButton('export', true, {}, exportText); var $export = Cryptpad.createButton('export', true, {}, exportText);
$rightside.append($export); $rightside.append($export);
@ -436,58 +451,68 @@ define([
var configureLanguage = function (cb) { var configureLanguage = function (cb) {
// FIXME this is async so make it happen as early as possible // FIXME this is async so make it happen as early as possible
var options = [];
/* Let the user select different syntax highlighting modes */ Modes.list.forEach(function (l) {
var $language = module.$language = $('<select>', { options.push({
title: 'syntax highlighting', tag: 'a',
id: 'language-mode', attributes: {
'class': 'rightside-element' 'data-value': l.mode,
}).on('change', function () { 'href': '#',
setMode($language.val()); },
onLocal(); content: l.language // Pretty name of the language value
});
}); });
var dropdownConfig = {
Modes.list.map(function (o) { text: 'Mode', // Button initial text
$language.append($('<option>', { options: options, // Entries displayed in the menu
value: o.mode, left: true, // Open to the left of the button
}).text(o.language)); };
var $block = module.$language = Cryptpad.createDropdown(dropdownConfig);
var $button = $block.find('.buttonTitle');
$block.find('a').click(function (e) {
setMode($(this).attr('data-value'));
$button.text($(this).text());
}); });
$rightside.append($language);
$rightside.append($block);
cb(); cb();
}; };
var configureTheme = function () { var configureTheme = function () {
/* Remember the user's last choice of theme using localStorage */ /* Remember the user's last choice of theme using localStorage */
var themeKey = 'CRYPTPAD_CODE_THEME'; var themeKey = 'CRYPTPAD_CODE_THEME';
var lastTheme = localStorage.getItem(themeKey) || 'default'; var lastTheme = localStorage.getItem(themeKey) || 'default';
/* Let the user select different themes */ var options = [];
var $themeDropdown = $('<select>', { Themes.forEach(function (l) {
title: 'color theme', options.push({
id: 'display-theme', tag: 'a',
'class': 'rightside-element' attributes: {
}); 'data-value': l.name,
Themes.forEach(function (o) { 'href': '#',
$themeDropdown.append($('<option>', { },
selected: o.name === lastTheme, content: l.name // Pretty name of the language value
}).val(o.name).text(o.name)); });
}); });
var dropdownConfig = {
text: 'Theme', // Button initial text
$rightside.append($themeDropdown); options: options, // Entries displayed in the menu
left: true, // Open to the left of the button
var $theme = $bar.find('select#display-theme'); };
var $block = module.$theme = Cryptpad.createDropdown(dropdownConfig);
setTheme(lastTheme, $theme); var $button = $block.find('.buttonTitle');
$theme.on('change', function () { setTheme(lastTheme, $block);
var theme = $theme.val();
console.log("Setting theme to %s", theme); $block.find('a').click(function (e) {
setTheme(theme, $theme); var theme = $(this).attr('data-value');
// remember user choices setTheme(theme, $block);
$button.text($(this).text());
localStorage.setItem(themeKey, theme); localStorage.setItem(themeKey, theme);
}); });
$rightside.append($block);
}; };
if (!readOnly) { if (!readOnly) {
@ -501,15 +526,6 @@ define([
// set the hash // set the hash
if (!readOnly) { Cryptpad.replaceHash(editHash); } if (!readOnly) { Cryptpad.replaceHash(editHash); }
Cryptpad.getPadTitle(function (err, title) {
if (err) {
console.log("Unable to get pad title");
console.error(err);
return;
}
updateTitle(title || defaultName);
});
}; };
var unnotify = module.unnotify = function () { var unnotify = module.unnotify = function () {
@ -666,8 +682,9 @@ define([
module.patchText(shjson2); module.patchText(shjson2);
} }
} }
if (oldDoc !== remoteDoc) {
notify(); notify();
}
}; };
var onAbort = config.onAbort = function (info) { var onAbort = config.onAbort = function (info) {

@ -80,7 +80,7 @@ define([
var logout = common.logout = function (cb) { var logout = common.logout = function (cb) {
[ [
fileHashKey, // fileHashKey,
userHashKey, userHashKey,
].forEach(function (k) { ].forEach(function (k) {
sessionStorage.removeItem(k); sessionStorage.removeItem(k);
@ -88,6 +88,11 @@ define([
delete localStorage[k]; delete localStorage[k];
delete sessionStorage[k]; delete sessionStorage[k];
}); });
// Make sure we have an FS_hash in localStorage before reloading all the tabs
// so that we don't end up with tabs using different anon hashes
if (!localStorage[fileHashKey]) {
localStorage[fileHashKey] = common.createRandomHash();
}
if (cb) { cb(); } if (cb) { cb(); }
}; };
@ -101,6 +106,11 @@ define([
return hash; return hash;
}; };
var isLoggedIn = common.isLoggedIn = function () {
//return typeof getStore().getLoginName() === "string";
return typeof getUserHash() === "string";
};
// var isArray = function (o) { return Object.prototype.toString.call(o) === '[object Array]'; }; // var isArray = function (o) { return Object.prototype.toString.call(o) === '[object Array]'; };
var isArray = common.isArray = $.isArray; var isArray = common.isArray = $.isArray;
@ -171,15 +181,25 @@ define([
var getHashFromKeys = common.getHashFromKeys = getEditHashFromKeys; var getHashFromKeys = common.getHashFromKeys = getEditHashFromKeys;
var specialHashes = common.specialHashes = ['iframe']; var specialHashes = common.specialHashes = ['iframe'];
/*
* Returns all needed keys for a realtime channel
* - no argument: use the URL hash or create one if it doesn't exist
* - secretHash provided: use secretHash to find the keys
*/
var getSecrets = common.getSecrets = function (secretHash) { var getSecrets = common.getSecrets = function (secretHash) {
var secret = {}; var secret = {};
var generate = function () { var generate = function () {
secret.keys = Crypto.createEditCryptor(); secret.keys = Crypto.createEditCryptor();
secret.key = Crypto.createEditCryptor().editKeyStr; secret.key = Crypto.createEditCryptor().editKeyStr;
}; };
if (/#\?path=/.test(window.location.href)) { // If we have a hash in the URL specifying a path, it means the document was created from
var arr = window.location.hash.match(/\?path=(.+)/); // the drive and should be stored at the selected path.
common.initialPath = arr[1] || undefined; if (/[?&]path=/.test(window.location.hash)) {
var patharr = window.location.hash.match(/[?&]path=([^&]+)/);
var namearr = window.location.hash.match(/[?&]name=([^&]+)/);
common.initialPath = patharr[1] || undefined;
common.initialName = namearr[1] ? decodeURIComponent(namearr[1]) : undefined;
window.location.hash = ''; window.location.hash = '';
} }
if (!secretHash && !/#/.test(window.location.href)) { if (!secretHash && !/#/.test(window.location.href)) {
@ -389,7 +409,7 @@ define([
// STORAGE // STORAGE
var setPadAttribute = common.setPadAttribute = function (attr, value, cb, legacy) { var setPadAttribute = common.setPadAttribute = function (attr, value, cb, legacy) {
getStore(legacy).set([getHash(), attr].join('.'), value, function (err, data) { getStore(legacy).setDrive([getHash(), attr].join('.'), value, function (err, data) {
cb(err, data); cb(err, data);
}); });
}; };
@ -404,7 +424,7 @@ define([
// STORAGE // STORAGE
var getPadAttribute = common.getPadAttribute = function (attr, cb, legacy) { var getPadAttribute = common.getPadAttribute = function (attr, cb, legacy) {
getStore(legacy).get([getHash(), attr].join('.'), function (err, data) { getStore(legacy).getDrive([getHash(), attr].join('.'), function (err, data) {
cb(err, data); cb(err, data);
}); });
}; };
@ -436,7 +456,7 @@ define([
// STORAGE // STORAGE
/* fetch and migrate your pad history from localStorage */ /* fetch and migrate your pad history from localStorage */
var getRecentPads = common.getRecentPads = function (cb, legacy) { var getRecentPads = common.getRecentPads = function (cb, legacy) {
getStore(legacy).get(storageKey, function (err, recentPads) { getStore(legacy).getDrive(storageKey, function (err, recentPads) {
if (isArray(recentPads)) { if (isArray(recentPads)) {
cb(void 0, migrateRecentPads(recentPads)); cb(void 0, migrateRecentPads(recentPads));
return; return;
@ -448,7 +468,7 @@ define([
// STORAGE // STORAGE
/* commit a list of pads to localStorage */ /* commit a list of pads to localStorage */
var setRecentPads = common.setRecentPads = function (pads, cb, legacy) { var setRecentPads = common.setRecentPads = function (pads, cb, legacy) {
getStore(legacy).set(storageKey, pads, function (err, data) { getStore(legacy).setDrive(storageKey, pads, function (err, data) {
cb(err, data); cb(err, data);
}); });
}; };
@ -560,7 +580,7 @@ define([
var data = makePad(href, name); var data = makePad(href, name);
renamed.push(data); renamed.push(data);
if (USE_FS_STORE && typeof(getStore().addPad) === "function") { if (USE_FS_STORE && typeof(getStore().addPad) === "function") {
getStore().addPad(href, common.initialPath, name); getStore().addPad(href, common.initialPath, common.initialName || name);
} }
} }
@ -643,8 +663,27 @@ define([
$(function() { $(function() {
// Race condition : if document.body is undefined when alertify.js is loaded, Alertify // Race condition : if document.body is undefined when alertify.js is loaded, Alertify
// won't work. We have to reset it now to make sure it uses a correct "body" // won't work. We have to reset it now to make sure it uses a correct "body"
Alertify.reset(); Alertify.reset();
// Load the new pad when the hash has changed
var oldHash = document.location.hash.slice(1);
window.onhashchange = function () {
var newHash = document.location.hash.slice(1);
var parsedOld = parseHash(oldHash);
var parsedNew = parseHash(newHash);
if (parsedOld && parsedNew && (
parsedOld.channel !== parsedNew.channel
|| parsedOld.mode !== parsedNew.mode
|| parsedOld.key !== parsedNew.key)) {
document.location.reload();
return;
}
if (parsedNew) {
oldHash = newHash;
}
};
// Everything's ready, continue...
if($('#pad-iframe').length) { if($('#pad-iframe').length) {
var $iframe = $('#pad-iframe'); var $iframe = $('#pad-iframe');
var iframe = $iframe[0]; var iframe = $iframe[0];
@ -686,8 +725,8 @@ define([
$loading.append($container); $loading.append($container);
$('body').append($loading); $('body').append($loading);
}; };
common.removeLoadingScreen = function () { common.removeLoadingScreen = function (cb) {
$('#' + LOADING).fadeOut(750); $('#' + LOADING).fadeOut(750, cb);
}; };
common.errorLoadingScreen = function (error) { common.errorLoadingScreen = function (error) {
$('.spinnerContainer').hide(); $('.spinnerContainer').hide();
@ -842,9 +881,9 @@ define([
} }
break; break;
case 'editshare': case 'editshare':
button = $('<button>', { button = $('<a>', {
title: Messages.editShareTitle, title: Messages.editShareTitle,
}).text(Messages.editShare); }).html('<span class="fa fa-users" style="font-family:FontAwesome;"></span>').append(' ' + Messages.editShare);
if (data && data.editHash) { if (data && data.editHash) {
var editHash = data.editHash; var editHash = data.editHash;
button.click(function () { button.click(function () {
@ -860,9 +899,9 @@ define([
} }
break; break;
case 'viewshare': case 'viewshare':
button = $('<button>', { button = $('<a>', {
title: Messages.viewShareTitle, title: Messages.viewShareTitle,
}).text(Messages.viewShare); }).html('<span class="fa fa-eye" style="font-family:FontAwesome;"></span>').append(' ' + Messages.viewShare);
if (data && data.viewHash) { if (data && data.viewHash) {
button.click(function () { button.click(function () {
var baseUrl = window.location.origin + window.location.pathname + '#'; var baseUrl = window.location.origin + window.location.pathname + '#';
@ -877,9 +916,9 @@ define([
} }
break; break;
case 'viewopen': case 'viewopen':
button = $('<button>', { button = $('<a>', {
title: Messages.viewOpenTitle, title: Messages.viewOpenTitle,
}).text(Messages.viewOpen); }).html('<span class="fa fa-eye" style="font-family:FontAwesome;"></span>').append(' ' + Messages.viewOpen);
if (data && data.viewHash) { if (data && data.viewHash) {
button.click(function () { button.click(function () {
var baseUrl = window.location.origin + window.location.pathname + '#'; var baseUrl = window.location.origin + window.location.pathname + '#';
@ -915,6 +954,99 @@ define([
return button; return button;
}; };
// Create a button with a dropdown menu
// input is a config object with parameters:
// - container (optional): the dropdown container (span)
// - text (optional): the button text value
// - options: array of {tag: "", attributes: {}, content: "string"}
//
// allowed options tags: ['a', 'hr', 'p']
var createDropdown = common.createDropdown = function (config) {
if (typeof config !== "object" || !isArray(config.options)) { return; }
var allowedTags = ['a', 'p', 'hr'];
var isValidOption = function (o) {
if (typeof o !== "object") { return false; }
if (!o.tag || allowedTags.indexOf(o.tag) === -1) { return false; }
return true;
};
// Container
var $container = $(config.container);
if (!config.container) {
$container = $('<span>', {
'class': 'dropdown-bar'
});
}
// Button
var $button = $('<button>', {
'class': ''
}).append($('<span>', {'class': 'buttonTitle'}).html(config.text || ""));
$('<span>', {
'class': 'fa fa-caret-down',
}).appendTo($button);
// Menu
var $innerblock = $('<div>', {'class': 'cryptpad-dropdown dropdown-bar-content'});
if (config.left) { $innerblock.addClass('left'); }
config.options.forEach(function (o) {
if (!isValidOption(o)) { return; }
$('<' + o.tag + '>', o.attributes || {}).html(o.content || '').appendTo($innerblock);
});
$container.append($button).append($innerblock);
$button.click(function (e) {
e.stopPropagation();
var state = $innerblock.is(':visible');
$('.dropdown-bar-content').hide();
$('iframe').each(function (idx, ifrw) {
$(ifrw).contents().find('.dropdown-bar-content').hide();
});
if (state) {
$innerblock.hide();
return;
}
$innerblock.show();
});
return $container;
};
// Provide $container if you want to put the generated block in another element
// Provide $initBlock if you already have the menu block and you want the content inserted in it
var createLanguageSelector = common.createLanguageSelector = function ($container, $initBlock) {
var options = [];
var languages = Messages._languages;
for (var l in languages) {
options.push({
tag: 'a',
attributes: {
'class': 'languageValue',
'data-value': l,
'href': '#',
},
content: languages[l] // Pretty name of the language value
});
}
var dropdownConfig = {
text: Messages.language, // Button initial text
options: options, // Entries displayed in the menu
left: true, // Open to the left of the button
container: $initBlock // optional
};
var $block = createDropdown(dropdownConfig);
$block.attr('id', 'language-selector');
if ($container) {
$block.appendTo($container);
}
Messages._initSelector($block);
};
/* /*
* Alertifyjs * Alertifyjs
*/ */

@ -13,7 +13,7 @@ define([
var NEW_FOLDER_NAME = Messages.fm_newFolder; var NEW_FOLDER_NAME = Messages.fm_newFolder;
var init = module.init = function (files, config) { var init = module.init = function (files, config) {
FILES_DATA = config.storageKey; FILES_DATA = config.storageKey || FILES_DATA;
var DEBUG = config.DEBUG || false; var DEBUG = config.DEBUG || false;
var logging = function () { var logging = function () {
console.log.apply(console, arguments); console.log.apply(console, arguments);
@ -58,6 +58,10 @@ define([
return path[0] && path[0] === TRASH; return path[0] && path[0] === TRASH;
}; };
var isPathInFilesData = exp.isPathInFilesData = function (path) {
return path[0] && path[0] === FILES_DATA;
};
var isFile = exp.isFile = function (element) { var isFile = exp.isFile = function (element) {
return typeof(element) === "string"; return typeof(element) === "string";
}; };
@ -241,6 +245,17 @@ define([
return path[0] === TRASH && path.length === 4; return path[0] === TRASH && path.length === 4;
}; };
var removePadAttribute = function (f) {
Object.keys(files).forEach(function (key) {
var hash = f.indexOf('#') !== -1 ? f.slice(f.indexOf('#') + 1) : null;
if (hash && key.indexOf(hash) === 0) {
debug("Deleting pad attribute in the realtime object");
files[key] = undefined;
delete files[key];
}
});
};
var checkDeletedFiles = function () { var checkDeletedFiles = function () {
// Nothing in FILES_DATA for workgroups // Nothing in FILES_DATA for workgroups
if (workgroup) { return; } if (workgroup) { return; }
@ -264,6 +279,7 @@ define([
if (idx !== -1) { if (idx !== -1) {
debug("Removing", f, "from filesData"); debug("Removing", f, "from filesData");
files[FILES_DATA].splice(idx, 1); files[FILES_DATA].splice(idx, 1);
removePadAttribute(f.href);
} }
}); });
}; };
@ -274,7 +290,7 @@ define([
var parentEl = exp.findElement(files, parentPath); var parentEl = exp.findElement(files, parentPath);
if (path.length === 4 && path[0] === TRASH) { if (path.length === 4 && path[0] === TRASH) {
files[TRASH][path[1]].splice(path[2], 1); files[TRASH][path[1]].splice(path[2], 1);
} else if (path[0] === UNSORTED) { //TODO || === TEMPLATE } else if (path[0] === UNSORTED || path[0] === TEMPLATE) {
parentEl.splice(key, 1); parentEl.splice(key, 1);
} else { } else {
parentEl[key] = undefined; parentEl[key] = undefined;
@ -283,6 +299,67 @@ define([
checkDeletedFiles(); checkDeletedFiles();
}; };
// Permanently delete multiple files at once using a list of paths
// NOTE: We have to be careful when removing elements from arrays (trash root, unsorted or template)
var deleteHrefs = function (hrefs) {
hrefs.forEach(function (obj) {
var idx = files[obj.root].indexOf(obj.href);
files[obj.root].splice(idx, 1);
});
};
var deleteMultipleTrashRoot = function (roots) {
roots.forEach(function (obj) {
var idx = files[TRASH][obj.name].indexOf(obj.el);
files[TRASH][obj.name].splice(idx, 1);
});
};
var deleteMultiplePermanently = exp.deletePathsPermanently = function (paths) {
var hrefPaths = paths.filter(isPathInHrefArray);
var rootPaths = paths.filter(isPathInRoot);
var trashPaths = paths.filter(isPathInTrash);
var hrefs = [];
hrefPaths.forEach(function (path) {
var href = exp.findElement(files, path);
hrefs.push({
root: path[0],
href: href
});
});
deleteHrefs(hrefs);
rootPaths.forEach(function (path) {
var parentPath = path.slice();
var key = parentPath.pop();
var parentEl = exp.findElement(files, parentPath);
parentEl[key] = undefined;
delete parentEl[key];
});
var trashRoot = [];
trashPaths.forEach(function (path) {
var parentPath = path.slice();
var key = parentPath.pop();
var parentEl = exp.findElement(files, parentPath);
// Trash root: we have array here, we can't just splice with the path otherwise we might break the path
// of another element in the loop
console.log(path);
if (path.length === 4) {
trashRoot.push({
name: path[1],
el: parentEl
});
return;
}
// Trash but not root: it's just a tree so remove the key
parentEl[key] = undefined;
delete parentEl[key];
});
deleteMultipleTrashRoot(trashRoot);
checkDeletedFiles();
};
// Find an element in a object following a path, resursively // Find an element in a object following a path, resursively
// NOTE: it is always used with an absolute path and root === files in our code // NOTE: it is always used with an absolute path and root === files in our code
var findElement = exp.findElement = function (root, pathInput) { var findElement = exp.findElement = function (root, pathInput) {
@ -352,7 +429,6 @@ define([
}; };
// Move to trash // Move to trash
// TODO: rename the function
var removeElement = exp.removeElement = function (path, cb, keepOld) { var removeElement = exp.removeElement = function (path, cb, keepOld) {
if (!path || path.length < 2 || path[0] === TRASH) { if (!path || path.length < 2 || path[0] === TRASH) {
debug("Calling removeElement from a wrong path: ", path); debug("Calling removeElement from a wrong path: ", path);
@ -386,7 +462,7 @@ define([
if (isPathInHrefArray(newParentPath)) { if (isPathInHrefArray(newParentPath)) {
if (isFolder(element)) { if (isFolder(element)) {
log(Messages.fo_moveUnsortedError); //TODO or template log(Messages.fo_moveUnsortedError);
return; return;
} else { } else {
if (elementPath[0] === newParentPath[0]) { return; } if (elementPath[0] === newParentPath[0]) { return; }
@ -572,6 +648,28 @@ define([
if(cb) { cb(); } if(cb) { cb(); }
}; };
var deleteFileData = exp.deleteFileData = function (href, cb) {
if (workgroup) { return; }
var toRemove = [];
files[FILES_DATA].forEach(function (arr) {
var f = arr.href;
if (f === href) {
toRemove.push(arr);
}
});
toRemove.forEach(function (f) {
var idx = files[FILES_DATA].indexOf(f);
if (idx !== -1) {
debug("Removing", f, "from filesData");
files[FILES_DATA].splice(idx, 1);
// Remove the "padAttributes" stored in the realtime object for that pad
removePadAttribute(f.href);
}
});
if(cb) { cb(); }
};
var renameElement = exp.renameElement = function (path, newName, cb) { var renameElement = exp.renameElement = function (path, newName, cb) {
if (path.length <= 1) { if (path.length <= 1) {
@ -619,14 +717,15 @@ define([
var trashFiles = getTrashFiles(); var trashFiles = getTrashFiles();
var templateFiles = getTemplateFiles(); var templateFiles = getTemplateFiles();
var newPath, parentEl; var newPath, parentEl;
if (path && isPathInHrefArray(path)) { if (path) {
newPath = decodeURIComponent(path).split(','); newPath = decodeURIComponent(path).split(',');
}
if (path && isPathInHrefArray(newPath)) {
parentEl = findElement(files, newPath); parentEl = findElement(files, newPath);
parentEl.push(href); parentEl.push(href);
return; return;
} }
if (path && name) { if (path && isPathInRoot(newPath) && name) {
newPath = decodeURIComponent(path).split(',');
parentEl = findElement(files, newPath); parentEl = findElement(files, newPath);
if (parentEl) { if (parentEl) {
var newName = getAvailableName(parentEl, name); var newName = getAvailableName(parentEl, name);
@ -634,7 +733,7 @@ define([
return; return;
} }
} }
if (unsortedFiles.indexOf(href) === -1 && rootFiles.indexOf(href) === -1&& templateFiles.indexOf(href) === -1 && trashFiles.indexOf(href) === -1) { if (unsortedFiles.indexOf(href) === -1 && rootFiles.indexOf(href) === -1 && templateFiles.indexOf(href) === -1 && trashFiles.indexOf(href) === -1) {
files[UNSORTED].push(href); files[UNSORTED].push(href);
} }
}; };
@ -736,11 +835,8 @@ define([
toClean.push(idx); toClean.push(idx);
} }
}); });
toClean.forEach(function (el) { toClean.forEach(function (idx) {
var idx = us.indexOf(el); us.splice(idx, 1);
if (idx !== -1) {
us.splice(idx, 1);
}
}); });
}; };
var fixTemplate = function () { var fixTemplate = function () {
@ -755,31 +851,31 @@ define([
toClean.push(idx); toClean.push(idx);
} }
}); });
toClean.forEach(function (el) { toClean.forEach(function (idx) {
var idx = us.indexOf(el); us.splice(idx, 1);
if (idx !== -1) {
us.splice(idx, 1);
}
}); });
}; };
var fixFilesData = function (fd) { var fixFilesData = function () {
if (!$.isArray(files[FILES_DATA])) { debug("FILES_DATA was not an array"); files[FILES_DATA] = []; } if (!$.isArray(files[FILES_DATA])) { debug("FILES_DATA was not an array"); files[FILES_DATA] = []; }
var fd = files[FILES_DATA]; var fd = files[FILES_DATA];
var rootFiles = getRootFiles(); var rootFiles = getRootFiles();
var unsortedFiles = getUnsortedFiles(); var unsortedFiles = getUnsortedFiles();
var templateFiles = getTemplateFiles();
var trashFiles = getTrashFiles(); var trashFiles = getTrashFiles();
var toClean = []; var toClean = [];
fd.forEach(function (el, idx) { fd.forEach(function (el, idx) {
if (typeof(el) !== "object") { if (typeof(el) !== "object") {
debug("An element in filesData was not an object.", el); debug("An element in filesData was not an object.", el);
toClean.push(el); toClean.push(el);
} else { return;
if (rootFiles.indexOf(el.href) === -1 }
&& unsortedFiles.indexOf(el.href) === -1 if (rootFiles.indexOf(el.href) === -1
&& trashFiles.indexOf(el.href) === -1) { && unsortedFiles.indexOf(el.href) === -1
debug("An element in filesData was not in ROOT, UNSORTED or TRASH.", el); && templateFiles.indexOf(el.href) === -1
files[UNSORTED].push(el.href); && trashFiles.indexOf(el.href) === -1) {
} debug("An element in filesData was not in ROOT, UNSORTED or TRASH.", el);
files[UNSORTED].push(el.href);
return;
} }
}); });
toClean.forEach(function (el) { toClean.forEach(function (el) {

@ -33,17 +33,14 @@ define([
var USERLIST_CLS = Bar.constants.userlist = "cryptpad-dropdown-users"; var USERLIST_CLS = Bar.constants.userlist = "cryptpad-dropdown-users";
var EDITSHARE_CLS = Bar.constants.editShare = "cryptpad-dropdown-editShare"; var EDITSHARE_CLS = Bar.constants.editShare = "cryptpad-dropdown-editShare";
var VIEWSHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-viewShare"; var VIEWSHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-viewShare";
var SHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-share";
var DROPDOWN_CONTAINER_CLS = Bar.constants.dropdownContainer = "cryptpad-dropdown-container"; var DROPDOWN_CONTAINER_CLS = Bar.constants.dropdownContainer = "cryptpad-dropdown-container";
var DROPDOWN_CLS = Bar.constants.dropdown = "cryptpad-dropdown"; var DROPDOWN_CLS = Bar.constants.dropdown = "cryptpad-dropdown";
var TITLE_CLS = Bar.constants.title = "cryptpad-title"; var TITLE_CLS = Bar.constants.title = "cryptpad-title";
var USER_CLS = Bar.constants.userAdmin = "cryptpad-user"; var USER_CLS = Bar.constants.userAdmin = "cryptpad-user";
var USERBUTTON_CLS = Bar.constants.changeUsername = "cryptpad-change-username";
/** Key in the localStore which indicates realtime activity should be disallowed. */
// TODO remove? will never be used in cryptpad
var LOCALSTORAGE_DISALLOW = Bar.constants.localstorageDisallow = 'cryptpad-disallow';
var SPINNER_DISAPPEAR_TIME = 3000; var SPINNER_DISAPPEAR_TIME = 3000;
var SPINNER = [ '-', '\\', '|', '/' ];
var uid = function () { var uid = function () {
return 'cryptpad-uid-' + String(Math.random()).substring(2); return 'cryptpad-uid-' + String(Math.random()).substring(2);
@ -79,94 +76,65 @@ define([
return $toolbar; return $toolbar;
}; };
var createSpinner = function ($container) { var createSpinner = function ($container, config) {
var $spinner = $('<div>', { if (config.displayed.indexOf('spinner') !== -1) {
id: uid(), var $spinner = $('<span>', {
'class': SPINNER_CLS, id: uid(),
}); 'class': SPINNER_CLS + ' fa fa fa-spinner fa-pulse',
$container.prepend($spinner); }).hide();
return $spinner[0]; $container.prepend($spinner);
return $spinner[0];
}
}; };
var kickSpinner = function (spinnerElement, reversed) { var kickSpinner = function (spinnerElement) {
var txt = spinnerElement.textContent || '-'; if (!spinnerElement) { return; }
var inc = (reversed) ? -1 : 1; $(spinnerElement).show();
spinnerElement.textContent = SPINNER[(SPINNER.indexOf(txt) + inc) % SPINNER.length];
if (spinnerElement.timeout) { clearTimeout(spinnerElement.timeout); } if (spinnerElement.timeout) { clearTimeout(spinnerElement.timeout); }
spinnerElement.timeout = setTimeout(function () { spinnerElement.timeout = setTimeout(function () {
spinnerElement.textContent = ''; $(spinnerElement).hide();
}, SPINNER_DISAPPEAR_TIME); }, SPINNER_DISAPPEAR_TIME);
}; };
var createUserButtons = function ($userlistElement, readOnly) { var createUserButtons = function ($userlistElement, config, readOnly, Cryptpad) {
var $listElement = $('<span>', {
id: 'userButtons',
'class': USERBUTTONS_CONTAINER_CLS
}).appendTo($userlistElement);
var createHandler = function ($elmt) {
return function () {
if ($elmt.is(':visible')) {
$elmt.hide();
return;
}
$userlistElement.find('.' + DROPDOWN_CLS).hide();
$elmt.show();
};
};
var fa_caretdown = '<span class="fa fa-caret-down" style="font-family:FontAwesome;"></span>';
// User list button // User list button
var $editIcon = $('<button>', { if (config.displayed.indexOf('userlist') !== -1) {
'class': 'userlist dropbtn edit', if (!config.userData) {
}); throw new Error("You must provide a `userData` object to display the userlist");
var $editIconSmall = $editIcon.clone().addClass('small'); }
var $dropdownEditUsers = $('<p>', {'class': USERLIST_CLS}); var dropdownConfig = {
var $dropdownEditContainer = $('<div>', {'class': DROPDOWN_CONTAINER_CLS}); options: [{
var $dropdownEdit = $('<div>', { tag: 'p',
id: "cryptpad-dropdown-edit", attributes: {'class': USERLIST_CLS},
'class': DROPDOWN_CLS }] // Entries displayed in the menu
}).append($dropdownEditUsers); //.append($dropdownEditShare); };
$editIcon.click(createHandler($dropdownEdit)); var $block = Cryptpad.createDropdown(dropdownConfig);
$editIconSmall.click(createHandler($dropdownEdit)); $block.attr('id', 'userButtons');
$dropdownEditContainer.append($editIcon).append($editIconSmall).append($dropdownEdit); $userlistElement.append($block);
$listElement.append($dropdownEditContainer); }
// Share button // Share button
var fa_share = '<span class="fa fa-share-alt" style="font-family:FontAwesome;"></span>'; if (config.displayed.indexOf('share') !== -1) {
var fa_editusers = '<span class="fa fa-users" style="font-family:FontAwesome;"></span>'; var $shareIcon = $('<span>', {'class': 'fa fa-share-alt'});
var fa_viewusers = '<span class="fa fa-eye" style="font-family:FontAwesome;"></span>'; var $span = $('<span>', {'class': 'large'}).append($shareIcon.clone()).append(' ' +Messages.shareButton);
var $shareIcon = $('<button>', { var $spanSmall = $('<span>', {'class': 'small'}).append($shareIcon.clone());
'class': 'userlist dropbtn share', var dropdownConfigShare = {
}).html(fa_share + ' ' + Messages.shareButton + ' ' + fa_caretdown); text: $('<div>').append($span).append($spanSmall).html(),
var $shareIconSmall = $shareIcon.clone().addClass('small').html(fa_share + ' ' + fa_caretdown); options: []
var $shareTitle = $('<h2>').html(fa_editusers + ' ' + Messages.shareEdit); };
var $shareTitleView = $('<h2>').html(fa_viewusers + ' ' + Messages.shareView); var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare);
var $dropdownShareP = $('<p>', {'class': EDITSHARE_CLS}).append($shareTitle); $shareBlock.find('.dropdown-bar-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS);
var $dropdownSharePView = $('<p>', {'class': VIEWSHARE_CLS}).append($shareTitleView); $userlistElement.append($shareBlock);
var $dropdownShareContainer = $('<div>', {'class': DROPDOWN_CONTAINER_CLS});
var $dropdownShare = $('<div>', {
id: "cryptpad-dropdown-share",
'class': DROPDOWN_CLS
});
if (readOnly !== 1) {
// Hide the "Share edit URL" section when in read-only mode
$dropdownShare.append($dropdownShareP);
} }
$dropdownShare.append($dropdownSharePView);
$shareIcon.click(createHandler($dropdownShare));
$shareIconSmall.click(createHandler($dropdownShare));
$dropdownShareContainer.append($shareIcon).append($shareIconSmall).append($dropdownShare);
$listElement.append($dropdownShareContainer);
}; };
var createUserList = function ($container, readOnly) { var createUserList = function ($container, config, readOnly, Cryptpad) {
if (config.displayed.indexOf('userlist') === -1 && config.displayed.indexOf('share') === -1) { return; }
var $userlist = $('<div>', { var $userlist = $('<div>', {
'class': USER_LIST_CLS, 'class': USER_LIST_CLS,
id: uid(), id: uid(),
}); });
createUserButtons($userlist, readOnly); createUserButtons($userlist, config, readOnly, Cryptpad);
$container.append($userlist); $container.append($userlist);
return $userlist[0]; return $userlist[0];
}; };
@ -207,88 +175,89 @@ define([
$stateElement.text(''); $stateElement.text('');
}; };
var updateUserList = function (myUserName, userlistElement, userList, userData, readOnly, $userAdminElement) { var updateUserList = function (config, myUserName, userlistElement, userList, userData, readOnly, $userAdminElement) {
// Make sure the elements are displayed // Make sure the elements are displayed
var $userButtons = $(userlistElement).find("#userButtons"); var $userButtons = $(userlistElement).find("#userButtons");
$userButtons.attr('display', 'inline'); $userButtons.attr('display', 'inline');
var numberOfUsers = userList.length; if (config.displayed.indexOf('userlist') !== -1) {
var numberOfUsers = userList.length;
// If we are using old pads (readonly unavailable), only editing users are in userList. // If we are using old pads (readonly unavailable), only editing users are in userList.
// With new pads, we also have readonly users in userList, so we have to intersect with // With new pads, we also have readonly users in userList, so we have to intersect with
// the userData to have only the editing users. We can't use userData directly since it // the userData to have only the editing users. We can't use userData directly since it
// may contain data about users that have already left the channel. // may contain data about users that have already left the channel.
userList = readOnly === -1 ? userList : arrayIntersect(userList, Object.keys(userData)); userList = readOnly === -1 ? userList : arrayIntersect(userList, Object.keys(userData));
var numberOfEditUsers = userList.length; var numberOfEditUsers = userList.length;
var numberOfViewUsers = numberOfUsers - numberOfEditUsers; var numberOfViewUsers = numberOfUsers - numberOfEditUsers;
// Names of editing users // Names of editing users
var editUsersNames = getOtherUsers(myUserName, userList, userData); var editUsersNames = getOtherUsers(myUserName, userList, userData);
// Number of anonymous editing users // Number of anonymous editing users
var anonymous = numberOfEditUsers - editUsersNames.length; var anonymous = numberOfEditUsers - editUsersNames.length;
// Update the userlist // Update the userlist
var editUsersList = ''; var editUsersList = '';
if (readOnly !== 1) { if (readOnly !== 1) {
editUsersNames.unshift('<span class="yourself">' + Messages.yourself + '</span>'); editUsersNames.unshift('<span class="yourself">' + Messages.yourself + '</span>');
anonymous--; anonymous--;
} }
if (anonymous > 0) { if (anonymous > 0) {
var text = anonymous === 1 ? Messages.anonymousUser : Messages.anonymousUsers; var text = anonymous === 1 ? Messages.anonymousUser : Messages.anonymousUsers;
editUsersNames.push('<span class="anonymous">' + anonymous + ' ' + text + '</span>'); editUsersNames.push('<span class="anonymous">' + anonymous + ' ' + text + '</span>');
} }
if (numberOfViewUsers > 0) { if (numberOfViewUsers > 0) {
var viewText = '<span class="viewer">'; var viewText = '<span class="viewer">';
if (numberOfEditUsers > 0) { if (numberOfEditUsers > 0) {
editUsersNames.push(''); editUsersNames.push('');
viewText += Messages.and + ' '; viewText += Messages.and + ' ';
}
var viewerText = numberOfViewUsers > 1 ? Messages.viewers : Messages.viewer;
viewText += numberOfViewUsers + ' ' + viewerText + '</span>';
editUsersNames.push(viewText);
}
if (editUsersNames.length > 0) {
editUsersList += editUsersNames.join('<br>');
} }
var viewerText = numberOfViewUsers > 1 ? Messages.viewers : Messages.viewer;
viewText += numberOfViewUsers + ' ' + viewerText + '</span>';
editUsersNames.push(viewText);
}
if (editUsersNames.length > 0) {
editUsersList += editUsersNames.join('<br>');
}
var $usersTitle = $('<h2>').text(Messages.users); var $usersTitle = $('<h2>').text(Messages.users);
var $editUsers = $userButtons.find('.' + USERLIST_CLS); var $editUsers = $userButtons.find('.' + USERLIST_CLS);
$editUsers.html('').append($usersTitle).append(editUsersList); $editUsers.html('').append($usersTitle).append(editUsersList);
// Update the buttons // Update the buttons
var fa_caretdown = '<span class="fa fa-caret-down" style="font-family:FontAwesome;"></span>'; var fa_editusers = '<span class="fa fa-users"></span>';
var fa_editusers = '<span class="fa fa-users" style="font-family:FontAwesome;"></span>'; var fa_viewusers = '<span class="fa fa-eye"></span>';
var fa_viewusers = '<span class="fa fa-eye" style="font-family:FontAwesome;"></span>'; var viewersText = numberOfViewUsers > 1 ? Messages.viewers : Messages.viewer;
var viewersText = numberOfViewUsers > 1 ? Messages.viewers : Messages.viewer; var editorsText = numberOfEditUsers > 1 ? Messages.editors : Messages.editor;
var editorsText = numberOfEditUsers > 1 ? Messages.editors : Messages.editor; var $span = $('<span>', {'class': 'large'}).html(fa_editusers + ' ' + numberOfEditUsers + ' ' + editorsText + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers + ' ' + viewersText);
$userButtons.find('.userlist.edit').html(fa_editusers + ' ' + numberOfEditUsers + ' ' + editorsText + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers + ' ' + viewersText + ' ' + fa_caretdown); var $spansmall = $('<span>', {'class': 'small'}).html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers);
$userButtons.find('.userlist.edit.small').html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers + ' ' + fa_caretdown); $userButtons.find('.buttonTitle').html('').append($span).append($spansmall);
// Change username button
var $userElement = $userAdminElement.find('.' + USERNAME_CLS);
$userElement.show();
if (readOnly === 1) {
$userElement.html('<span class="' + READONLY_CLS + '">' + Messages.readonly + '</span>');
} }
else {
var name = userData[myUserName] && userData[myUserName].name; if (config.displayed.indexOf('useradmin') !== -1) {
var icon = '<span class="fa fa-user" style="font-family:FontAwesome;"></span>'; // Change username in useradmin dropdown
if (!name) { var $userElement = $userAdminElement.find('.' + USERNAME_CLS);
name = Messages.anonymous; $userElement.show();
if (readOnly === 1) {
$userElement.addClass(READONLY_CLS).text(Messages.readonly);
}
else {
var name = userData[myUserName] && userData[myUserName].name;
if (!name) {
name = Messages.anonymous;
}
$userElement.removeClass(READONLY_CLS).text(name);
} }
$userElement.find("button").show();
$userElement.find("button").html(icon + ' ' + name);
} }
}; };
var createLagElement = function ($container) { var createLagElement = function () {
var $lag = $('<span>', { var $lag = $('<span>', {
'class': LAG_ELEM_CLS, 'class': LAG_ELEM_CLS,
id: uid(), id: uid(),
}); });
$container.prepend($lag);
return $lag[0]; return $lag[0];
}; };
@ -356,37 +325,137 @@ define([
$linkContainer.append($aTagSmall).append($aTagBig); $linkContainer.append($aTagSmall).append($aTagBig);
}; };
var createUserAdmin = function ($topContainer) { var createUserAdmin = function ($topContainer, config, readOnly, lagElement, Cryptpad) {
var $lag = $(lagElement);
var $userContainer = $('<span>', { var $userContainer = $('<span>', {
'class': USER_CLS 'class': USER_CLS
}).appendTo($topContainer); }).appendTo($topContainer);
var $state = $('<span>', { if (config.displayed.indexOf('state') !== -1) {
'class': STATE_CLS var $state = $('<span>', {
}).text(Messages.synchronizing).appendTo($userContainer); 'class': STATE_CLS
}).text(Messages.synchronizing).appendTo($userContainer);
}
var $span = $('<span>' , { if (config.displayed.indexOf('lag') !== -1) {
'class': 'cryptpad-language' $userContainer.append($lag);
}); }
var $select = $('<select>', {
'id': 'language-selector' if (config.displayed.indexOf('language') !== -1) {
}).appendTo($userContainer); // Dropdown language selector
Cryptpad.createLanguageSelector($userContainer);
var languages = Messages._languages;
for (var l in languages) {
$('<option>', {
value: l
}).text(languages[l]).appendTo($select);
} }
Messages._initSelector($select); if (config.displayed.indexOf('newpad') !== -1) {
var pads_options = [];
['pad', 'code', 'slide', 'poll'].forEach(function (p) {
pads_options.push({
tag: 'a',
attributes: {
'target': '_blank', // TODO: open in the same window?
'href': '/' + p,
},
content: Messages.type[p] // Pretty name of the language value
});
});
var $newButton = $('<div>').append($('<span>', {'class': 'fa fa-plus'})).append(Messages.newPadButton);
var dropdownConfig = {
text: $newButton.html(), // Button initial text
options: pads_options, // Entries displayed in the menu
left: true, // Open to the left of the button
};
var $newPadBlock = Cryptpad.createDropdown(dropdownConfig);
$newPadBlock.find('button').attr('title', Messages.newPadButtonTitle);
$newPadBlock.appendTo($userContainer);
}
$select.on('mousedown', function (e) { // User dropdown
e.stopPropagation(); if (config.displayed.indexOf('useradmin') !== -1) {
return true; if (!config.userName || !config.userName.setName || !config.userName.lastName) {
}); throw new Error("You must provide a `userName` object containing `setName` (function) " +
"and `lastName` (object) if you want to display the user admin menu.");
}
var $displayedName = $('<span>', {'class': USERNAME_CLS});
var accountName = Cryptpad.getStore().getLoginName ? Cryptpad.getStore().getLoginName() : null;
var account = typeof accountName === "string";
var $userAdminContent = $('<p>');
if (account) {
var $userAccount = $('<span>', {'class': 'userAccount'}).append(Messages.user_accountName + ': ' + accountName);
$userAdminContent.append($userAccount);
$userAdminContent.append($('<br>'));
}
var $userName = $('<span>', {'class': 'userDisplayName'});
if (readOnly !== 1) {
// Hide "Display name:" in read only mode
$userName.append(Messages.user_displayName + ': ');
}
$userName.append($displayedName.clone());
$userAdminContent.append($userName);
var options = [{
tag: 'p',
attributes: {'class': 'accountData'},
content: $userAdminContent.html()
}];
// Add the change display name button if not in read only mode
if (readOnly !== 1) {
options.push({
tag: 'a',
attributes: {'class': USERBUTTON_CLS},
content: Messages.user_rename
});
}
// Add login or logout button depending on the current status
if (account) {
options.push({
tag: 'a',
attributes: {'class': 'logout'},
content: Messages.logoutButton
});
} else {
options.push({
tag: 'a',
attributes: {'class': 'login'},
content: Messages.login_login
});
options.push({
tag: 'a',
attributes: {'class': 'register'},
content: Messages.login_register
});
}
var $icon = $('<span>', {'class': 'fa fa-user'});
var $button = $('<div>').append($icon).append($displayedName.clone());
if (account) {
$button.append($('<span>', {'class': 'account-name'}).text('(' + accountName + ')'));
}
var dropdownConfigUser = {
text: $button.html(), // Button initial text
options: options, // Entries displayed in the menu
left: true, // Open to the left of the button
};
var $userAdmin = Cryptpad.createDropdown(dropdownConfigUser);
$userContainer.append($userAdmin);
var $usernameElement = $('<span>', {'class': USERNAME_CLS}).appendTo($userContainer); $userAdmin.find('a.logout').click(function (e) {
Cryptpad.logout();
window.location.reload();
});
$userAdmin.find('a.login').click(function (e) {
window.open('/user');
});
$userAdmin.find('a.register').click(function (e) {
window.open('/user#register');
});
if (config.userName && config.userName.setName && config.userName.lastName) {
$userAdmin.find('a.' + USERBUTTON_CLS).click(function (e) {
Cryptpad.prompt(Messages.changeNamePrompt, config.userName.lastName.lastName || '', function (newName) {
config.userName.setName(newName);
});
});
}
}
return $userContainer; return $userContainer;
}; };
@ -473,63 +542,66 @@ define([
config = config || {}; config = config || {};
var readOnly = (typeof config.readOnly !== "undefined") ? (config.readOnly ? 1 : 0) : -1; var readOnly = (typeof config.readOnly !== "undefined") ? (config.readOnly ? 1 : 0) : -1;
var Cryptpad = config.common; var Cryptpad = config.common;
config.displayed = config.displayed || [];
var toolbar = createRealtimeToolbar($container); var toolbar = createRealtimeToolbar($container);
var userListElement = config.userData ? createUserList(toolbar.find('.' + LEFTSIDE_CLS), readOnly) : undefined; var userListElement = createUserList(toolbar.find('.' + LEFTSIDE_CLS), config, readOnly, Cryptpad);
var $titleElement = createTitle(toolbar.find('.' + TOP_CLS), readOnly, config.title, Cryptpad); var $titleElement = createTitle(toolbar.find('.' + TOP_CLS), readOnly, config.title, Cryptpad);
var $linkElement = createLinkToMain(toolbar.find('.' + TOP_CLS)); var $linkElement = createLinkToMain(toolbar.find('.' + TOP_CLS));
var $userAdminElement = createUserAdmin(toolbar.find('.' + TOP_CLS)); var lagElement = createLagElement();
var lagElement = createLagElement($userAdminElement); var $userAdminElement = createUserAdmin(toolbar.find('.' + TOP_CLS), config, readOnly, lagElement, Cryptpad);
var spinner = createSpinner($userAdminElement); var spinner = createSpinner($userAdminElement, config);
var userData = config.userData; var userData = config.userData;
// readOnly = 1 (readOnly enabled), 0 (disabled), -1 (old pad without readOnly mode) // readOnly = 1 (readOnly enabled), 0 (disabled), -1 (old pad without readOnly mode)
var saveElement; var saveElement;
var loadElement; var loadElement;
var $stateElement = $userAdminElement.find('.' + STATE_CLS); var $stateElement = toolbar.find('.' + STATE_CLS);
var connected = false; var connected = false;
if (config.ifrw) { if (config.ifrw) {
var removeDropdowns = function (e) { var removeDropdowns = function (e) {
if ($(e.target).parents('.cryptpad-dropdown-container').length) {
return;
}
$container.find('.cryptpad-dropdown').hide(); $container.find('.cryptpad-dropdown').hide();
}; };
var cancelEditTitle = function (e) { var cancelEditTitle = function (e) {
// Now we want to apply the title even if we click somewhere else
if ($(e.target).parents('.' + TITLE_CLS).length || !$titleElement) { if ($(e.target).parents('.' + TITLE_CLS).length || !$titleElement) {
return; return;
} }
var ev = $.Event("keyup");
ev.which = 13;
$titleElement.find('input').trigger(ev);
/*
$titleElement.find('input').hide(); $titleElement.find('input').hide();
$titleElement.find('span.title').show(); $titleElement.find('span.title').show();
$titleElement.find('span.pencilIcon').css('display', ''); $titleElement.find('span.pencilIcon').css('display', '');
*/
}; };
$(config.ifrw).on('click', removeDropdowns); $(config.ifrw).on('click', removeDropdowns);
$(config.ifrw).on('click', cancelEditTitle); $(config.ifrw).on('click', cancelEditTitle);
try { try {
if (config.ifrw.$('iframe').length) { if (config.ifrw.$('iframe').length) {
var innerIfrw = config.ifrw.$('iframe').each(function (i, el) { var innerIfrw = config.ifrw.$('iframe').each(function (i, el) {
$(el.contentWindow).on('click', removeDropdowns); $(el.contentWindow).on('click', removeDropdowns);
$(el.contentWindow).on('click', cancelEditTitle); $(el.contentWindow).on('click', cancelEditTitle);
}); });
}
} catch (e) {
// empty try catch in case this iframe is problematic
} }
} catch (e) {
// empty try catch in case this iframe is problematic
}
} }
// Update user list // Update user list
if (config.userData) { if (userData) {
userList.change.push(function (newUserData) { userList.change.push(function (newUserData) {
var users = userList.users; var users = userList.users;
if (users.indexOf(myUserName) !== -1) { connected = true; } if (users.indexOf(myUserName) !== -1) { connected = true; }
if (!connected) { return; } if (!connected) { return; }
/*if (newUserData) { // Someone has changed his name/color
userData = newUserData;
}*/
checkSynchronizing(users, myUserName, $stateElement); checkSynchronizing(users, myUserName, $stateElement);
updateUserList(myUserName, userListElement, users, userData, readOnly, $userAdminElement); updateUserList(config, myUserName, userListElement, users, userData, readOnly, $userAdminElement);
}); });
} else { } else {
userList.change.push(function () { userList.change.push(function () {
@ -596,7 +668,7 @@ define([
} }
var ks = function () { var ks = function () {
if (connected) { kickSpinner(spinner, false); } if (connected) { kickSpinner(spinner); }
}; };
realtime.onPatch(ks); realtime.onPatch(ks);

@ -0,0 +1,390 @@
/* PAGE */
html,
body {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
position: relative;
font-size: 20px;
overflow: auto;
}
body {
display: flex;
flex-flow: column;
}
.unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.app-container {
flex: 1;
overflow: auto;
width: 100%;
display: flex;
flex-flow: row;
}
.fa {
/*min-width: 17px;*/
margin-right: 3px;
font-family: FontAwesome;
}
ul {
list-style: none;
padding-left: 0px;
}
li {
padding: 0px 5px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.folder,
.file {
margin-right: 5px;
}
.contextMenu {
display: none;
position: absolute;
}
.contextMenu li {
padding: 0;
font-size: 16px;
}
.droppable {
background-color: #FE9A2E;
color: #222;
}
.selected {
border: 1px dotted #bbb;
background: #666;
color: #eee;
margin: -1px;
}
span.fa-folder,
span.fa-folder-open {
color: #FEDE8B;
text-shadow: -1px 0 #000000, 0 1px #000000, 1px 0 #000000, 0 -1px #000000;
}
/* TREE */
#tree {
border-right: 1px solid #ccc;
box-sizing: border-box;
background: #ffffff;
overflow: auto;
resize: horizontal;
width: 250px;
white-space: nowrap;
max-width: 500px;
min-width: 200px;
padding: 10px 0px;
color: #000000;
}
#tree li {
cursor: auto;
}
#tree li:hover > span.element {
text-decoration: underline;
}
#tree li.collapsed ul {
display: none;
}
#tree li input {
width: calc(70%);
}
#tree span.element {
cursor: pointer;
}
#tree .active {
text-decoration: underline;
}
#tree .category2 {
margin-top: 2em;
}
#tree .category2 .root > .fa {
min-width: 30px;
cursor: pointer;
}
#tree .fa.expcol {
margin-left: -10px;
font-size: 14px;
position: absolute;
left: -20px;
top: 9px;
width: auto;
height: 11px;
padding: 0;
margin: 0;
background: white;
z-index: 10;
cursor: default;
}
#tree .fa.expcol:before {
position: relative;
top: -1px;
}
#tree ul {
margin: 0px 0px 0px 10px;
list-style: none;
padding-left: 10px;
}
#tree ul li {
position: relative;
}
#tree ul li:before {
position: absolute;
left: -15px;
top: -0.25em;
content: '';
display: block;
border-left: 1px solid #888888;
height: 1em;
border-bottom: 1px solid #888888;
width: 17.5px;
}
#tree ul li:after {
position: absolute;
left: -15px;
bottom: -7px;
content: '';
display: block;
border-left: 1px solid #888888;
height: 100%;
}
#tree ul li.root {
margin: 0px 0px 0px -10px;
}
#tree ul li.root:before {
display: none;
}
#tree ul li.root:after {
display: none;
}
#tree ul li:last-child:after {
display: none;
}
/* CONTENT */
#content {
box-sizing: border-box;
background: #ffffff;
color: #000000;
overflow: auto;
flex: 1;
display: flex;
flex-flow: column;
}
#content h1 {
padding-left: 10px;
margin-top: 10px;
}
#content .info-box {
height: 40px;
line-height: 40px;
padding-left: 10px;
margin: 10px auto;
background: #ddddff;
border: 1px solid #bbbbbb;
border-radius: 5px;
}
#content .info-box span {
cursor: pointer;
margin-left: 10px;
float: right;
}
#content li:not(.header) *:not(input) {
pointer-events: none;
}
#content li:not(.header):hover .name {
text-decoration: underline;
}
#content div.grid {
padding: 20px;
}
#content div.grid li {
display: inline-block;
margin: 10px 10px;
width: 140px;
text-align: center;
vertical-align: top;
}
#content div.grid li .name {
width: 100%;
}
#content div.grid li input {
width: 100%;
}
#content div.grid li .fa {
display: block;
margin: auto;
font-size: 40px;
text-align: center;
}
#content div.grid li .fa.listonly {
display: none;
}
#content div.grid .listElement {
display: none;
}
#content .list ul {
display: table;
width: 100%;
padding: 0px 10px;
}
#content .list li {
display: table-row;
}
#content .list li > span {
padding: 0 5px;
display: table-cell;
}
#content .list li.header {
cursor: default;
color: #555555;
}
#content .list li.header span:not(.fa) {
text-align: left;
}
#content .list li.header span.sortasc,
#content .list li.header span.sortdesc {
float: right;
}
#content .list li.header > span {
padding: 15px 5px;
}
#content .list li.header > span.active {
font-weight: bold;
}
#content .list li.header > span.clickable {
cursor: pointer;
}
#content .list li.header > span.clickable:hover {
background: #e8e8e8;
}
#content .list .element span {
overflow: hidden;
white-space: nowrap;
box-sizing: border-box;
}
#content .list .element span.icon {
width: 30px;
}
#content .list .element span.type,
#content .list .element span.atime,
#content .list .element span.ctime {
width: 175px;
}
#content .list .element span.title {
width: 250px;
}
@media screen and (max-width: 1200px) {
#content .list .element span.title {
display: none;
}
}
#content .list .element span.folders,
#content .list .element span.files {
width: 150px;
}
.parentFolder {
cursor: pointer;
margin-left: 10px;
}
.parentFolder:hover {
text-decoration: underline;
}
#folderContent {
padding-right: 10px;
flex: 1;
}
/* Toolbar */
#driveToolbar {
background: #dddddd;
color: #555555;
height: 40px;
display: flex;
flex-flow: row;
border-top: 1px solid #cccccc;
border-bottom: ;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
z-index: 100;
box-sizing: content-box;
/* The container <div> - needed to position the dropdown content */
}
#driveToolbar .newPadContainer {
display: inline-block;
height: 100%;
}
#driveToolbar button.element {
border-radius: 2px;
height: 30px;
background: #888888;
color: #eeeeee;
font-size: 16px;
border: none;
font-weight: bold;
}
#driveToolbar button.element:hover {
box-shadow: 0px 0px 2px #000;
}
#driveToolbar button.new {
padding: 0 20px;
}
#driveToolbar .dropdown-bar {
margin: 5px 5px;
line-height: 1em;
position: relative;
display: inline-block;
}
#driveToolbar .dropdown-bar.right {
float: right;
/* Right-side buttons */
}
#driveToolbar .dropdown-bar.right button {
display: inline-block;
}
#driveToolbar .dropdown-bar.right button.active {
display: none;
}
#driveToolbar .dropdown-bar.right button .fa {
margin-right: 0px;
}
#driveToolbar .dropdown-bar-content {
margin-top: -3px;
margin-right: 2px;
}
#driveToolbar .leftside {
width: 250px;
margin: 0;
padding: 0;
}
#driveToolbar .rightside {
margin: 0;
padding: 0;
flex: 1;
}
#driveToolbar .path {
display: inline-block;
height: 100%;
line-height: 40px;
cursor: default;
}
#driveToolbar .path .element {
padding: 5px;
border: 1px solid #dddddd;
border-radius: 2px;
box-sizing: border-box;
}
#driveToolbar .path .element.clickable {
cursor: pointer;
}
#driveToolbar .path .element.clickable:hover {
background: #ffffff;
border: 1px solid #888888;
}

@ -0,0 +1,454 @@
@tree-bg: #fff;
@tree-fg: #000;
@tree-lines-col: #888;
@content-bg: @tree-bg;
@content-fg: @tree-fg;
@info-box-bg: #ddddff;
@info-box-border: #bbb;
@table-header-fg: #555;
@table-header-bg: #e8e8e8;
@toolbar-bg: #ddd;
@toolbar-fg: #555;
@toolbar-border-col: #ccc;
@toolbar-button-bg: #888;
@toolbar-button-fg: #eee;
@toolbar-path-bg: #fff;
@toolbar-path-border: #888;
/* PAGE */
html, body {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
position: relative;
font-size: 20px;
overflow: auto;
}
body {
display: flex;
flex-flow: column;
}
.unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.app-container {
flex: 1;
overflow: auto;
width: 100%;
display: flex;
flex-flow: row;
}
.fa {
/*min-width: 17px;*/
margin-right: 3px;
font-family: FontAwesome;
}
ul {
list-style: none;
padding-left: 0px; // Remove the default padding
}
li {
padding: 0px 5px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.folder, .file {
margin-right: 5px;
}
.contextMenu {
display: none;
position: absolute;
li {
padding: 0;
font-size: 16px;
}
}
.droppable {
background-color: #FE9A2E;
color: #222;
}
.selected {
border: 1px dotted #bbb;
background: #666;
color: #eee;
margin: -1px;
}
span {
&.fa-folder, &.fa-folder-open {
color: #FEDE8B;
text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;
}
}
/* TREE */
#tree {
border-right: 1px solid #ccc;
box-sizing: border-box;
background: @tree-bg;
overflow: auto;
resize: horizontal;
width: 250px;
white-space: nowrap;
max-width: 500px;
min-width: 200px;
padding: 10px 0px;
color: @tree-fg;
li {
cursor: auto;
&:hover > span.element {
text-decoration: underline;
}
&.collapsed ul {
display: none;
}
input {
width: calc(100% - 30px);
}
}
span.element {
cursor: pointer;
}
.active {
text-decoration: underline;
}
.category2 {
margin-top: 2em;
.root {
&> .fa {
min-width: 30px;
cursor: pointer;
}
}
}
.fa.expcol {
margin-left: -10px;
font-size: 14px;
position: absolute;
left: -20px;
top: 9px;
width: auto;
height: 11px;
padding: 0;
margin: 0;
background: white;
z-index: 10;
cursor: default;
&:before {
position:relative;
top: -1px;
}
}
// Expand/collapse lines
ul {
margin: 0px 0px 0px 10px;
list-style: none;
padding-left: 10px;
li {
position: relative;
&:before {
position: absolute;
left: -15px;
top: -0.25em;
content: '';
display: block;
border-left: 1px solid @tree-lines-col;
height: 1em;
border-bottom: 1px solid @tree-lines-col;
width: 17.5px;
}
&:after {
position: absolute;
left: -15px;
bottom: -7px;
content: '';
display: block;
border-left: 1px solid @tree-lines-col;
height: 100%;
}
&.root {
margin: 0px 0px 0px -10px;
&:before {
display: none;
}
&:after {
display: none;
}
}
&:last-child:after {
display: none;
}
}
}
}
/* CONTENT */
#content {
box-sizing: border-box;
background: @content-bg;
color: @content-fg;
overflow: auto;
flex: 1;
display: flex;
flex-flow: column;
h1 {
padding-left: 10px;
margin-top: 10px;
}
.info-box {
height: 40px;
line-height: 40px;
padding-left: 10px;
margin: 10px auto;
background: @info-box-bg;
border: 1px solid @info-box-border;
border-radius: 5px;
span {
cursor: pointer;
margin-left: 10px;
float: right;
}
}
li {
&:not(.header) {
*:not(input) {
pointer-events: none;
}
&:hover {
.name {
text-decoration: underline;
}
}
}
}
div.grid {
padding: 20px;
li {
display: inline-block;
margin: 10px 10px;
width: 140px;
text-align: center;
vertical-align: top;
.name {
width: 100%;
}
input {
width: 100%;
}
.fa {
display: block;
margin: auto;
font-size: 40px;
text-align: center;
&.listonly {
display: none;
}
}
}
.listElement {
display: none;
}
}
.list {
// Make it act as a table!
ul {
display: table;
width: 100%;
padding: 0px 10px;
}
li {
display: table-row;
&> span {
padding: 0 5px;
display: table-cell;
}
}
li {
&.header {
cursor: default;
color: @table-header-fg;
span {
&:not(.fa) {
text-align: left;
}
&.sortasc, &.sortdesc {
float: right;
}
}
&> span {
padding: 15px 5px;
&.active {
font-weight: bold;
}
&.clickable {
cursor: pointer;
&:hover {
background: @table-header-bg;
}
}
}
}
}
.element {
span {
overflow: hidden;
white-space: nowrap;
box-sizing: border-box;
&.icon {
width: 30px;
}
&.type, &.atime, &.ctime {
width: 175px;
}
&.title {
width: 250px;
@media screen and (max-width: 1200px) {
display: none;
}
}
&.folders, &.files {
width: 150px;
}
}
}
}
}
.parentFolder {
cursor: pointer;
margin-left: 10px;
&:hover {
text-decoration: underline;
}
}
#folderContent {
padding-right: 10px;
flex: 1;
}
/* Toolbar */
#driveToolbar {
background: @toolbar-bg;
color: @toolbar-fg;
height: 40px;
display: flex;
flex-flow: row;
border-top: 1px solid @toolbar-border-col;
border-bottom: ;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
z-index: 100;
box-sizing: content-box;
.newPadContainer {
display: inline-block;
height: 100%;
}
button {
&.element {
border-radius: 2px;
height: 30px;
background: @toolbar-button-bg;
color: @toolbar-button-fg;
font-size: 16px;
border: none;
font-weight: bold;
&:hover {
box-shadow: 0px 0px 2px #000;
}
}
&.new {
padding: 0 20px;
}
}
/* The container <div> - needed to position the dropdown content */
.dropdown-bar {
margin: 5px 5px;
line-height: 1em;
position: relative;
display: inline-block;
&.right {
float: right;
/* Right-side buttons */
button {
display: inline-block;
&.active {
display: none;
}
.fa {
margin-right: 0px;
}
}
}
}
.dropdown-bar-content {
margin-top: -3px;
margin-right: 2px;
}
.leftside {
width: 250px;
margin: 0;
padding: 0;
}
.rightside {
margin: 0;
padding: 0;
flex: 1;
}
.path {
display: inline-block;
height: 100%;
line-height: 40px;
cursor: default;
.element {
padding: 5px;
border: 1px solid @toolbar-bg;
border-radius: 2px;
box-sizing: border-box;
&.clickable {
cursor: pointer;
&:hover {
background: @toolbar-path-bg;
border: 1px solid @toolbar-path-border;
}
}
}
}
}

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="cp"> <html class="cp">
<head> <head>
<title>CryptFiles</title> <title>CryptDrive</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<link rel="icon" type="image/png" <link rel="icon" type="image/png"
href="/customize/main-favicon.png" href="/customize/main-favicon.png"
@ -36,7 +36,10 @@
</style> </style>
</head> </head>
<body> <body>
<iframe id="pad-iframe" src="inner.html"></iframe> <iframe id="pad-iframe"></iframe>
<script>
document.getElementById('pad-iframe').setAttribute('src', 'inner.html?' + new Date().getTime());
</script>
</body> </body>
</html> </html>

@ -7,41 +7,50 @@
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> <script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="file.css" /> <link rel="stylesheet" href="file.css" />
<link rel="stylesheet" href="/customize/main.css" />
</head> </head>
<body> <body>
<div id="toolbar" class="toolbar-container"></div> <div id="toolbar" class="toolbar-container"></div>
<div id="driveToolbar"></div>
<div class="app-container"> <div class="app-container">
<div id="tree"> <div id="tree">
</div> </div>
<div id="content"> <div id="content">
</div> </div>
<div id="contextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;"> <div id="treeContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;"> <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="#" class="open" data-localization="fc_open">Open</a></li> <li><a tabindex="-1" href="#" class="open dropdown-item" data-localization="fc_open">Open</a></li>
<li><a tabindex="-1" href="#" class="rename editable" data-localization="fc_rename">Rename</a></li> <li><a tabindex="-1" href="#" class="open_ro dropdown-item" data-localization="fc_open_ro">Open (read-only)</a></li>
<li><a tabindex="-1" href="#" class="delete editable" data-localization="fc_delete">Delete</a></li> <li><a tabindex="-1" href="#" class="rename editable dropdown-item" data-localization="fc_rename">Rename</a></li>
<li><a tabindex="-1" href="#" class="newfolder editable" data-localization="fc_newfolder">New folder</a></li> <li><a tabindex="-1" href="#" class="delete editable dropdown-item" data-localization="fc_delete">Delete</a></li>
<li><a tabindex="-1" href="#" class="newfolder editable dropdown-item" data-localization="fc_newfolder">New folder</a></li>
</ul> </ul>
</div> </div>
<div id="contentContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;"> <div id="contentContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;"> <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="#" class="newfolder editable" data-localization="fc_newfolder">New folder</a></li> <li><a tabindex="-1" href="#" class="newfolder editable dropdown-item" data-localization="fc_newfolder">New folder</a></li>
<li><a tabindex="-1" href="#" class="newdoc own editable" data-type="pad" data-localization="fc_newpad" target="_blank">New pad</a></li> <li><a tabindex="-1" href="#" class="newdoc own editable dropdown-item" data-type="pad" data-localization="fc_newpad" target="_blank">New pad</a></li>
<li><a tabindex="-1" href="#" class="newdoc own editable" data-type="code" data-localization="fc_newcode" target="_blank">New code</a></li> <li><a tabindex="-1" href="#" class="newdoc own editable dropdown-item" data-type="code" data-localization="fc_newcode" target="_blank">New code</a></li>
<li><a tabindex="-1" href="#" class="newdoc own editable" data-type="slide" data-localization="fc_newslide" target="_blank">New slide</a></li> <li><a tabindex="-1" href="#" class="newdoc own editable dropdown-item" data-type="slide" data-localization="fc_newslide" target="_blank">New slide</a></li>
<li><a tabindex="-1" href="#" class="newdoc own editable" data-type="poll" data-localization="fc_newpoll" target="_blank">New poll</a></li> <li><a tabindex="-1" href="#" class="newdoc own editable dropdown-item" data-type="poll" data-localization="fc_newpoll" target="_blank">New poll</a></li>
</ul>
</div>
<div id="defaultContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="#" class="open dropdown-item" data-localization="fc_open">Open</a></li>
<li><a tabindex="-1" href="#" class="open_ro dropdown-item" data-localization="fc_open_ro">Open (read-only)</a></li>
</ul> </ul>
</div> </div>
<div id="trashTreeContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;"> <div id="trashTreeContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;"> <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="#" class="empty editable" data-localization="fc_empty">Empty the trash</a></li> <li><a tabindex="-1" href="#" class="empty editable dropdown-item" data-localization="fc_empty">Empty the trash</a></li>
</ul> </ul>
</div> </div>
<div id="trashContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;"> <div id="trashContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;"> <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="#" class="remove editable" data-localization="fc_remove">Delete permanently</a></li> <li><a tabindex="-1" href="#" class="remove editable dropdown-item" data-localization="fc_remove">Delete permanently</a></li>
<li><a tabindex="-1" href="#" class="restore editable" data-localization="fc_restore">Restore</a></li> <li><a tabindex="-1" href="#" class="restore editable dropdown-item" data-localization="fc_restore">Restore</a></li>
<li><a tabindex="-1" href="#" class="properties" data-localization="fc_prop">Properties</a></li> <li><a tabindex="-1" href="#" class="properties dropdown-item" data-localization="fc_prop">Properties</a></li>
</ul> </ul>
</div> </div>
</div> </div>

File diff suppressed because it is too large Load Diff

@ -71,7 +71,7 @@ li {
#tree { #tree {
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
box-sizing: border-box; box-sizing: border-box;
background: white; background: #eee;
overflow: auto; overflow: auto;
resize: horizontal; resize: horizontal;
width: 350px; width: 350px;
@ -97,7 +97,7 @@ li {
text-decoration: underline; text-decoration: underline;
} }
#tree #trashTree, #tree #unsortedTree, #tree #templateTree, #tree #allfilesTree { #tree .category2 {
margin-top: 2em; margin-top: 2em;
} }
@ -184,6 +184,7 @@ li {
#content h1 { #content h1 {
padding-left: 10px; padding-left: 10px;
margin-top: 10px;
} }
#content .info-box { #content .info-box {
@ -200,12 +201,6 @@ li {
float: right; float: right;
} }
.topButtonContainer {
border: 1px solid #ccc;
float: right;
border-radius: 4px;
}
.parentFolder { .parentFolder {
cursor: pointer; cursor: pointer;
margin-left: 10px; margin-left: 10px;
@ -248,16 +243,20 @@ li {
display: block; display: block;
margin: auto; margin: auto;
font-size: 40px; font-size: 40px;
width: auto;
text-align: center; text-align: center;
} }
#content .grid li .fa.listonly {
display: none;
}
#content .list li { #content .list li {
display: flex; display: flex;
flex-flow: row; flex-flow: row;
align-items: center; align-items: center;
padding-right: 0px; padding-right: 0px;
} }
#content .list li .element { #content .list li .element {
display: inline-flex; display: inline-flex;
flex: 1; flex: 1;
@ -325,3 +324,55 @@ li {
display: inline; display: inline;
} }
} }
/* Toolbar */
#driveToolbar {
background: #ddd;
height: 40px;
}
.newPadContainer {
display: inline-block;
height: 100%;
}
button.newElement {
border-radius: 2px;
height: 30px;
background: #888;
color: #eee;
font-size: 16px;
border: none;
font-weight: bold;
}
button.newElement:hover {
box-shadow: 0px 0px 2px #000;
}
/* The container <div> - needed to position the dropdown content */
#driveToolbar .dropdown-bar {
margin: 4px 5px;
position: relative;
display: inline-block;
}
#driveToolbar .dropdown-bar.right {
float: right;
}
/* Right-side buttons */
#driveToolbar .dropdown-bar.right button {
display: inline-block;
}
#driveToolbar .dropdown-bar.right button.active {
display: none;
}
#driveToolbar .dropdown-bar.right button .fa {
margin-right: 0px;
}
.dropdown-bar-content {
margin-top: -3px;
margin-right: 2px;
}

@ -13,7 +13,7 @@ define([
'/bower_components/fabric.js/dist/fabric.min.js', '/bower_components/fabric.js/dist/fabric.min.js',
'/bower_components/jquery/dist/jquery.min.js', '/bower_components/jquery/dist/jquery.min.js',
'/bower_components/file-saver/FileSaver.min.js', '/bower_components/file-saver/FileSaver.min.js',
'/customize/pad.js' //'/customize/pad.js'
], function (Config, Realtime, Crypto, TextPatcher, JSONSortify, JsonOT, Cryptpad) { ], function (Config, Realtime, Crypto, TextPatcher, JSONSortify, JsonOT, Cryptpad) {
var saveAs = window.saveAs; var saveAs = window.saveAs;

@ -12,16 +12,17 @@ define([
//console.log("plaintext"); //console.log("plaintext");
//console.log(body); //console.log(body);
/*
0 && Crypt.put(body, function (e, out) { 0 && Crypt.put(body, function (e, out) {
if (e) { return void console.error(e); } if (e) { return void console.error(e); }
if (out) { if (out) {
console.log(out); console.log(out);
} }
}); }); */
var data = {}; var data = {};
(_ => { (function () {
var cyphertext = data.payload = Crypto.encrypt(body, key); var cyphertext = data.payload = Crypto.encrypt(body, key);
console.log("encrypted"); console.log("encrypted");
console.log(cyphertext); console.log(cyphertext);

@ -1,28 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<!--
This file only exists for supporting some old URLs that we're trying not to break.
Legacy support is tricky.
:(
-->
<script>
(function () {
if (/cryptpad\.fr$/i.test(window.location.hostname)) {
window.location.hostname = 'old.cryptpad.fr';
}
}());
</script>
</head>
<body>
<p>There's nothing here.</p>
<p>You probably want to visit <strong><a href="https://old.cryptpad.fr">old.cryptpad.fr</a></strong></p>
</body>
</html>

@ -1,7 +1,9 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="cp pad"> <html class="cp pad">
<head> <head>
<title>CryptPad</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" <link rel="icon" type="image/png"
href="/customize/main-favicon.png" href="/customize/main-favicon.png"
data-main-favicon="/customize/main-favicon.png" data-main-favicon="/customize/main-favicon.png"
@ -58,7 +60,10 @@
</style> </style>
</head> </head>
<body> <body>
<iframe id="pad-iframe" src="inner.html"></iframe> <iframe id="pad-iframe"></iframe>
<script>
document.getElementById('pad-iframe').setAttribute('src', 'inner.html?' + new Date().getTime());
</script>
</body> </body>
</html> </html>

@ -6,6 +6,9 @@
<script src="/bower_components/jquery/dist/jquery.min.js"></script> <script src="/bower_components/jquery/dist/jquery.min.js"></script>
<script src="/bower_components/ckeditor/ckeditor.js"></script> <script src="/bower_components/ckeditor/ckeditor.js"></script>
<style> <style>
html, body {
margin: 0px;
}
#cke_1_top { #cke_1_top {
overflow: visible; overflow: visible;
} }
@ -16,6 +19,7 @@
#cke_1_top .cryptpad-toolbar { #cke_1_top .cryptpad-toolbar {
margin-bottom: 1px; margin-bottom: 1px;
padding: 0px; padding: 0px;
display: inline-block;
} }
</style> </style>
</head> </head>

@ -62,6 +62,14 @@ define([
Cryptpad: Cryptpad, Cryptpad: Cryptpad,
}; };
var emitResize = module.emitResize = function () {
var cw = $('#pad-iframe')[0].contentWindow;
var evt = cw.document.createEvent('UIEvents');
evt.initUIEvent('resize', true, false, cw, 0);
cw.dispatchEvent(evt);
};
var toolbar; var toolbar;
var isNotMagicLine = function (el) { var isNotMagicLine = function (el) {
@ -98,8 +106,12 @@ define([
// document itself and causes problems when it's sent across the wire and reflected back // document itself and causes problems when it's sent across the wire and reflected back
removePlugins: 'resize', removePlugins: 'resize',
extraPlugins: 'autolink,colorbutton,colordialog,font', extraPlugins: 'autolink,colorbutton,colordialog,font',
//skin: 'moono', toolbarGroups: [{"name":"clipboard","groups":["clipboard","undo"]},{"name":"editing","groups":["find","selection"]},{"name":"links"},{"name":"insert"},{"name":"forms"},{"name":"tools"},{"name":"document","groups":["mode","document","doctools"]},{"name":"others"},{"name":"basicstyles","groups":["basicstyles","cleanup"]},{"name":"paragraph","groups":["list","indent","blocks","align","bidi"]},{"name":"styles"},{"name":"colors"}],
toolbarGroups: [{"name":"clipboard","groups":["clipboard","undo"]},{"name":"editing","groups":["find","selection"]},{"name":"links"},{"name":"insert"},{"name":"forms"},{"name":"tools"},{"name":"document","groups":["mode","document","doctools"]},{"name":"others"},{"name":"basicstyles","groups":["basicstyles","cleanup"]},{"name":"paragraph","groups":["list","indent","blocks","align","bidi"]},{"name":"styles"},{"name":"colors"}] //skin: 'moono-cryptpad,/pad/themes/moono-cryptpad/'
//skin: 'flat,/pad/themes/flat/'
//skin: 'moono-lisa,/pad/themes/moono-lisa/'
//skin: 'moono-dark,/pad/themes/moono-dark/'
//skin: 'office2013,/pad/themes/office2013/'
}); });
editor.on('instanceReady', function (Ckeditor) { editor.on('instanceReady', function (Ckeditor) {
@ -362,7 +374,9 @@ define([
defaultTitle: defaultName defaultTitle: defaultName
} }
}; };
hjson[3].metadata.title = document.title; if (!initializing) {
hjson[3].metadata.title = document.title;
}
return stringify(hjson); return stringify(hjson);
}; };
@ -433,6 +447,7 @@ define([
// Extract the user list (metadata) from the hyperjson // Extract the user list (metadata) from the hyperjson
var hjson = JSON.parse(shjson); var hjson = JSON.parse(shjson);
var peerMetadata = hjson[3]; var peerMetadata = hjson[3];
var titleUpdated = false;
if (peerMetadata && peerMetadata.metadata) { if (peerMetadata && peerMetadata.metadata) {
if (peerMetadata.metadata.users) { if (peerMetadata.metadata.users) {
var userData = peerMetadata.metadata.users; var userData = peerMetadata.metadata.users;
@ -443,9 +458,13 @@ define([
updateDefaultTitle(peerMetadata.metadata.defaultTitle); updateDefaultTitle(peerMetadata.metadata.defaultTitle);
} }
if (typeof peerMetadata.metadata.title !== "undefined") { if (typeof peerMetadata.metadata.title !== "undefined") {
updateTitle(peerMetadata.metadata.title); updateTitle(peerMetadata.metadata.title || defaultName);
titleUpdated = true;
} }
} }
if (!titleUpdated) {
updateTitle(defaultName);
}
}; };
var unnotify = function () { var unnotify = function () {
@ -465,6 +484,8 @@ define([
var onRemote = realtimeOptions.onRemote = function (info) { var onRemote = realtimeOptions.onRemote = function (info) {
if (initializing) { return; } if (initializing) { return; }
var oldShjson = stringifyDOM(inner);
var shjson = info.realtime.getUserDoc(); var shjson = info.realtime.getUserDoc();
// remember where the cursor is // remember where the cursor is
@ -473,6 +494,12 @@ define([
// Update the user list (metadata) from the hyperjson // Update the user list (metadata) from the hyperjson
updateMetadata(shjson); updateMetadata(shjson);
var newInner = JSON.parse(shjson);
var newSInner;
if (newInner.length > 2) {
newSInner = stringify(newInner[2]);
}
// build a dom from HJSON, diff, and patch the editor // build a dom from HJSON, diff, and patch the editor
applyHjson(shjson); applyHjson(shjson);
@ -506,7 +533,12 @@ define([
} }
} }
} }
notify();
// Notify only when the content has changed, not when someone has joined/left
var oldSInner = stringify(JSON.parse(oldShjson)[2]);
if (newSInner && newSInner !== oldSInner) {
notify();
}
}; };
var getHTML = function (Dom) { var getHTML = function (Dom) {
@ -547,7 +579,16 @@ define([
var onInit = realtimeOptions.onInit = function (info) { var onInit = realtimeOptions.onInit = function (info) {
userList = info.userList; userList = info.userList;
module.userName = {};
// The lastName is stored in an object passed to the toolbar so that when the user clicks on
// the "change display name" button, the prompt already knows his current name
getLastName(function (err, lastName) {
module.userName.lastName = lastName;
});
var config = { var config = {
displayed: ['useradmin', 'language', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad'],
userData: userData, userData: userData,
readOnly: readOnly, readOnly: readOnly,
ifrw: ifrw, ifrw: ifrw,
@ -556,6 +597,10 @@ define([
defaultName: defaultName, defaultName: defaultName,
suggestName: suggestName suggestName: suggestName
}, },
userName: {
setName: setName,
lastName: module.userName
},
common: Cryptpad common: Cryptpad
}; };
if (readOnly) {delete config.changeNameID; } if (readOnly) {delete config.changeNameID; }
@ -565,6 +610,7 @@ define([
var $userBlock = $bar.find('.' + Toolbar.constants.username); var $userBlock = $bar.find('.' + Toolbar.constants.username);
var $editShare = $bar.find('.' + Toolbar.constants.editShare); var $editShare = $bar.find('.' + Toolbar.constants.editShare);
var $viewShare = $bar.find('.' + Toolbar.constants.viewShare); var $viewShare = $bar.find('.' + Toolbar.constants.viewShare);
var $usernameButton = module.$userNameButton = $($bar.find('.' + Toolbar.constants.changeUsername));
var editHash; var editHash;
var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys); var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys);
@ -573,14 +619,7 @@ define([
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
} }
// Store the object sent for the "change username" button so that we can update the field value correctly
var userNameButtonObject = module.userName = {};
/* add a "change username" button */
getLastName(function (err, lastName) {
userNameButtonObject.lastName = lastName;
var $username = module.$userNameButton = Cryptpad.createButton('username', false, userNameButtonObject, setName).hide();
$userBlock.append($username);
});
/* add an export button */ /* add an export button */
var $export = Cryptpad.createButton('export', true, {}, exportFile); var $export = Cryptpad.createButton('export', true, {}, exportFile);
@ -606,6 +645,9 @@ define([
if (!readOnly) { if (!readOnly) {
$editShare.append(Cryptpad.createButton('editshare', false, {editHash: editHash})); $editShare.append(Cryptpad.createButton('editshare', false, {editHash: editHash}));
if (viewHash) {
$editShare.append($('<hr>'));
}
} }
if (viewHash) { if (viewHash) {
/* add a 'links' button */ /* add a 'links' button */
@ -617,27 +659,19 @@ define([
// set the hash // set the hash
if (!readOnly) { Cryptpad.replaceHash(editHash); } if (!readOnly) { Cryptpad.replaceHash(editHash); }
Cryptpad.getPadTitle(function (err, title) {
if (err) {
console.error(err);
console.log("Couldn't get pad title");
return;
}
updateTitle(title || defaultName);
});
}; };
// this should only ever get called once, when the chain syncs // this should only ever get called once, when the chain syncs
var onReady = realtimeOptions.onReady = function (info) { var onReady = realtimeOptions.onReady = function (info) {
if (!APP.isMaximized) { if (!module.isMaximized) {
editor.execCommand('maximize'); editor.execCommand('maximize');
// We have to call it 3 times in Safari in order to have the editor fully maximized -_- module.isMaximized = true;
// We have to call it 3 times in Safari
// in order to have the editor fully maximized -_-
if ((''+window.navigator.vendor).indexOf('Apple') !== -1) { if ((''+window.navigator.vendor).indexOf('Apple') !== -1) {
editor.execCommand('maximize'); editor.execCommand('maximize');
editor.execCommand('maximize'); editor.execCommand('maximize');
} }
APP.isMaximized = true;
} }
module.patchText = TextPatcher.create({ module.patchText = TextPatcher.create({
@ -664,7 +698,8 @@ define([
console.log("Unlocking editor"); console.log("Unlocking editor");
setEditable(true); setEditable(true);
initializing = false; initializing = false;
Cryptpad.removeLoadingScreen(); Cryptpad.removeLoadingScreen(emitResize);
// Update the toolbar list: // Update the toolbar list:
// Add the current user in the metadata if he has edit rights // Add the current user in the metadata if he has edit rights
if (readOnly) { return; } if (readOnly) { return; }
@ -758,9 +793,17 @@ define([
var first = function () { var first = function () {
Ckeditor = ifrw.CKEDITOR; Ckeditor = ifrw.CKEDITOR;
if (Ckeditor) { if (Ckeditor) {
//andThen(Ckeditor); //andThen(Ckeditor);
// mobile configuration
Ckeditor.config.toolbarCanCollapse = true;
Ckeditor.config.height = '72vh';
if (screen.height < 800) {
Ckeditor.config.toolbarStartupExpanded = false;
$('meta[name=viewport]').attr('content', 'width=device-width, initial-scale=1.0, user-scalable=no');
} else {
$('meta[name=viewport]').attr('content', 'width=device-width, initial-scale=1.0, user-scalable=yes');
}
second(Ckeditor); second(Ckeditor);
} else { } else {
console.log("Ckeditor was not defined. Trying again in %sms",interval); console.log("Ckeditor was not defined. Trying again in %sms",interval);

@ -4,6 +4,11 @@
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title data-localization="poll_title">Zero Knowledge Date Picker</title> <title data-localization="poll_title">Zero Knowledge Date Picker</title>
<link rel="icon" type="image/png"
href="/customize/main-favicon.png"
data-main-favicon="/customize/main-favicon.png"
data-alt-favicon="/customize/alt-favicon.png"
id="favicon" />
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css"> <link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="/customize/main.css" /> <link rel="stylesheet" href="/customize/main.css" />
<script data-main="main" src="/bower_components/requirejs/require.js"></script> <script data-main="main" src="/bower_components/requirejs/require.js"></script>
@ -22,10 +27,11 @@
color: #000; color: #000;
line-height: auto; line-height: auto;
} }
.cryptpad-toolbar {
display: inline-block;
}
.realtime { .realtime {
display: block; display: block;
overflow: auto;
max-height: 100%; max-height: 100%;
max-width: 100%; max-width: 100%;
} }
@ -43,11 +49,6 @@
font: white; font: white;
border: 0px; border: 0px;
} }
td label {
border: .5px solid black;
}
table#table { table#table {
margin: 0px; margin: 0px;
} }
@ -95,7 +96,7 @@
} }
#description[disabled] { #description[disabled] {
resize: none; resize: none;
color: #bbb; color: #000;
border: 1px solid #444; border: 1px solid #444;
} }

@ -12,7 +12,7 @@ define([
'/common/notify.js', '/common/notify.js',
'/bower_components/file-saver/FileSaver.min.js', '/bower_components/file-saver/FileSaver.min.js',
'/bower_components/jquery/dist/jquery.min.js', '/bower_components/jquery/dist/jquery.min.js',
], function (Config, Messages, TextPatcher, Listmap, Crypto, Cryptpad, Hyperjson, Renderer, Toolbar) { ], function (Config, Messages, TextPatcher, Listmap, Crypto, Cryptpad, Hyperjson, Renderer, Toolbar, Visible, Notify) {
var $ = window.jQuery; var $ = window.jQuery;
var unlockHTML = '<i class="fa fa-unlock" aria-hidden="true"></i>'; var unlockHTML = '<i class="fa fa-unlock" aria-hidden="true"></i>';
@ -22,6 +22,16 @@ define([
var secret = Cryptpad.getSecrets(); var secret = Cryptpad.getSecrets();
var readOnly = secret.keys && !secret.keys.editKeyStr; var readOnly = secret.keys && !secret.keys.editKeyStr;
if (!secret.keys) {
secret.keys = secret.key;
}
var DEBUG = false;
var debug = console.log;
if (!DEBUG) {
debug = function() {};
}
var error = console.error;
Cryptpad.addLoadingScreen(); Cryptpad.addLoadingScreen();
var onConnectError = function (info) { var onConnectError = function (info) {
@ -182,10 +192,27 @@ define([
} }
}; };
var unnotify = function () {
if (APP.tabNotification &&
typeof(APP.tabNotification.cancel) === 'function') {
APP.tabNotification.cancel();
}
};
var notify = function () {
if (Visible.isSupported() && !Visible.currently()) {
unnotify();
APP.tabNotification = Notify.tab(1000, 10);
}
};
/* Any time the realtime object changes, call this function */ /* Any time the realtime object changes, call this function */
var change = function (o, n, path, throttle) { var change = function (o, n, path, throttle) {
if (path && !Cryptpad.isArray(path)) {
return;
}
if (path && path.join) { if (path && path.join) {
console.log("Change from [%s] to [%s] at [%s]", debug("Change from [%s] to [%s] at [%s]",
o, n, path.join(', ')); o, n, path.join(', '));
} }
@ -207,6 +234,7 @@ define([
https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
*/ */
notify();
if (throttle) { if (throttle) {
if (APP.throttled) { window.clearTimeout(APP.throttled); } if (APP.throttled) { window.clearTimeout(APP.throttled); }
@ -235,7 +263,7 @@ define([
var type = input.type.toLowerCase(); var type = input.type.toLowerCase();
var id = getRealtimeId(input); var id = getRealtimeId(input);
console.log(input); debug(input);
var object = APP.proxy; var object = APP.proxy;
@ -246,22 +274,22 @@ define([
switch (type) { switch (type) {
case 'text': case 'text':
console.log("text[rt-id='%s'] [%s]", id, input.value); debug("text[rt-id='%s'] [%s]", id, input.value);
if (!input.value) { return void console.log("Hit enter?"); } if (!input.value) { return void debug("Hit enter?"); }
Render.setValue(object, id, input.value); Render.setValue(object, id, input.value);
change(null, null, null, 50); change(null, null, null, 50);
break; break;
case 'checkbox': case 'checkbox':
console.log("checkbox[tr-id='%s'] %s", id, input.checked); debug("checkbox[tr-id='%s'] %s", id, input.checked);
if (APP.editable.col.indexOf(x) >= 0 || x === APP.userid) { if (APP.editable.col.indexOf(x) >= 0 || x === APP.userid) {
Render.setValue(object, id, input.checked); Render.setValue(object, id, input.checked);
change(); change();
} else { } else {
console.log('checkbox locked'); debug('checkbox locked');
} }
break; break;
default: default:
console.log("Input[type='%s']", type); debug("Input[type='%s']", type);
break; break;
} }
}; };
@ -301,7 +329,7 @@ define([
} else if (type === 'cell') { } else if (type === 'cell') {
change(); change();
} else { } else {
console.log("UNHANDLED"); debug("UNHANDLED");
} }
}; };
@ -326,10 +354,10 @@ define([
var target = e && e.target; var target = e && e.target;
if (isKeyup) { if (isKeyup) {
console.log("Keyup!"); debug("Keyup!");
} }
if (!target) { return void console.log("NO TARGET"); } if (!target) { return void debug("NO TARGET"); }
var nodeName = target && target.nodeName; var nodeName = target && target.nodeName;
@ -349,7 +377,7 @@ define([
//console.error(new Error("C'est pas possible!")); //console.error(new Error("C'est pas possible!"));
break; break;
default: default:
console.log(target, nodeName); debug(target, nodeName);
break; break;
} }
}; };
@ -359,14 +387,21 @@ define([
*/ */
var prepareProxy = function (proxy, schema) { var prepareProxy = function (proxy, schema) {
if (proxy && proxy.version === 1) { return; } if (proxy && proxy.version === 1) { return; }
console.log("Configuring proxy schema..."); debug("Configuring proxy schema...");
proxy.info = proxy.info || schema.info;
Object.keys(schema.info).forEach(function (k) {
if (!proxy.info[k]) { proxy.info[k] = schema.info[k]; }
});
proxy.table = proxy.table || schema.table;
Object.keys(schema.table).forEach(function (k) {
if (!proxy.table[k]) { proxy.table[k] = schema.table[k]; }
});
proxy.info = schema.info;
proxy.table = schema.table;
proxy.version = 1; proxy.version = 1;
}; };
/* /*
*/ */
@ -381,88 +416,90 @@ define([
}); });
}; };
var userData = APP.userData = {}; // List of pretty names for all users (mapped with their ID) var userData = APP.userData = {}; // List of pretty names for all users (mapped with their ID)
var userList; // List of users still connected to the channel (server IDs) var userList; // List of users still connected to the channel (server IDs)
var addToUserData = function(data) { var addToUserData = function(data) {
var users = userList ? userList.users : undefined; var users = userList ? userList.users : undefined;
//var userData = APP.proxy.info.userData; //var userData = APP.proxy.info.userData;
for (var attrname in data) { userData[attrname] = data[attrname]; } for (var attrname in data) { userData[attrname] = data[attrname]; }
if (users && users.length) { if (users && users.length) {
for (var userKey in userData) { for (var userKey in userData) {
if (users.indexOf(userKey) === -1) { delete userData[userKey]; } if (users.indexOf(userKey) === -1) { delete userData[userKey]; }
} }
} }
if(userList && typeof userList.onChange === "function") { if(userList && typeof userList.onChange === "function") {
userList.onChange(userData); userList.onChange(userData);
} }
APP.proxy.info.userData = userData; APP.proxy.info.userData = userData;
}; };
//var myData = {}; //var myData = {};
var getLastName = function (cb) { var getLastName = function (cb) {
Cryptpad.getAttribute('username', function (err, userName) { Cryptpad.getAttribute('username', function (err, userName) {
cb(err, userName || ''); cb(err, userName || '');
}); });
}; };
var setName = APP.setName = function (newName) { var setName = APP.setName = function (newName) {
if (typeof(newName) !== 'string') { return; } if (typeof(newName) !== 'string') { return; }
var myUserNameTemp = Cryptpad.fixHTML(newName.trim()); var myUserNameTemp = Cryptpad.fixHTML(newName.trim());
if(myUserNameTemp.length > 32) { if(myUserNameTemp.length > 32) {
myUserNameTemp = myUserNameTemp.substr(0, 32); myUserNameTemp = myUserNameTemp.substr(0, 32);
} }
var myUserName = myUserNameTemp; var myUserName = myUserNameTemp;
var myID = APP.myID; var myID = APP.myID;
var myData = {}; var myData = {};
myData[myID] = { myData[myID] = {
name: myUserName name: myUserName
}; };
addToUserData(myData); addToUserData(myData);
Cryptpad.setAttribute('username', newName, function (err, data) { Cryptpad.setAttribute('username', newName, function (err, data) {
if (err) { if (err) {
console.error("Couldn't set username"); console.error("Couldn't set username");
return; return;
} }
APP.userName.lastName = myUserName; APP.userName.lastName = myUserName;
//change(); //change();
}); });
}; };
var updateTitle = function (newTitle) {
if (newTitle === document.title) { return; }
// Change the title now, and set it back to the old value if there is an error
var oldTitle = document.title;
document.title = newTitle;
Cryptpad.renamePad(newTitle, function (err, data) {
if (err) {
console.log("Couldn't set pad title");
console.error(err);
document.title = oldTitle;
return;
}
document.title = data;
APP.$bar.find('.' + Toolbar.constants.title).find('span.title').text(data);
APP.$bar.find('.' + Toolbar.constants.title).find('input').val(data);
});
};
var updateDefaultTitle = function (defaultTitle) { var updateTitle = function (newTitle) {
defaultName = defaultTitle; if (newTitle === document.title) { return; }
APP.$bar.find('.' + Toolbar.constants.title).find('input').attr("placeholder", defaultName); // Change the title now, and set it back to the old value if there is an error
}; var oldTitle = document.title;
var renameCb = function (err, title) { document.title = newTitle;
if (err) { return; } Cryptpad.renamePad(newTitle, function (err, data) {
document.title = title; if (err) {
APP.proxy.info.title = title; debug("Couldn't set pad title");
}; error(err);
document.title = oldTitle;
return;
}
document.title = data;
APP.$bar.find('.' + Toolbar.constants.title).find('span.title').text(data);
APP.$bar.find('.' + Toolbar.constants.title).find('input').val(data);
});
};
var suggestName = function (fallback) { var updateDefaultTitle = function (defaultTitle) {
return document.title || defaultName || ""; defaultName = defaultTitle;
}; APP.$bar.find('.' + Toolbar.constants.title).find('input').attr("placeholder", defaultName);
};
var renameCb = function (err, title) {
if (err) { return; }
document.title = title;
APP.proxy.info.title = title === defaultName ? "" : title;
};
var suggestName = function (fallback) {
if (document.title === defaultName) {
return fallback || "";
}
return document.title || defaultName || "";
};
var copyObject = function (obj) { var copyObject = function (obj) {
return JSON.parse(JSON.stringify(obj)); return JSON.parse(JSON.stringify(obj));
@ -473,8 +510,8 @@ define([
var $description = $('#description').attr('placeholder', Messages.poll_descriptionHint || 'description'); var $description = $('#description').attr('placeholder', Messages.poll_descriptionHint || 'description');
var ready = function (info, userid, readOnly) { var ready = function (info, userid, readOnly) {
console.log("READY"); debug("READY");
console.log('userid: %s', userid); debug('userid: %s', userid);
var proxy = APP.proxy; var proxy = APP.proxy;
var uncommitted = APP.uncommitted = {}; var uncommitted = APP.uncommitted = {};
@ -565,6 +602,7 @@ define([
.on('change', ['info'], function (o, n, p) { .on('change', ['info'], function (o, n, p) {
if (p[1] === 'title') { if (p[1] === 'title') {
updateTitle(n); updateTitle(n);
notify();
} else if (p[1] === "userData") { } else if (p[1] === "userData") {
addToUserData(APP.proxy.info.userData); addToUserData(APP.proxy.info.userData);
} else if (p[1] === 'description') { } else if (p[1] === 'description') {
@ -581,15 +619,23 @@ define([
el.selectionStart = selects[0]; el.selectionStart = selects[0];
el.selectionEnd = selects[1]; el.selectionEnd = selects[1];
} }
notify();
} }
console.log("change: (%s, %s, [%s])", o, n, p.join(', ')); debug("change: (%s, %s, [%s])", o, n, p.join(', '));
}) })
.on('change', ['table'], change) .on('change', ['table'], change)
.on('remove', [], change); .on('remove', [], change);
addToUserData(APP.proxy.info.userData); addToUserData(APP.proxy.info.userData);
if (Visible.isSupported()) {
Visible.onChange(function (yes) {
if (yes) { unnotify(); }
});
}
getLastName(function (err, lastName) { getLastName(function (err, lastName) {
APP.ready = true; APP.ready = true;
@ -633,7 +679,16 @@ define([
}); });
userList = APP.userList = info.userList; userList = APP.userList = info.userList;
APP.userName = {};
// The lastName is stored in an object passed to the toolbar so that when the user clicks on
// the "change display name" button, the prompt already knows his current name
getLastName(function (err, lastName) {
APP.userName.lastName = lastName;
});
var config = { var config = {
displayed: ['useradmin', 'language', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad'],
userData: userData, userData: userData,
readOnly: readOnly, readOnly: readOnly,
title: { title: {
@ -641,8 +696,12 @@ define([
defaultName: defaultName, defaultName: defaultName,
suggestName: suggestName suggestName: suggestName
}, },
userName: {
setName: setName,
lastName: APP.userName
},
ifrw: window, ifrw: window,
common: Cryptpad common: Cryptpad,
}; };
var toolbar = info.realtime.toolbar = Toolbar.create(APP.$bar, info.myID, info.realtime, info.getLag, userList, config); var toolbar = info.realtime.toolbar = Toolbar.create(APP.$bar, info.myID, info.realtime, info.getLag, userList, config);
@ -651,15 +710,7 @@ define([
var $userBlock = $bar.find('.' + Toolbar.constants.username); var $userBlock = $bar.find('.' + Toolbar.constants.username);
var $editShare = $bar.find('.' + Toolbar.constants.editShare); var $editShare = $bar.find('.' + Toolbar.constants.editShare);
var $viewShare = $bar.find('.' + Toolbar.constants.viewShare); var $viewShare = $bar.find('.' + Toolbar.constants.viewShare);
var $usernameButton = APP.$userNameButton = $($bar.find('.' + Toolbar.constants.changeUsername));
// Store the object sent for the "change username" button so that we can update the field value correctly
var userNameButtonObject = APP.userName = {};
/* add a "change username" button */
getLastName(function (err, lastName) {
userNameButtonObject.lastName = lastName;
var $username = APP.$userNameButton = Cryptpad.createButton('username', false, userNameButtonObject, setName).hide();
$userBlock.append($username);
});
/* add a forget button */ /* add a forget button */
var forgetCb = function (err, title) { var forgetCb = function (err, title) {
@ -685,8 +736,8 @@ define([
Cryptpad.getPadTitle(function (err, title) { Cryptpad.getPadTitle(function (err, title) {
if (err) { if (err) {
console.error(err); error(err);
console.log("Couldn't get pad title"); debug("Couldn't get pad title");
return; return;
} }
updateTitle(title || defaultName); updateTitle(title || defaultName);
@ -737,7 +788,7 @@ define([
Cryptpad.getAttribute(HIDE_INTRODUCTION_TEXT, function (e, value) { Cryptpad.getAttribute(HIDE_INTRODUCTION_TEXT, function (e, value) {
if (e) { console.error(e); } if (e) { console.error(e); }
if (value === null) { if (!value) {
Cryptpad.setAttribute(HIDE_INTRODUCTION_TEXT, "1", function (e) { Cryptpad.setAttribute(HIDE_INTRODUCTION_TEXT, "1", function (e) {
if (e) { console.error(e); } if (e) { console.error(e); }
}); });

@ -127,7 +127,6 @@ var Renderer = function (Cryptpad) {
}; };
var createRow = Render.createRow = function (obj, cb, id, value) { var createRow = Render.createRow = function (obj, cb, id, value) {
console.error('new row!');
var order = Cryptpad.find(obj, ['table', 'rowsOrder']); var order = Cryptpad.find(obj, ['table', 'rowsOrder']);
if (!order) { throw new Error("Uninitialized realtime object!"); } if (!order) { throw new Error("Uninitialized realtime object!"); }
id = id || rowuid(); id = id || rowuid();

@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="cp slide"> <html class="cp slide">
<head> <head>
<title>CryptPad</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script data-main="main" src="/bower_components/requirejs/require.js"></script> <script data-main="main" src="/bower_components/requirejs/require.js"></script>
<script> <script>
@ -50,7 +51,10 @@
</head> </head>
<body> <body>
<div id="iframe-container"> <div id="iframe-container">
<iframe id="pad-iframe" src="inner.html"></iframe> <iframe id="pad-iframe"></iframe>
<script>
document.getElementById('pad-iframe').setAttribute('src', 'inner.html?' + new Date().getTime());
</script>
</div> </div>
</body> </body>
</html> </html>

@ -92,6 +92,9 @@
<span class="cp slide"> <span class="cp slide">
<div id="modal"> <div id="modal">
<div id="button_exit" class="button"><span class="fa fa-times"></span></div>
<div id="button_left" class="button"><span class="fa fa-chevron-left"></span></div>
<div id="button_right" class="button"><span class="fa fa-chevron-right"></span></div>
<div id="content"></div> <div id="content"></div>
</div> </div>
</span> </span>

@ -126,7 +126,7 @@ define([
} }
editor.setOption('theme', theme); editor.setOption('theme', theme);
} }
if ($select && $select.val) { $select.val(theme || 'default'); } if ($select) { $select.find('.buttonTitle').text(theme || 'Theme'); }
}; };
}()); }());
@ -216,7 +216,9 @@ define([
defaultTitle: defaultName defaultTitle: defaultName
} }
}; };
obj.metadata.title = APP.title; if (!initializing) {
obj.metadata.title = APP.title;
}
if (textColor) { if (textColor) {
obj.metadata.color = textColor; obj.metadata.color = textColor;
} }
@ -384,6 +386,7 @@ define([
var updateMetadata = function(shjson) { var updateMetadata = function(shjson) {
// Extract the user list (metadata) from the hyperjson // Extract the user list (metadata) from the hyperjson
var json = (shjson === "") ? "" : JSON.parse(shjson); var json = (shjson === "") ? "" : JSON.parse(shjson);
var titleUpdated = false;
if (json && json.metadata) { if (json && json.metadata) {
if (json.metadata.users) { if (json.metadata.users) {
var userData = json.metadata.users; var userData = json.metadata.users;
@ -394,10 +397,14 @@ define([
updateDefaultTitle(json.metadata.defaultTitle); updateDefaultTitle(json.metadata.defaultTitle);
} }
if (typeof json.metadata.title !== "undefined") { if (typeof json.metadata.title !== "undefined") {
updateTitle(json.metadata.title); updateTitle(json.metadata.title || defaultName);
titleUpdated = true;
} }
updateColors(json.metadata.color, json.metadata.backColor); updateColors(json.metadata.color, json.metadata.backColor);
} }
if (!titleUpdated) {
updateTitle(defaultName);
}
}; };
var renameCb = function (err, title) { var renameCb = function (err, title) {
@ -409,7 +416,16 @@ define([
var onInit = config.onInit = function (info) { var onInit = config.onInit = function (info) {
userList = info.userList; userList = info.userList;
module.userName = {};
// The lastName is stored in an object passed to the toolbar so that when the user clicks on
// the "change display name" button, the prompt already knows his current name
getLastName(function (err, lastName) {
module.userName.lastName = lastName;
});
var config = { var config = {
displayed: ['useradmin', 'language', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad'],
userData: userData, userData: userData,
readOnly: readOnly, readOnly: readOnly,
ifrw: ifrw, ifrw: ifrw,
@ -418,6 +434,10 @@ define([
defaultName: defaultName, defaultName: defaultName,
suggestName: suggestName suggestName: suggestName
}, },
userName: {
setName: setName,
lastName: module.userName
},
common: Cryptpad common: Cryptpad
}; };
if (readOnly) {delete config.changeNameID; } if (readOnly) {delete config.changeNameID; }
@ -427,6 +447,7 @@ define([
var $userBlock = $bar.find('.' + Toolbar.constants.username); var $userBlock = $bar.find('.' + Toolbar.constants.username);
var $editShare = $bar.find('.' + Toolbar.constants.editShare); var $editShare = $bar.find('.' + Toolbar.constants.editShare);
var $viewShare = $bar.find('.' + Toolbar.constants.viewShare); var $viewShare = $bar.find('.' + Toolbar.constants.viewShare);
var $usernameButton = module.$userNameButton = $($bar.find('.' + Toolbar.constants.changeUsername));
var editHash; var editHash;
var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys); var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys);
@ -435,15 +456,6 @@ define([
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
} }
// Store the object sent for the "change username" button so that we can update the field value correctly
var userNameButtonObject = module.userName = {};
/* add a "change username" button */
getLastName(function (err, lastName) {
userNameButtonObject.lastName = lastName;
var $username = module.$userNameButton = Cryptpad.createButton('username', false, userNameButtonObject, setName).hide();
$userBlock.append($username);
});
/* add an export button */ /* add an export button */
var $export = Cryptpad.createButton('export', true, {}, exportText); var $export = Cryptpad.createButton('export', true, {}, exportText);
$rightside.append($export); $rightside.append($export);
@ -494,44 +506,40 @@ define([
} }
$rightside.append($leavePresent); $rightside.append($leavePresent);
var configureTheme = function () { var configureTheme = function () {
/*var $language = $('<span>', {
'style': "margin-right: 10px;",
'class': 'rightside-element'
}).text("Markdown");
$rightside.append($language);*/
/* Remember the user's last choice of theme using localStorage */ /* Remember the user's last choice of theme using localStorage */
var themeKey = 'CRYPTPAD_CODE_THEME'; var themeKey = 'CRYPTPAD_CODE_THEME';
var lastTheme = localStorage.getItem(themeKey) || 'default'; var lastTheme = localStorage.getItem(themeKey) || 'default';
/* Let the user select different themes */ var options = [];
var $themeDropdown = $('<select>', { Themes.forEach(function (l) {
title: 'color theme', options.push({
id: 'display-theme', tag: 'a',
'class': 'rightside-element' attributes: {
}); 'data-value': l.name,
Themes.forEach(function (o) { 'href': '#',
$themeDropdown.append($('<option>', { },
selected: o.name === lastTheme, content: l.name // Pretty name of the language value
}).val(o.name).text(o.name)); });
}); });
var dropdownConfig = {
text: 'Theme', // Button initial text
$rightside.append($themeDropdown); options: options, // Entries displayed in the menu
left: true, // Open to the left of the button
var $theme = $bar.find('select#display-theme'); };
var $block = module.$theme = Cryptpad.createDropdown(dropdownConfig);
setTheme(lastTheme, $theme); var $button = $block.find('.buttonTitle');
$theme.on('change', function () { setTheme(lastTheme, $block);
var theme = $theme.val();
console.log("Setting theme to %s", theme); $block.find('a').click(function (e) {
setTheme(theme, $theme); var theme = $(this).attr('data-value');
// remember user choices setTheme(theme, $block);
$button.text($(this).text());
localStorage.setItem(themeKey, theme); localStorage.setItem(themeKey, theme);
}); });
$rightside.append($block);
}; };
var configureColors = function () { var configureColors = function () {
@ -590,15 +598,6 @@ define([
if (!window.location.hash || window.location.hash === '#') { if (!window.location.hash || window.location.hash === '#') {
Cryptpad.replaceHash(editHash); Cryptpad.replaceHash(editHash);
} }
Cryptpad.getPadTitle(function (err, title) {
if (err) {
console.log("Unable to get pad title");
console.error(err);
return;
}
updateTitle(title || defaultName);
});
}; };
var unnotify = module.unnotify = function () { var unnotify = module.unnotify = function () {
@ -767,7 +766,9 @@ define([
} }
Slide.update(remoteDoc); Slide.update(remoteDoc);
notify(); if (oldDoc !== remoteDoc) {
notify();
}
}; };
var onAbort = config.onAbort = function (info) { var onAbort = config.onAbort = function (info) {

@ -202,6 +202,31 @@ define([
}; };
var addEvent = function () { var addEvent = function () {
var icon_to;
$modal.mousemove(function (e) {
var $buttons = $modal.find('.button');
$buttons.show();
if (icon_to) { window.clearTimeout(icon_to); }
icon_to = window.setTimeout(function() {
$buttons.fadeOut();
}, 1000);
});
$modal.find('#button_exit').click(function (e) {
var ev = $.Event("keyup");
ev.which = 27;
$modal.trigger(ev);
});
$modal.find('#button_left').click(function (e) {
var ev = $.Event("keyup");
ev.which = 37;
$modal.trigger(ev);
});
$modal.find('#button_right').click(function (e) {
var ev = $.Event("keyup");
ev.which = 39;
$modal.trigger(ev);
});
$(ifrw).on('keyup', function (e) { $(ifrw).on('keyup', function (e) {
if (!Slide.shown) { return; } if (!Slide.shown) { return; }
switch(e.which) { switch(e.which) {

@ -25,6 +25,11 @@
display: none; display: none;
border-radius: 5px; border-radius: 5px;
} }
.register {
display: none;
}
@media (max-width: 1000px) { @media (max-width: 1000px) {
div.panel { width: 90%; } div.panel { width: 90%; }
} }
@ -49,6 +54,7 @@
<div id="warning" data-localization="login_warning"></div> <div id="warning" data-localization="login_warning"></div>
<input type="text" name="username" id="username" data-localization-placeholder="login_username" autofocus><br /> <input type="text" name="username" id="username" data-localization-placeholder="login_username" autofocus><br />
<input type="password" name="password" id="password" data-localization-placeholder="login_password"><br /> <input type="password" name="password" id="password" data-localization-placeholder="login_password"><br />
<input type="password" class="register" name="confirm" id="confirm_register" data-localization-placeholder="login_confirm"><br />
<button id="login" data-localization="login_login"></button> <button id="login" data-localization="login_login"></button>
<input type="checkbox" name="remember" id="remember" checked="true"><label for="remember" data-localization="login_remember"></label> <input type="checkbox" name="remember" id="remember" checked="true"><label for="remember" data-localization="login_remember"></label>
@ -83,6 +89,6 @@
<p><span data-localization="username_label"></span> <p><span data-localization="username_label"></span>
<strong id="user-name" class="display"></strong></p> <strong id="user-name" class="display"></strong></p>
<p>View your <a href="/file" target="_blank">file manager</a>.</p> <p>View your <a href="/drive" target="_blank">CryptDrive</a>.</p>
</div> </div>
</div> </div>

@ -23,8 +23,10 @@ define([
// login elements // login elements
var $loginBox = $('#login-panel'); var $loginBox = $('#login-panel');
var $login = $('#login'); var $login = $('#login');
var $login_register = $('#login_register');
var $username = $('#username'); var $username = $('#username');
var $password = $('#password'); var $password = $('#password');
var $password_register = $('#confirm_register');
var $remember = $('#remember'); var $remember = $('#remember');
// hashing elements // hashing elements
@ -61,7 +63,13 @@ define([
var revealConfirm = APP.revealConfirm = revealer($confirmBox); var revealConfirm = APP.revealConfirm = revealer($confirmBox);
var revealLogout = APP.revealLogout= revealer($logoutBox); var revealLogout = APP.revealLogout= revealer($logoutBox);
var revealUser = APP.revealUser = revealer($userBox); var revealUser_false = APP.revealUser_false = revealer($userBox);
var revealUser = APP.revealUser = function (state) {
if (!state) {
revealUser_false(state);
}
document.location.href = '/drive';
};
var getDisplayName = APP.getDisplayName = function (proxy) { var getDisplayName = APP.getDisplayName = function (proxy) {
return proxy['cryptpad.username']; return proxy['cryptpad.username'];
@ -186,9 +194,9 @@ define([
}; };
}; };
var handleNewUser = function (proxy, opt) { var handleNewUser = function (proxy, opt, force) {
// could not find a profile for that username/password // could not find a profile for that username/password
confirmPassword(proxy, opt.password, function () { var todo = function () {
APP.confirming = false; APP.confirming = false;
APP.setAccountName((proxy.login_name = opt.name)); APP.setAccountName((proxy.login_name = opt.name));
APP.setDisplayName(APP.getDisplayName(proxy)); APP.setDisplayName(APP.getDisplayName(proxy));
@ -219,17 +227,35 @@ define([
proxy[k] = map[k]; proxy[k] = map[k];
}); });
delete localStorage.FS_hash; var whenSynced = function () {
delete localStorage.FS_hash;
if (!proxy[USERNAME_KEY]) {
proxy[USERNAME_KEY] = opt.name; if (!proxy[USERNAME_KEY]) {
} proxy[USERNAME_KEY] = opt.name;
}
next(); next();
};
// Make sure the migration is synced in chainpad before continuing otherwise
// we may leave that page too early or trigger a reload in another tab before
// the migration is complete
var check = function () {
if (APP.realtime.getUserDoc() === APP.realtime.getAuthDoc()) {
whenSynced();
return;
}
window.setTimeout(check, 300);
};
check();
}); });
}); });
}); });
}); };
if (force) {
todo();
return;
}
confirmPassword(proxy, opt.password, todo);
}; };
var handleUser = function (proxy, opt) { var handleUser = function (proxy, opt) {
@ -237,6 +263,9 @@ define([
var now = opt.now = +(new Date()); var now = opt.now = +(new Date());
if (!proxyKeys.length) { if (!proxyKeys.length) {
if (opt.register) {
return handleNewUser(proxy, opt, true);
}
return handleNewUser(proxy, opt); return handleNewUser(proxy, opt);
} }
handleRegisteredUser(proxy, opt); handleRegisteredUser(proxy, opt);
@ -287,7 +316,6 @@ define([
}; };
var rt = APP.rt = Listmap.create(config); var rt = APP.rt = Listmap.create(config);
rt.proxy.on('create', function (info) { rt.proxy.on('create', function (info) {
APP.realtime = info.realtime; APP.realtime = info.realtime;
}) })
@ -310,6 +338,11 @@ define([
revealUser(true); revealUser(true);
} else { } else {
if (sessionStorage.register || document.location.hash.slice(1) === 'register') {
document.location.hash = 'register';
$login.text(Cryptpad.Messages.login_register);
$('#login-panel .register').show();
}
revealLogin(true); revealLogin(true);
} }
@ -318,9 +351,15 @@ define([
$login.click(function () { $login.click(function () {
var uname = $username.val().trim(); var uname = $username.val().trim();
var passwd = $password.val(); var passwd = $password.val();
var passwd_confirm = $password_register.val();
var confirm = $confirm.val(); var confirm = $confirm.val();
var remember = $remember[0].checked; var remember = $remember[0].checked;
var register = document.location.hash.slice(1) === 'register';
if (passwd !== passwd_confirm && register) {
return void Cryptpad.alert("Passwords are not the same");
}
if (!Cred.isValidUsername(uname)) { if (!Cred.isValidUsername(uname)) {
return void Cryptpad.alert('invalid username'); return void Cryptpad.alert('invalid username');
} }
@ -341,7 +380,7 @@ define([
window.setTimeout(function () { window.setTimeout(function () {
useBytes(bytes, { useBytes(bytes, {
remember: remember, remember: remember,
//register: register, register: register,
name: uname, name: uname,
password: passwd, password: passwd,
}); });
@ -350,5 +389,21 @@ define([
}, 75); }, 75);
}); });
}); });
if (sessionStorage.login) {
$username.val(sessionStorage.login_user);
$password.val(sessionStorage.login_pass);
$remember.attr('checked', sessionStorage.login_rmb === "true");
$login.click();
}
if (sessionStorage.register) {
$username.val(sessionStorage.login_user);
$password.val(sessionStorage.login_pass);
$remember.attr('checked', sessionStorage.login_rmb === "true");
}
['login', 'register', 'login_user', 'login_pass', 'login_rmb'].forEach(function (k) {
delete sessionStorage[k];
});
}); });
}); });

Loading…
Cancel
Save