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
.DS_Store
www/scratch
data

@ -6,9 +6,8 @@ env:
branches:
only:
- master
- diffdom
- beta
- netflux
- soon
- staging
node_js:
- "4.2.1"
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 */
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;
if (process.env.SAUCE_USERNAME !== undefined) {
var browserArray = process.env.BROWSER.split(':');
@ -19,6 +24,8 @@ if (process.env.SAUCE_USERNAME !== undefined) {
driver.get('http://localhost:3000/assert/');
var report = driver.wait(WebDriver.until.elementLocated(WebDriver.By.className("report")), 5000);
report.getAttribute("class").then(function (cls) {
report.getText().then(function (text) {
console.log("\n-----\n" + text + "\n-----");
driver.quit();
if (!cls) {
throw new Error("cls is null");
@ -28,3 +35,4 @@ report.getAttribute("class").then(function (cls) {
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",
"spin.js": "^2.3.2",
"scrypt-async": "^1.2.0",
"bootstrap": "^3.3.7"
"bootstrap": "#v4.0.0-alpha.6"
}
}

@ -17,7 +17,7 @@ module.exports = {
/*
httpHeaders: {
"Content-Security-Policy": [
"default-serc 'none'",
"default-src 'none'",
"style-src 'unsafe-inline' 'self'",
"script-src 'self' 'unsafe-eval' 'unsafe-inline'",
"child-src 'self' cryptpad.fr *.cryptpad.fr",
@ -50,6 +50,12 @@ module.exports = {
*/
//websocketPort: 3000,
/* if you want to run a different version of cryptpad but using the same websocket
* server, you should use the other server port as websocketPort and disable
* the websockets on that server
*/
//useExternalWebsocket: false,
/* If Cryptpad is proxied without using https, the server needs to know.
* Specify 'useSecureWebsockets: true' so that it can send
* Content Security Policy Headers that prevent http and https from mixing
@ -75,6 +81,7 @@ module.exports = {
'privacy',
'terms',
'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>
<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>
@ -13,18 +17,30 @@
});
</script>
</head>
<body>
<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>
<!-- Thanks! http://tholman.com/github-corners/ -->
<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>-->
<select id="language-selector" style="display: none;"></select>
<div id="main">
<center>
<a href="/"><img class="imgcenter cryptofist" src="/customize/cryptofist_small.png" /></a>
</center>
<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>
@ -41,18 +57,18 @@
<div id="main_other">
<center>
<h1>About</h1>
<h1 data-localization="about">About</h1>
</center>
<p data-localization="main_p2"><!-- CkEditor, CodeMirror, Chainpad --></p>
<h2 id="howitworks" data-localization="main_howitworks"></h2>
<p data-localization="main_howitworks_p1"><!-- Operational transform, Nakamoto blockchain, server kept unaware of the content--></p>
</div>
</div>
</body>
</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-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.amd.js',
'/file/fileObject.js'
'/common/fileObject.js'
], function (Config, Messages, Listmap, Crypto, TextPatcher, FO) {
/*
This module uses localStorage, which is synchronous, but exposes an
@ -39,6 +39,11 @@ define([
cb(void 0, map);
};
Store.setDrive = function (key, val, cb) {
storeObj.drive[key] = val;
cb();
};
var safeGet = window.safeGet = function (key) {
return storeObj[key];
};
@ -56,6 +61,10 @@ define([
cb(void 0, res);
};
Store.getDrive = function (key, cb) {
cb(void 0, storeObj.drive[key]);
};
var safeRemove = function (key) {
delete storeObj[key];
};
@ -98,6 +107,10 @@ define([
return exp;
};
Store.getLoginName = function () {
return storeObj.login_name;
};
var changeHandlers = Store.changeHandlers = [];
Store.change = function (f) {
@ -123,7 +136,7 @@ define([
};
var onReady = function (f, proxy, storageKey) {
filesOp = FO.init(proxy, {
filesOp = FO.init(proxy.drive, {
storageKey: storageKey
});
storeObj = proxy;
@ -138,7 +151,10 @@ define([
var init = function (f, Cryptpad) {
if (!Cryptpad || initialized) { return; }
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 listmapConfig = {
data: {},
@ -151,19 +167,34 @@ define([
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);
exp.proxy = rt.proxy;
rt.proxy.on('create', function (info) {
exp.info = info;
var realtime = info.realtime;
if (!Cryptpad.getUserHash()) {
localStorage.FS_hash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
}
}).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);
oldStore.get(Cryptpad.storageKey, function (err, s) {
rt.proxy[Cryptpad.storageKey] = s;
drive[Cryptpad.storageKey] = s;
onReady(f, rt.proxy, Cryptpad.storageKey);
});
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>
<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>
@ -13,18 +17,30 @@
});
</script>
</head>
<body>
<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>
<!-- Thanks! http://tholman.com/github-corners/ -->
<select id="language-selector" style="display: none;"></select>
<div id="main">
<center>
<a href="/"><img class="imgcenter cryptofist" src="/customize/cryptofist_small.png" /></a>
</center>
<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>
@ -41,50 +57,158 @@
<center>
<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>
<!--
(<a href="/file" target="_blank" data-localization="main_openFileManager">Open in a new tab</a>)
<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/ -->
<iframe src="/file/#iframe" id="fileManagerIframe"></iframe>
<br />
-->
<table class="recent scroll" style="display:none">
<tbody>
<tr>
<th data-localization="table_type"></th>
<th data-localization="table_link"></th>
<th data-localization="table_created"></th>
<th data-localization="table_last"></th>
<th></th>
</tr>
</tbody>
</table>
<div id="main">
<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>
<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>
<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>
<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>
</html>

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

@ -14,15 +14,16 @@
background: rgba(0, 0, 0, 0.8);
}
.alertify-logs > *.error {
background: #FF0073;
background: #FA5858;
}
.alertify-logs > *.success {
background: #46E981;
color: #302B28;
color: #fff;
}
.alertify {
position: fixed;
background-color: rgba(0, 0, 0, 0.3);
color: #fafafa;
left: 0;
right: 0;
top: 0;
@ -79,8 +80,8 @@
}
.alertify .dialog input:not(.form-control),
.alertify .alert input:not(.form-control) {
background-color: #302B28;
color: #fafafa;
background-color: #fff;
color: #555;
border: 0px;
border-radius: 5px;
margin-bottom: 15px;
@ -210,7 +211,7 @@
height: 2.5em;
display: inline-block;
width: 100%;
background: #302B28;
background: #fff;
border-top: 1px solid #444;
}
.top-bar a,
@ -226,7 +227,7 @@
display: block;
margin-left: 10px;
padding-top: 3px;
color: #fafafa;
color: #555;
}
.top-bar img,
.bottom-bar img {
@ -306,6 +307,7 @@
left: 0px;
right: 0px;
background: #302B28;
color: #fafafa;
text-align: center;
font-size: 1.5em;
}
@ -329,19 +331,153 @@
.cp #loading .spinnerContainer > div {
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,
.cp body {
font-size: .875em;
background-color: #302B28;
color: #fafafa;
background-color: #fff;
color: #555;
font-family: Georgia,Cambria,serif;
height: 100;
height: 100%;
}
.cp:not(.poll) #language-selector {
.cp:not(.poll) {
/*#language-selector {
position: absolute;
top: 0px;
top: @topbar-height;
right: 0px;
display: inline-block;
z-index: 2;
}*/
}
.cp {
/* buttons */
@ -353,6 +489,7 @@ html.cp,
font-size: 1rem;
font-weight: 400;
line-height: 2rem;
margin: 0;
}
.cp a.github-corner > svg {
fill: #00ADEE;
@ -368,7 +505,7 @@ html.cp,
.cp h4,
.cp h5,
.cp h6 {
color: #fafafa;
color: #555;
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
-webkit-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 {
cursor: pointer;
color: #46E981;
color: #3333ff;
text-decoration: none;
}
.cp a:hover {
color: #a1f4bf;
color: #0000cc;
}
.cp img {
height: auto;
@ -441,28 +578,311 @@ html.cp,
font-family: lato, Helvetica, sans-serif;
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 {
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;
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 {
width: 100%;
height: 500px;
margin-top: 15px;
}
.cp .buttons {
margin-bottom: 50px;
margin-top: 20px;
line-height: 2.5em;
}
.cp .create,
.cp .action {
display: inline-block;
border: 2px solid #46E981;
background-color: #302B28;
color: #46E981;
border: 0;
background-color: #3333ff;
color: #fff;
font-weight: bold;
font-size: large;
margin-right: 5px;
@ -470,18 +890,16 @@ html.cp,
}
.cp .create:hover,
.cp .action:hover {
border: 2px solid #a1f4bf;
color: #46E981;
color: #cccccc;
}
.cp .button {
padding: 4px 12px 4px 12px;
margin-top: 12px;
margin-bottom: 12px;
margin: 2px 0;
display: inline-block;
line-height: 1.5em;
}
.cp .panel {
background-color: #333;
background-color: #cccccc;
}
.cp table {
border-collapse: collapse;
@ -489,7 +907,7 @@ html.cp,
margin: 20px;
}
.cp tbody {
border: 2px solid black;
border: 1px solid #555;
}
.cp tbody tr {
text-align: center;
@ -507,18 +925,18 @@ html.cp,
cursor: pointer;
}
.cp tbody tr:nth-child(odd) {
background-color: #685d56;
background-color: #ffffff;
}
.cp tbody tr th:first-of-type {
border-left: 0px;
}
.cp tbody tr th {
box-sizing: border-box;
border: 1px solid black;
border: 1px solid #555;
}
.cp tbody tr th,
.cp tbody tr td {
color: #fafafa;
color: #555;
}
.cp tbody tr th.remove,
.cp tbody tr td.remove {
@ -528,7 +946,7 @@ html.cp,
border-right: 0px;
}
.cp tbody td {
border-right: 1px solid black;
border-right: 1px solid #555;
padding: 12px;
padding-top: 0px;
padding-bottom: 0px;
@ -543,7 +961,7 @@ html.cp,
border-top-left-radius: 5px;
}
.cp .remove {
color: #FF0073;
color: #FA5858;
cursor: pointer !important;
}
.cp form.realtime,
@ -589,7 +1007,7 @@ html.cp,
.cp form.realtime table tr td div.text-cell input[disabled],
.cp div.realtime table tr td div.text-cell input[disabled] {
background-color: transparent;
color: #fafafa;
color: #000;
font-weight: bold;
}
.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 div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover {
font-weight: bold;
background-color: #FF0073;
color: #302B28;
background-color: #FA5858;
color: #000;
display: block;
}
.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 div.realtime table input[type="text"] {
height: auto;
border: 1px solid #302B28;
border: 1px solid #fff;
width: 80%;
}
.cp form.realtime table thead td,
.cp div.realtime table thead td {
padding: 0px 5px;
background: #4c443f;
background: #aaa;
border-radius: 20px 20px 0 0;
text-align: center;
}
@ -671,13 +1089,13 @@ html.cp,
}
.cp form.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;
border: none;
}
.cp form.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 div.realtime table tbody .text-cell input[type="text"] {
@ -693,6 +1111,10 @@ html.cp,
float: left;
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 div.realtime table .edit {
color: #46E981;
@ -710,7 +1132,7 @@ html.cp,
.cp form.realtime table thead tr th input[type="text"][disabled],
.cp div.realtime table thead tr th input[type="text"][disabled] {
background-color: transparent;
color: #fafafa;
color: #555;
font-weight: bold;
}
.cp form.realtime table thead tr th .remove,
@ -750,6 +1172,29 @@ html.cp,
.cp div.realtime #addoption {
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 ul,
.cp.slide #modal #content ol {
@ -773,7 +1218,7 @@ html.cp,
width: 100%;
height: 100vh;
display: none;
background-color: #302B28;
background-color: #000;
}
.cp div.modal #content,
.cp div#modal #content {
@ -840,6 +1285,20 @@ html.cp,
font-size: 1.6vw;
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 {
display: block;
@ -864,7 +1323,7 @@ html.cp,
width: 80%;
height: 80%;
margin: auto;
border: 1px solid #685d56;
border: 1px solid #ffffff;
text-align: center;
}
.cp div.modal.shown,
@ -880,7 +1339,7 @@ html.cp,
.cp div#modal table input {
height: 100%;
width: 90%;
border: 3px solid #302B28;
border: 3px solid #fff;
}
.cp div.modal table tfoot tr td,
.cp div#modal table tfoot tr td {

@ -33,22 +33,21 @@ define([
var displayCreateButtons = function () {
var $parent = $('#buttons');
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');
Cryptpad.createLanguageSelector(undefined, $sel);
$sel.find('button').addClass('btn').addClass('btn-secondary');
$sel.show();
Object.keys(Messages._languages).forEach(function (code) {
$sel.append($('<option>', {
value: code,
}).text(Messages._languages[code]));
$(window).click(function () {
$sel.find('.cryptpad-dropdown').hide();
});
LS.main();
Messages._applyTranslation();
$sel.show();
var makeRecentPadsTable = function (recentPads) {
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();
Cryptpad.ready(function () {
//Cryptpad.ready(function () {
console.log("ready");
if (Cryptpad.isLoggedIn()) {
document.location.href = '/drive';
}
addButtonHandlers();
$table = $('table.scroll');
$tbody = $table.find('tbody');
$tryit = $('#tryit');
Cryptpad.styleAlerts();
refreshTable();
//refreshTable();
/*
if (Cryptpad.store && Cryptpad.store.change) {
Cryptpad.store.change(function (data) {
if (data.key === 'CryptPad_RECENTPADS') {
@ -176,6 +222,7 @@ define([
}
});
}
});
*/
//});
});

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

@ -3,8 +3,12 @@
<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>
@ -13,18 +17,30 @@
});
</script>
</head>
<body>
<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>
<!-- Thanks! http://tholman.com/github-corners/ -->
<select id="language-selector" style="display: none;"></select>
<div id="main">
<center>
<a href="/"><img class="imgcenter cryptofist" src="/customize/cryptofist_small.png" /></a>
</center>
<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>
@ -41,6 +57,7 @@
<div id="main_other">
<center>
<h1 data-localization="policy_title"></h1>
</center>
@ -67,11 +84,10 @@
<p data-localization="policy_choices_ads"></p>
<br />
</div>
</div>
</body>
</html>

@ -24,7 +24,10 @@
onload(void 0, iframe, e);
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);
};

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

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

@ -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/ -->

@ -1,42 +1,148 @@
<center>
<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>
{{fork}}
<div id="main">
<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>
<!--
(<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>
<div id="buttons" class="buttons">
<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>
<tr>
<th data-localization="table_type"></th>
<th data-localization="table_link"></th>
<th data-localization="table_created"></th>
<th data-localization="table_last"></th>
<th></th>
</tr>
</div>
</tbody>
</table>
<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>
<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>
<!--<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>

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

@ -1,3 +1,4 @@
<div id="main_other">
<center>
<h1 data-localization="tos_title"></h1>
</center>
@ -7,4 +8,5 @@
<p data-localization="tos_e2ee"></p>
<p data-localization="tos_logs"></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 {
> * {
padding: @padding-base @padding-base * 4;
color: @fore;
color: @alertify-fore;
font-weight: bold;
font-size: large;
@ -30,6 +30,7 @@
.alertify {
position: fixed;
background-color: @alertify-bg;
color: @alertify-fg;
left: 0;
right: 0;
top: 0;
@ -128,7 +129,7 @@
cursor: pointer;
color: @alertify-btn-fg;
border: 1px solid @base;
border: 1px solid @alertify-base;
border-radius: 5px;
&:hover, &:active {
@ -136,7 +137,7 @@
}
&:focus {
border: 1px dotted @base;
border: 1px dotted @alertify-base;
}
&::-moz-focus-inner {
border:0;

@ -4,6 +4,8 @@
@import "./alertify.less";
@import "./bar.less";
@import "./loading.less";
@import "./dropdown.less";
@import "./topbar.less";
html.cp, .cp body {
font-size: .875em;
@ -11,16 +13,17 @@ html.cp, .cp body {
color: @fore;
font-family: Georgia,Cambria,serif;
height: 100;
height: 100%;
}
.cp:not(.poll) {
#language-selector {
/*#language-selector {
position: absolute;
top: 0px;
top: @topbar-height;
right: 0px;
display: inline-block;
}
z-index: 2;
}*/
}
.cp {
@ -29,11 +32,12 @@ body {
font-size: 1rem;
font-weight: 400;
line-height: 2rem;
margin: 0;
}
a.github-corner > svg {
fill: @cp-blue;
color: @base;
color: @old-base;
}
.lato {
@ -96,11 +100,11 @@ h6 {
a {
cursor: pointer;
color: @cp-green;
color: @cp-darkblue;
text-decoration: none;
&:hover {
color: @cp-accent;
color: @cp-accent2;
}
}
@ -122,12 +126,298 @@ p, pre, td, a, table, tr {
.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 {
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-top: 100px;
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 {
@ -138,27 +428,22 @@ p, pre, td, a, table, tr {
/* buttons */
// home page button container
.buttons {
margin-bottom: 50px;
margin-top: 20px;
line-height: 2.5em;
}
.create, .action {
display: inline-block;
@thick: 2px;
border: @thick solid @cp-green;
background-color: @base;
color: @cp-green;
//border: @thick solid @cp-darkblue;
border: 0;
background-color: @cp-darkblue;
color: @topbar-button-color;
font-weight: bold;
font-size: large;
margin-right: 5px;
margin-left: 5px;
&:hover {
border: @thick solid @cp-accent;
color: @cp-green;
color: darken(@topbar-button-color, 20%);
//border: @thick solid @cp-accent2;
//color: @cp-darkblue;
}
}
@ -167,15 +452,16 @@ p, pre, td, a, table, tr {
@vpad: 2 * 2px;
padding: @vpad @hpad @vpad @hpad;
margin-top: 2 * 6px;
margin-bottom: 2 * 6px;
//margin-top: 2 * 6px;
//margin-bottom: 2 * 6px;
margin: 2px 0;
display: inline-block;
line-height: 1.5em;
}
// currently only used in /user/
.panel {
background-color: #333;
background-color: @dark-base;
}
/* Tables
@ -187,7 +473,7 @@ table {
margin: 20px;
}
tbody {
border: 2px solid black;
border: 1px solid @poll-border-color;
tr {
text-align: center;
&:first-of-type th{
@ -211,7 +497,7 @@ tbody {
}
th {
box-sizing: border-box;
border: 1px solid black;
border: 1px solid @poll-border-color;
}
th, td {
color: @fore;
@ -226,7 +512,7 @@ tbody {
}
td {
border-right: 1px solid black;
border-right: 1px solid @poll-border-color;
padding: 12px;
padding-top: 0px;
padding-bottom: 0px;
@ -291,7 +577,7 @@ form.realtime, div.realtime {
border: 0px;
&[disabled] {
background-color: transparent;
color: @fore;
color: @poll-fg;
font-weight: bold;
}
}
@ -329,7 +615,7 @@ form.realtime, div.realtime {
font-weight: bold;
background-color: @cp-red;
color: @base;
color: @poll-cover-color;
&:after {
height: 100%;
@ -370,7 +656,7 @@ form.realtime, div.realtime {
thead {
td {
padding: 0px 5px;
background: @less-light-base;
background: @poll-th-bg;
border-radius: 20px 20px 0 0;
text-align: center;
input {
@ -378,7 +664,7 @@ form.realtime, div.realtime {
width: 100%;
box-sizing: border-box;
&[disabled] {
color: white;
color: @poll-fg;
padding: 1px 5px;
border: none;
}
@ -389,7 +675,7 @@ form.realtime, div.realtime {
tbody {
.text-cell {
background: @less-light-base;
background: @poll-td-bg;
//border-radius: 20px 0 0 20px;
input[type="text"] {
width: ~"calc(100% - 50px)";
@ -403,6 +689,11 @@ form.realtime, div.realtime {
margin: 0 0 0 10px;
}
}
td {
label {
border: .5px solid @poll-border-color;
}
}
}
.edit {
color: @cp-green;
@ -477,6 +768,31 @@ form.realtime, div.realtime {
}
&.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 {
p, ul, ol { font-size: 26px; }
@ -531,6 +847,10 @@ div.modal, div#modal {
h5 { .size(2.2); }
h6 { .size(1.6); }
h1, h2, h3, h4, h5, h6 {
color: inherit;
}
pre > code {
display: block;
position: relative;
@ -559,7 +879,7 @@ div.modal, div#modal {
height: 100vh;
display: none;
background-color: @base;
background-color: @slide-default-bg;
.center {
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;
right: 0px;
background: @bg-loading;
color: @color-loading;
text-align: center;
font-size: 1.5em;
.loadingContainer {

@ -1,6 +1,8 @@
@import "./variables.less";
@import "./mixins.less";
@import "./dropdown.less";
.unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
@ -24,11 +26,12 @@
box-sizing: border-box;
padding: 0px 6px;
.fa {font-family: FontAwesome;}
.unselectable;
font: normal normal normal 12px Arial,Helvetica,Tahoma,Verdana,Sans-Serif;
color: #000;
display: inline-block;
width: 100%;
z-index: 9001;
@ -38,12 +41,11 @@
.cryptpad-lag {
box-sizing: content-box;
vertical-align: top;
height: 16px;
width: 16px;
display: inline-block;
margin: 2px 0px;
padding: 5px;
margin: 3px 0;
div {
margin: auto;
}
@ -62,7 +64,12 @@
height: 26px;
padding-right: 5px;
padding-left: 5px;
margin: 2px;
margin: 3px 2px;
}
.dropdown-bar-content {
margin-top: -3px;
margin-right: 2px;
}
button {
@ -74,14 +81,17 @@
&:hover {
background-image:linear-gradient(to bottom,#f2f2f2,#ccc);
}
&.userlist {
}
.large {
@media screen and (max-width: 800px) {
display: none;
}
@media screen and (min-width: 801px) {
display: inline-block;
}
&.small {
}
.small {
@media screen and (max-width: 800px) {
display: inline-block;
}
@ -89,12 +99,9 @@
display: none;
}
}
}
}
.cryptpad-state {
line-height: 30px; /* equivalent to 26px + 2*2px margin used for buttons */
float: left;
line-height: 32px; /* equivalent to 26px + 2*2px margin used for buttons */
}
.rightside-button {
@ -244,6 +251,11 @@
vertical-align: top;
display: inline-block;
}
button {
span.fa {
vertical-align: baseline;
}
}
@media screen and (max-width: @media-narrow-screen) {
top: 3em;
}
@ -258,72 +270,52 @@
.cryptpad-user-list {
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 {
margin: 2px 4px 2px 0px;
}
.cryptpad-userbuttons-container {
/*display: none;*/
}
}
.cryptpad-toolbar-rightside {
text-align: right;
}
.cryptpad-spinner {
display: inline-block;
height: 26px;
margin: 2px;
line-height: 26px;
font-size: 20px;
height: 16px;
width: 16px;
margin: 8px;
line-height: 16px;
font-size: 16px;
}
.cryptpad-readonly {
margin-right: 5px;
font-weight: bold;
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 {
height: 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%);
@less-light-base: lighten(@base, 10%);
@fore: #fafafa;
@fore: #555;
@old-base: #302B28;
@old-fore: #fafafa;
@cp-green: #46E981;
@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-orange: #FE9A2E;
@ -27,20 +32,33 @@
@text-color: rgba(0, 0, 0, .8);
@border-radius: 1px;
@alertify-fore: @old-fore;
@alertify-base: @old-base;
@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-hover: rgba(200, 200, 200, .15);
@alertify-bg: rgba(0, 0, 0, .3);
@alertify-fg: @old-fore;
@alertify-input-bg: @base;
@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-small: 801px;
@ -51,3 +69,15 @@
@toolbar-gradient-start: #f5f5f5;
@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>
<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>
@ -13,18 +17,12 @@
});
</script>
</head>
<body>
{{fork}}
<select id="language-selector" style="display: none;"></select>
<div id="main">
{{logo}}
<body class="html">
{{topbar}}
{{noscript}}
{{main}}
</div>
</body>
</html>

@ -3,8 +3,12 @@
<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>
@ -13,18 +17,30 @@
});
</script>
</head>
<body>
<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>
<!-- Thanks! http://tholman.com/github-corners/ -->
<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>-->
<select id="language-selector" style="display: none;"></select>
<div id="main">
<center>
<a href="/"><img class="imgcenter cryptofist" src="/customize/cryptofist_small.png" /></a>
</center>
<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>
@ -41,6 +57,7 @@
<div id="main_other">
<center>
<h1 data-localization="tos_title"></h1>
</center>
@ -50,9 +67,9 @@
<p data-localization="tos_e2ee"></p>
<p data-localization="tos_logs"></p>
<p data-localization="tos_3rdparties"></p>
</div>
</div>
</body>
</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 {
-webkit-touch-callout: none;
-webkit-user-select: none;
@ -31,21 +103,22 @@
user-select: none;
font: normal normal normal 12px Arial, Helvetica, Tahoma, Verdana, Sans-Serif;
color: #000;
display: inline-block;
width: 100%;
z-index: 9001;
}
.cryptpad-toolbar .fa {
font-family: FontAwesome;
}
.cryptpad-toolbar a {
float: right;
}
.cryptpad-toolbar .cryptpad-lag {
box-sizing: content-box;
vertical-align: top;
height: 16px;
width: 16px;
display: inline-block;
margin: 2px 0px;
padding: 5px;
margin: 3px 0;
}
.cryptpad-toolbar .cryptpad-lag div {
margin: auto;
@ -65,7 +138,11 @@
height: 26px;
padding-right: 5px;
padding-left: 5px;
margin: 2px;
margin: 3px 2px;
}
.cryptpad-toolbar .dropdown-bar-content {
margin-top: -3px;
margin-right: 2px;
}
.cryptpad-toolbar button {
background-color: inherit;
@ -78,29 +155,28 @@
background-image: linear-gradient(to bottom, #f2f2f2, #ccc);
}
@media screen and (max-width: 800px) {
.cryptpad-toolbar button.userlist {
.cryptpad-toolbar .large {
display: none;
}
}
@media screen and (min-width: 801px) {
.cryptpad-toolbar button.userlist {
.cryptpad-toolbar .large {
display: inline-block;
}
}
@media screen and (max-width: 800px) {
.cryptpad-toolbar button.userlist.small {
.cryptpad-toolbar .small {
display: inline-block;
}
}
@media screen and (min-width: 801px) {
.cryptpad-toolbar button.userlist.small {
.cryptpad-toolbar .small {
display: none;
}
}
.cryptpad-toolbar .cryptpad-state {
line-height: 30px;
line-height: 32px;
/* equivalent to 26px + 2*2px margin used for buttons */
float: left;
}
.cryptpad-toolbar .rightside-button {
float: right;
@ -249,6 +325,9 @@
vertical-align: top;
display: inline-block;
}
.cryptpad-toolbar-top .cryptpad-user button span.fa {
vertical-align: baseline;
}
@media screen and (max-width: 400px) {
.cryptpad-toolbar-top .cryptpad-user {
top: 3em;
@ -266,71 +345,42 @@
.cryptpad-toolbar-leftside .cryptpad-user-list {
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 {
margin: 2px 4px 2px 0px;
}
.cryptpad-toolbar-leftside .cryptpad-userbuttons-container {
/*display: none;*/
}
.cryptpad-toolbar-rightside {
text-align: right;
}
.cryptpad-spinner {
display: inline-block;
height: 26px;
margin: 2px;
line-height: 26px;
font-size: 20px;
height: 16px;
width: 16px;
margin: 8px;
line-height: 16px;
font-size: 16px;
}
.cryptpad-readonly {
margin-right: 5px;
font-weight: bold;
text-transform: uppercase;
}
.cryptpad-user p.accountData {
background: #f0f0f0;
}
.cryptpad-user p.accountData > span {
font-weight: bold;
}
.cryptpad-user p.accountData > span span {
font-weight: normal;
}
.cryptpad-user .buttonTitle .fa:not(.fa-caret-down) {
margin-right: 5px;
}
.cryptpad-user .buttonTitle .account-name {
margin-left: 5px;
}
.cryptpad-dropdown-share a .fa {
margin-right: 5px;
}
.lag {
height: 15px !important;
width: 15px !important;
@ -341,7 +391,7 @@
background-color: #46E981;
}
.lag-red {
background-color: #FF0073;
background-color: #FA5858;
}
.lag-orange {
background-color: #FE9A2E;

@ -1,13 +1,15 @@
define(function () {
var out = {};
// translations must set this key for their language to be available in
// the language dropdowns that are shown throughout Cryptpad's interface
out._languageName = "Français";
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.type = {};
out.type.pad = 'Pad';
out.type.pad = 'Texte';
out.type.code = 'Code';
out.type.poll = 'Sondage';
out.type.slide = 'Présentation';
@ -42,6 +44,8 @@ define(function () {
out.editor = "éditeur";
out.editors = "éditeurs";
out.language = "Langue";
out.greenLight = "Tout fonctionne bien";
out.orangeLight = "Votre connexion est lente, ce qui réduit la qualité de l'éditeur";
out.redLight = "Vous êtes déconnectés de la session";
@ -59,6 +63,9 @@ define(function () {
out.userButton = 'UTILISATEUR';
out.userButtonTitle = "Changer votre nom d'utilisateur";
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.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.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.presentButtonTitle = "Entrer en 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.okButton = 'OK (Entrée)';
out.cancel = "Annuler";
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' +
'<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";
@ -170,12 +182,14 @@ define(function () {
// File manager
out.fm_rootName = "Mes documents";
out.fm_rootName = "Documents";
out.fm_trashName = "Corbeille";
out.fm_unsortedName = "Fichiers non triés";
out.fm_filesDataName = "Tous les fichiers";
out.fm_templateName = "Modèles";
out.fm_newButton = "Nouveau";
out.fm_newFolder = "Nouveau dossier";
out.fm_newFolderButton = "NOUVEAU DOSSIER";
out.fm_folder = "Dossier";
out.fm_folderName = "Nom du dossier";
out.fm_numberOfFolders = "# de dossiers";
out.fm_numberOfFiles = "# de fichiers";
@ -185,9 +199,10 @@ define(function () {
out.fm_creation = "Création";
out.fm_forbidden = "Action interdite";
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_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_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 ?";
@ -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_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_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_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
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_rename = "Renommer";
out.fc_open = "Ouvrir";
out.fc_open_ro = "Ouvrir (lecture seule)";
out.fc_delete = "Supprimer";
out.fc_restore = "Restaurer";
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_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
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_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.';
@ -234,10 +294,19 @@ define(function () {
out.table_created = 'Créé le';
out.table_last = 'Dernier accès';
out.button_newpad = 'CRÉER UN PAD WYSIWYG';
out.button_newcode = 'CRÉER UN PAD DE CODE';
out.button_newpoll = 'CRÉER UN SONDAGE';
out.button_newslide = 'CRÉER UNE PRÉSENTATION';
out.button_newpad = 'Nouveau document texte';
out.button_newcode = 'Nouvelle page de code';
out.button_newpoll = 'Nouveau sondage';
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

@ -9,7 +9,7 @@ define(function () {
out.main_slogan = "Unity is Strength - Collaboration is Key";
out.type = {};
out.type.pad = 'Pad';
out.type.pad = 'Rich text';
out.type.code = 'Code';
out.type.poll = 'Poll';
out.type.slide = 'Presentation';
@ -44,6 +44,8 @@ define(function () {
out.editor = "editor";
out.editors = "editors";
out.language = "Language";
out.greenLight = "Everything is working fine";
out.orangeLight = "Your slow connection may impact your experience";
out.redLight = "You are disconnected from the session";
@ -61,6 +63,9 @@ define(function () {
out.userButton = 'USER';
out.userButtonTitle = 'Change your username';
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.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.shareFailed = "Failed to copy URL to clipboard";
out.newPadButton = 'New';
out.newPadButtonTitle = 'Create a new document';
out.presentButton = 'PRESENT';
out.presentButtonTitle = "Enter presentation mode";
out.presentSuccess = 'Hit ESC to exit presentation mode';
@ -114,7 +122,7 @@ define(function () {
out.okButton = 'OK (enter)';
out.cancel = "cancel";
out.cancel = "Cancel";
out.cancelButton = 'Cancel (esc)';
out.forget = "Forget";
@ -171,13 +179,14 @@ define(function () {
// File manager
out.fm_rootName = "My documents";
out.fm_rootName = "Documents";
out.fm_trashName = "Trash";
out.fm_unsortedName = "Unsorted files";
out.fm_filesDataName = "All files";
out.fm_templateName = "Templates";
out.fm_newButton = "New";
out.fm_newFolder = "New folder";
out.fm_newFolderButton = "NEW FOLDER";
out.fm_folder = "Folder";
out.fm_folderName = "Folder name";
out.fm_numberOfFolders = "# of folders";
out.fm_numberOfFiles = "# of files";
@ -187,9 +196,10 @@ define(function () {
out.fm_creation = "Creation";
out.fm_forbidden = "Forbidden action";
out.fm_originalPath = "Original path";
out.fm_noname = "Untitled Document";
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_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_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?";
@ -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_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_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_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
out.fc_newfolder = "New folder";
out.fc_rename = "Rename";
out.fc_open = "Open";
out.fc_open_ro = "Open (read-only)";
out.fc_delete = "Delete";
out.fc_restore = "Restore";
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.";
// login
out.login_login = "log in";
out.login_register = "register";
out.logoutButton = "log out";
out.login_login = "Log in";
out.login_nologin = "Your browser's recent pads";
out.login_register = "Sign up";
out.logoutButton = "Log out";
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_password = "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_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_warning = [
@ -248,11 +266,6 @@ define(function () {
'<p><strong>Make sure you do not forget your username and password!</strong></p>',
].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_no_user = "There is no user associated with the username and password that you entered.";
@ -263,7 +276,10 @@ define(function () {
// 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_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.';
@ -277,10 +293,19 @@ define(function () {
out.table_created = 'Created';
out.table_last = 'Last Accessed';
out.button_newpad = 'NEW RICH TEXT PAD';
out.button_newcode = 'NEW CODE PAD';
out.button_newpoll = 'NEW POLL';
out.button_newslide = 'NEW PRESENTATION';
out.button_newpad = 'New Rich Text pad';
out.button_newcode = 'New Code pad';
out.button_newpoll = 'New Poll';
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

@ -74,7 +74,7 @@ define(function () {
out.forgetButton = 'ESQUECER';
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.shareButtonTitle = "Copiar endereço do clipboard";
@ -181,7 +181,7 @@ define(function () {
out.table_type = 'Tipo';
out.table_link = 'Link';
out.table_created = 'Criado';
out.table_last = 'Último acessado';
out.table_last = 'Último acesso';
out.button_newpad = 'NOVO BLOCO WYSIWYG';
out.button_newcode = 'NOVO BLOCO DE NOTAS';
@ -192,7 +192,7 @@ define(function () {
out.policy_title = 'Política de privacidade do Cryptpad';
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_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';
@ -203,17 +203,17 @@ define(function () {
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_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_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>.';
// terms.html
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_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_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.";

@ -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.
If you use Mac, you can `brew install chromedriver`.
# Setup using Docker
See [Cryptpad-Docker](cryptpad-docker.md)
## Security
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 WebSocketServer = require('ws').Server;
var NetfluxSrv = require('./NetfluxWebsocketSrv');
var WebRTCSrv = require('./WebRTCSrv');
var config = require('./config');
var websocketPort = config.websocketPort || config.httpPort;
var useSecureWebsockets = config.useSecureWebsockets || false;
// support multiple storage back ends
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
// 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$');
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]+/, '');
res.setHeader('Content-Type', 'text/javascript');
res.send('define(' + JSON.stringify({
websocketPath: config.websocketPath,
websocketURL:'ws' + ((httpsOpts) ? 's' : '') + '://' + host + ':' +
websocketPath: config.useExternalWebsocket ? undefined : config.websocketPath,
websocketURL:'ws' + ((useSecureWebsockets) ? 's' : '') + '://' + host + ':' +
websocketPort + '/cryptpad_websocket',
}) + ');');
});
@ -95,6 +95,8 @@ httpServer.listen(config.httpPort,config.httpAddress,function(){
});
var wsConfig = { server: httpServer };
if(!config.useExternalWebsocket) {
if (websocketPort !== config.httpPort) {
console.log("setting up a new websocket server");
wsConfig = { port: websocketPort};
@ -102,5 +104,5 @@ if (websocketPort !== config.httpPort) {
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);
});
return false;
// No, this is crappy, it's going to cause tests to fail basically all of the time.
//return false;
}
return true;
}, "expected all translation keys in default language to be present in all translations. See console for details.");

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html class="cp code">
<head>
<title>CryptPad</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script data-main="main" src="/bower_components/requirejs/require.js"></script>
<script>
@ -46,7 +47,10 @@
</head>
<body>
<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>
</body>
</html>

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

@ -80,7 +80,7 @@ define([
var logout = common.logout = function (cb) {
[
fileHashKey,
// fileHashKey,
userHashKey,
].forEach(function (k) {
sessionStorage.removeItem(k);
@ -88,6 +88,11 @@ define([
delete localStorage[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(); }
};
@ -101,6 +106,11 @@ define([
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 = common.isArray = $.isArray;
@ -171,15 +181,25 @@ define([
var getHashFromKeys = common.getHashFromKeys = getEditHashFromKeys;
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 secret = {};
var generate = function () {
secret.keys = Crypto.createEditCryptor();
secret.key = Crypto.createEditCryptor().editKeyStr;
};
if (/#\?path=/.test(window.location.href)) {
var arr = window.location.hash.match(/\?path=(.+)/);
common.initialPath = arr[1] || undefined;
// If we have a hash in the URL specifying a path, it means the document was created from
// the drive and should be stored at the selected path.
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 = '';
}
if (!secretHash && !/#/.test(window.location.href)) {
@ -389,7 +409,7 @@ define([
// STORAGE
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);
});
};
@ -404,7 +424,7 @@ define([
// STORAGE
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);
});
};
@ -436,7 +456,7 @@ define([
// STORAGE
/* fetch and migrate your pad history from localStorage */
var getRecentPads = common.getRecentPads = function (cb, legacy) {
getStore(legacy).get(storageKey, function (err, recentPads) {
getStore(legacy).getDrive(storageKey, function (err, recentPads) {
if (isArray(recentPads)) {
cb(void 0, migrateRecentPads(recentPads));
return;
@ -448,7 +468,7 @@ define([
// STORAGE
/* commit a list of pads to localStorage */
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);
});
};
@ -560,7 +580,7 @@ define([
var data = makePad(href, name);
renamed.push(data);
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() {
// 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"
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) {
var $iframe = $('#pad-iframe');
var iframe = $iframe[0];
@ -686,8 +725,8 @@ define([
$loading.append($container);
$('body').append($loading);
};
common.removeLoadingScreen = function () {
$('#' + LOADING).fadeOut(750);
common.removeLoadingScreen = function (cb) {
$('#' + LOADING).fadeOut(750, cb);
};
common.errorLoadingScreen = function (error) {
$('.spinnerContainer').hide();
@ -842,9 +881,9 @@ define([
}
break;
case 'editshare':
button = $('<button>', {
button = $('<a>', {
title: Messages.editShareTitle,
}).text(Messages.editShare);
}).html('<span class="fa fa-users" style="font-family:FontAwesome;"></span>').append(' ' + Messages.editShare);
if (data && data.editHash) {
var editHash = data.editHash;
button.click(function () {
@ -860,9 +899,9 @@ define([
}
break;
case 'viewshare':
button = $('<button>', {
button = $('<a>', {
title: Messages.viewShareTitle,
}).text(Messages.viewShare);
}).html('<span class="fa fa-eye" style="font-family:FontAwesome;"></span>').append(' ' + Messages.viewShare);
if (data && data.viewHash) {
button.click(function () {
var baseUrl = window.location.origin + window.location.pathname + '#';
@ -877,9 +916,9 @@ define([
}
break;
case 'viewopen':
button = $('<button>', {
button = $('<a>', {
title: Messages.viewOpenTitle,
}).text(Messages.viewOpen);
}).html('<span class="fa fa-eye" style="font-family:FontAwesome;"></span>').append(' ' + Messages.viewOpen);
if (data && data.viewHash) {
button.click(function () {
var baseUrl = window.location.origin + window.location.pathname + '#';
@ -915,6 +954,99 @@ define([
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
*/

@ -13,7 +13,7 @@ define([
var NEW_FOLDER_NAME = Messages.fm_newFolder;
var init = module.init = function (files, config) {
FILES_DATA = config.storageKey;
FILES_DATA = config.storageKey || FILES_DATA;
var DEBUG = config.DEBUG || false;
var logging = function () {
console.log.apply(console, arguments);
@ -58,6 +58,10 @@ define([
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) {
return typeof(element) === "string";
};
@ -241,6 +245,17 @@ define([
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 () {
// Nothing in FILES_DATA for workgroups
if (workgroup) { return; }
@ -264,6 +279,7 @@ define([
if (idx !== -1) {
debug("Removing", f, "from filesData");
files[FILES_DATA].splice(idx, 1);
removePadAttribute(f.href);
}
});
};
@ -274,7 +290,7 @@ define([
var parentEl = exp.findElement(files, parentPath);
if (path.length === 4 && path[0] === TRASH) {
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);
} else {
parentEl[key] = undefined;
@ -283,6 +299,67 @@ define([
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
// NOTE: it is always used with an absolute path and root === files in our code
var findElement = exp.findElement = function (root, pathInput) {
@ -352,7 +429,6 @@ define([
};
// Move to trash
// TODO: rename the function
var removeElement = exp.removeElement = function (path, cb, keepOld) {
if (!path || path.length < 2 || path[0] === TRASH) {
debug("Calling removeElement from a wrong path: ", path);
@ -386,7 +462,7 @@ define([
if (isPathInHrefArray(newParentPath)) {
if (isFolder(element)) {
log(Messages.fo_moveUnsortedError); //TODO or template
log(Messages.fo_moveUnsortedError);
return;
} else {
if (elementPath[0] === newParentPath[0]) { return; }
@ -572,6 +648,28 @@ define([
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) {
if (path.length <= 1) {
@ -619,14 +717,15 @@ define([
var trashFiles = getTrashFiles();
var templateFiles = getTemplateFiles();
var newPath, parentEl;
if (path && isPathInHrefArray(path)) {
if (path) {
newPath = decodeURIComponent(path).split(',');
}
if (path && isPathInHrefArray(newPath)) {
parentEl = findElement(files, newPath);
parentEl.push(href);
return;
}
if (path && name) {
newPath = decodeURIComponent(path).split(',');
if (path && isPathInRoot(newPath) && name) {
parentEl = findElement(files, newPath);
if (parentEl) {
var newName = getAvailableName(parentEl, name);
@ -736,11 +835,8 @@ define([
toClean.push(idx);
}
});
toClean.forEach(function (el) {
var idx = us.indexOf(el);
if (idx !== -1) {
toClean.forEach(function (idx) {
us.splice(idx, 1);
}
});
};
var fixTemplate = function () {
@ -755,31 +851,31 @@ define([
toClean.push(idx);
}
});
toClean.forEach(function (el) {
var idx = us.indexOf(el);
if (idx !== -1) {
toClean.forEach(function (idx) {
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] = []; }
var fd = files[FILES_DATA];
var rootFiles = getRootFiles();
var unsortedFiles = getUnsortedFiles();
var templateFiles = getTemplateFiles();
var trashFiles = getTrashFiles();
var toClean = [];
fd.forEach(function (el, idx) {
if (typeof(el) !== "object") {
debug("An element in filesData was not an object.", el);
toClean.push(el);
} else {
return;
}
if (rootFiles.indexOf(el.href) === -1
&& unsortedFiles.indexOf(el.href) === -1
&& templateFiles.indexOf(el.href) === -1
&& 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) {

@ -33,17 +33,14 @@ define([
var USERLIST_CLS = Bar.constants.userlist = "cryptpad-dropdown-users";
var EDITSHARE_CLS = Bar.constants.editShare = "cryptpad-dropdown-editShare";
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_CLS = Bar.constants.dropdown = "cryptpad-dropdown";
var TITLE_CLS = Bar.constants.title = "cryptpad-title";
var USER_CLS = Bar.constants.userAdmin = "cryptpad-user";
/** 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 USERBUTTON_CLS = Bar.constants.changeUsername = "cryptpad-change-username";
var SPINNER_DISAPPEAR_TIME = 3000;
var SPINNER = [ '-', '\\', '|', '/' ];
var uid = function () {
return 'cryptpad-uid-' + String(Math.random()).substring(2);
@ -79,94 +76,65 @@ define([
return $toolbar;
};
var createSpinner = function ($container) {
var $spinner = $('<div>', {
var createSpinner = function ($container, config) {
if (config.displayed.indexOf('spinner') !== -1) {
var $spinner = $('<span>', {
id: uid(),
'class': SPINNER_CLS,
});
'class': SPINNER_CLS + ' fa fa fa-spinner fa-pulse',
}).hide();
$container.prepend($spinner);
return $spinner[0];
}
};
var kickSpinner = function (spinnerElement, reversed) {
var txt = spinnerElement.textContent || '-';
var inc = (reversed) ? -1 : 1;
spinnerElement.textContent = SPINNER[(SPINNER.indexOf(txt) + inc) % SPINNER.length];
var kickSpinner = function (spinnerElement) {
if (!spinnerElement) { return; }
$(spinnerElement).show();
if (spinnerElement.timeout) { clearTimeout(spinnerElement.timeout); }
spinnerElement.timeout = setTimeout(function () {
spinnerElement.textContent = '';
$(spinnerElement).hide();
}, SPINNER_DISAPPEAR_TIME);
};
var createUserButtons = function ($userlistElement, readOnly) {
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>';
var createUserButtons = function ($userlistElement, config, readOnly, Cryptpad) {
// User list button
var $editIcon = $('<button>', {
'class': 'userlist dropbtn edit',
});
var $editIconSmall = $editIcon.clone().addClass('small');
var $dropdownEditUsers = $('<p>', {'class': USERLIST_CLS});
var $dropdownEditContainer = $('<div>', {'class': DROPDOWN_CONTAINER_CLS});
var $dropdownEdit = $('<div>', {
id: "cryptpad-dropdown-edit",
'class': DROPDOWN_CLS
}).append($dropdownEditUsers); //.append($dropdownEditShare);
$editIcon.click(createHandler($dropdownEdit));
$editIconSmall.click(createHandler($dropdownEdit));
$dropdownEditContainer.append($editIcon).append($editIconSmall).append($dropdownEdit);
$listElement.append($dropdownEditContainer);
if (config.displayed.indexOf('userlist') !== -1) {
if (!config.userData) {
throw new Error("You must provide a `userData` object to display the userlist");
}
var dropdownConfig = {
options: [{
tag: 'p',
attributes: {'class': USERLIST_CLS},
}] // Entries displayed in the menu
};
var $block = Cryptpad.createDropdown(dropdownConfig);
$block.attr('id', 'userButtons');
$userlistElement.append($block);
}
// Share button
var fa_share = '<span class="fa fa-share-alt" style="font-family:FontAwesome;"></span>';
var fa_editusers = '<span class="fa fa-users" style="font-family:FontAwesome;"></span>';
var fa_viewusers = '<span class="fa fa-eye" style="font-family:FontAwesome;"></span>';
var $shareIcon = $('<button>', {
'class': 'userlist dropbtn share',
}).html(fa_share + ' ' + Messages.shareButton + ' ' + fa_caretdown);
var $shareIconSmall = $shareIcon.clone().addClass('small').html(fa_share + ' ' + fa_caretdown);
var $shareTitle = $('<h2>').html(fa_editusers + ' ' + Messages.shareEdit);
var $shareTitleView = $('<h2>').html(fa_viewusers + ' ' + Messages.shareView);
var $dropdownShareP = $('<p>', {'class': EDITSHARE_CLS}).append($shareTitle);
var $dropdownSharePView = $('<p>', {'class': VIEWSHARE_CLS}).append($shareTitleView);
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);
if (config.displayed.indexOf('share') !== -1) {
var $shareIcon = $('<span>', {'class': 'fa fa-share-alt'});
var $span = $('<span>', {'class': 'large'}).append($shareIcon.clone()).append(' ' +Messages.shareButton);
var $spanSmall = $('<span>', {'class': 'small'}).append($shareIcon.clone());
var dropdownConfigShare = {
text: $('<div>').append($span).append($spanSmall).html(),
options: []
};
var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare);
$shareBlock.find('.dropdown-bar-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS);
$userlistElement.append($shareBlock);
}
};
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>', {
'class': USER_LIST_CLS,
id: uid(),
});
createUserButtons($userlist, readOnly);
createUserButtons($userlist, config, readOnly, Cryptpad);
$container.append($userlist);
return $userlist[0];
};
@ -207,11 +175,12 @@ define([
$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
var $userButtons = $(userlistElement).find("#userButtons");
$userButtons.attr('display', 'inline');
if (config.displayed.indexOf('userlist') !== -1) {
var numberOfUsers = userList.length;
// If we are using old pads (readonly unavailable), only editing users are in userList.
@ -258,37 +227,37 @@ define([
$editUsers.html('').append($usersTitle).append(editUsersList);
// 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" style="font-family:FontAwesome;"></span>';
var fa_viewusers = '<span class="fa fa-eye" style="font-family:FontAwesome;"></span>';
var fa_editusers = '<span class="fa fa-users"></span>';
var fa_viewusers = '<span class="fa fa-eye"></span>';
var viewersText = numberOfViewUsers > 1 ? Messages.viewers : Messages.viewer;
var editorsText = numberOfEditUsers > 1 ? Messages.editors : Messages.editor;
$userButtons.find('.userlist.edit').html(fa_editusers + ' ' + numberOfEditUsers + ' ' + editorsText + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers + ' ' + viewersText + ' ' + fa_caretdown);
$userButtons.find('.userlist.edit.small').html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers + ' ' + fa_caretdown);
var $span = $('<span>', {'class': 'large'}).html(fa_editusers + ' ' + numberOfEditUsers + ' ' + editorsText + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers + ' ' + viewersText);
var $spansmall = $('<span>', {'class': 'small'}).html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers);
$userButtons.find('.buttonTitle').html('').append($span).append($spansmall);
}
// Change username button
if (config.displayed.indexOf('useradmin') !== -1) {
// Change username in useradmin dropdown
var $userElement = $userAdminElement.find('.' + USERNAME_CLS);
$userElement.show();
if (readOnly === 1) {
$userElement.html('<span class="' + READONLY_CLS + '">' + Messages.readonly + '</span>');
$userElement.addClass(READONLY_CLS).text(Messages.readonly);
}
else {
var name = userData[myUserName] && userData[myUserName].name;
var icon = '<span class="fa fa-user" style="font-family:FontAwesome;"></span>';
if (!name) {
name = Messages.anonymous;
}
$userElement.find("button").show();
$userElement.find("button").html(icon + ' ' + name);
$userElement.removeClass(READONLY_CLS).text(name);
}
}
};
var createLagElement = function ($container) {
var createLagElement = function () {
var $lag = $('<span>', {
'class': LAG_ELEM_CLS,
id: uid(),
});
$container.prepend($lag);
return $lag[0];
};
@ -356,37 +325,137 @@ define([
$linkContainer.append($aTagSmall).append($aTagBig);
};
var createUserAdmin = function ($topContainer) {
var createUserAdmin = function ($topContainer, config, readOnly, lagElement, Cryptpad) {
var $lag = $(lagElement);
var $userContainer = $('<span>', {
'class': USER_CLS
}).appendTo($topContainer);
if (config.displayed.indexOf('state') !== -1) {
var $state = $('<span>', {
'class': STATE_CLS
}).text(Messages.synchronizing).appendTo($userContainer);
}
var $span = $('<span>' , {
'class': 'cryptpad-language'
});
var $select = $('<select>', {
'id': 'language-selector'
}).appendTo($userContainer);
if (config.displayed.indexOf('lag') !== -1) {
$userContainer.append($lag);
}
var languages = Messages._languages;
for (var l in languages) {
$('<option>', {
value: l
}).text(languages[l]).appendTo($select);
if (config.displayed.indexOf('language') !== -1) {
// Dropdown language selector
Cryptpad.createLanguageSelector($userContainer);
}
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);
}
// User dropdown
if (config.displayed.indexOf('useradmin') !== -1) {
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);
$select.on('mousedown', function (e) {
e.stopPropagation();
return true;
$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');
});
var $usernameElement = $('<span>', {'class': USERNAME_CLS}).appendTo($userContainer);
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;
};
@ -473,36 +542,42 @@ define([
config = config || {};
var readOnly = (typeof config.readOnly !== "undefined") ? (config.readOnly ? 1 : 0) : -1;
var Cryptpad = config.common;
config.displayed = config.displayed || [];
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 $linkElement = createLinkToMain(toolbar.find('.' + TOP_CLS));
var $userAdminElement = createUserAdmin(toolbar.find('.' + TOP_CLS));
var lagElement = createLagElement($userAdminElement);
var spinner = createSpinner($userAdminElement);
var lagElement = createLagElement();
var $userAdminElement = createUserAdmin(toolbar.find('.' + TOP_CLS), config, readOnly, lagElement, Cryptpad);
var spinner = createSpinner($userAdminElement, config);
var userData = config.userData;
// readOnly = 1 (readOnly enabled), 0 (disabled), -1 (old pad without readOnly mode)
var saveElement;
var loadElement;
var $stateElement = $userAdminElement.find('.' + STATE_CLS);
var $stateElement = toolbar.find('.' + STATE_CLS);
var connected = false;
if (config.ifrw) {
var removeDropdowns = function (e) {
if ($(e.target).parents('.cryptpad-dropdown-container').length) {
return;
}
$container.find('.cryptpad-dropdown').hide();
};
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) {
return;
}
var ev = $.Event("keyup");
ev.which = 13;
$titleElement.find('input').trigger(ev);
/*
$titleElement.find('input').hide();
$titleElement.find('span.title').show();
$titleElement.find('span.pencilIcon').css('display', '');
*/
};
$(config.ifrw).on('click', removeDropdowns);
$(config.ifrw).on('click', cancelEditTitle);
@ -520,16 +595,13 @@ define([
}
// Update user list
if (config.userData) {
if (userData) {
userList.change.push(function (newUserData) {
var users = userList.users;
if (users.indexOf(myUserName) !== -1) { connected = true; }
if (!connected) { return; }
/*if (newUserData) { // Someone has changed his name/color
userData = newUserData;
}*/
checkSynchronizing(users, myUserName, $stateElement);
updateUserList(myUserName, userListElement, users, userData, readOnly, $userAdminElement);
updateUserList(config, myUserName, userListElement, users, userData, readOnly, $userAdminElement);
});
} else {
userList.change.push(function () {
@ -596,7 +668,7 @@ define([
}
var ks = function () {
if (connected) { kickSpinner(spinner, false); }
if (connected) { kickSpinner(spinner); }
};
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>
<html class="cp">
<head>
<title>CryptFiles</title>
<title>CryptDrive</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<link rel="icon" type="image/png"
href="/customize/main-favicon.png"
@ -36,7 +36,10 @@
</style>
</head>
<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>
</html>

@ -7,41 +7,50 @@
<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="file.css" />
<link rel="stylesheet" href="/customize/main.css" />
</head>
<body>
<div id="toolbar" class="toolbar-container"></div>
<div id="driveToolbar"></div>
<div class="app-container">
<div id="tree">
</div>
<div id="content">
</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;">
<li><a tabindex="-1" href="#" class="open" 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="delete editable" data-localization="fc_delete">Delete</a></li>
<li><a tabindex="-1" href="#" class="newfolder editable" data-localization="fc_newfolder">New folder</a></li>
<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>
<li><a tabindex="-1" href="#" class="rename editable dropdown-item" data-localization="fc_rename">Rename</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>
</div>
<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;">
<li><a tabindex="-1" href="#" class="newfolder editable" 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" 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" data-type="poll" data-localization="fc_newpoll" target="_blank">New poll</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 dropdown-item" 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="code" data-localization="fc_newcode" target="_blank">New code</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 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>
</div>
<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;">
<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>
</div>
<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;">
<li><a tabindex="-1" href="#" class="remove editable" 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="properties" data-localization="fc_prop">Properties</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 dropdown-item" data-localization="fc_restore">Restore</a></li>
<li><a tabindex="-1" href="#" class="properties dropdown-item" data-localization="fc_prop">Properties</a></li>
</ul>
</div>
</div>

File diff suppressed because it is too large Load Diff

@ -71,7 +71,7 @@ li {
#tree {
border-right: 1px solid #ccc;
box-sizing: border-box;
background: white;
background: #eee;
overflow: auto;
resize: horizontal;
width: 350px;
@ -97,7 +97,7 @@ li {
text-decoration: underline;
}
#tree #trashTree, #tree #unsortedTree, #tree #templateTree, #tree #allfilesTree {
#tree .category2 {
margin-top: 2em;
}
@ -184,6 +184,7 @@ li {
#content h1 {
padding-left: 10px;
margin-top: 10px;
}
#content .info-box {
@ -200,12 +201,6 @@ li {
float: right;
}
.topButtonContainer {
border: 1px solid #ccc;
float: right;
border-radius: 4px;
}
.parentFolder {
cursor: pointer;
margin-left: 10px;
@ -248,16 +243,20 @@ li {
display: block;
margin: auto;
font-size: 40px;
width: auto;
text-align: center;
}
#content .grid li .fa.listonly {
display: none;
}
#content .list li {
display: flex;
flex-flow: row;
align-items: center;
padding-right: 0px;
}
#content .list li .element {
display: inline-flex;
flex: 1;
@ -325,3 +324,55 @@ li {
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/jquery/dist/jquery.min.js',
'/bower_components/file-saver/FileSaver.min.js',
'/customize/pad.js'
//'/customize/pad.js'
], function (Config, Realtime, Crypto, TextPatcher, JSONSortify, JsonOT, Cryptpad) {
var saveAs = window.saveAs;

@ -12,16 +12,17 @@ define([
//console.log("plaintext");
//console.log(body);
/*
0 && Crypt.put(body, function (e, out) {
if (e) { return void console.error(e); }
if (out) {
console.log(out);
}
});
}); */
var data = {};
(_ => {
(function () {
var cyphertext = data.payload = Crypto.encrypt(body, key);
console.log("encrypted");
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>
<html class="cp pad">
<head>
<title>CryptPad</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png"
href="/customize/main-favicon.png"
data-main-favicon="/customize/main-favicon.png"
@ -58,7 +60,10 @@
</style>
</head>
<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>
</html>

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

@ -62,6 +62,14 @@ define([
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 isNotMagicLine = function (el) {
@ -98,8 +106,12 @@ define([
// document itself and causes problems when it's sent across the wire and reflected back
removePlugins: 'resize',
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) {
@ -362,7 +374,9 @@ define([
defaultTitle: defaultName
}
};
if (!initializing) {
hjson[3].metadata.title = document.title;
}
return stringify(hjson);
};
@ -433,6 +447,7 @@ define([
// Extract the user list (metadata) from the hyperjson
var hjson = JSON.parse(shjson);
var peerMetadata = hjson[3];
var titleUpdated = false;
if (peerMetadata && peerMetadata.metadata) {
if (peerMetadata.metadata.users) {
var userData = peerMetadata.metadata.users;
@ -443,9 +458,13 @@ define([
updateDefaultTitle(peerMetadata.metadata.defaultTitle);
}
if (typeof peerMetadata.metadata.title !== "undefined") {
updateTitle(peerMetadata.metadata.title);
updateTitle(peerMetadata.metadata.title || defaultName);
titleUpdated = true;
}
}
if (!titleUpdated) {
updateTitle(defaultName);
}
};
var unnotify = function () {
@ -465,6 +484,8 @@ define([
var onRemote = realtimeOptions.onRemote = function (info) {
if (initializing) { return; }
var oldShjson = stringifyDOM(inner);
var shjson = info.realtime.getUserDoc();
// remember where the cursor is
@ -473,6 +494,12 @@ define([
// Update the user list (metadata) from the hyperjson
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
applyHjson(shjson);
@ -506,7 +533,12 @@ define([
}
}
}
// 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) {
@ -547,7 +579,16 @@ define([
var onInit = realtimeOptions.onInit = function (info) {
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 = {
displayed: ['useradmin', 'language', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad'],
userData: userData,
readOnly: readOnly,
ifrw: ifrw,
@ -556,6 +597,10 @@ define([
defaultName: defaultName,
suggestName: suggestName
},
userName: {
setName: setName,
lastName: module.userName
},
common: Cryptpad
};
if (readOnly) {delete config.changeNameID; }
@ -565,6 +610,7 @@ define([
var $userBlock = $bar.find('.' + Toolbar.constants.username);
var $editShare = $bar.find('.' + Toolbar.constants.editShare);
var $viewShare = $bar.find('.' + Toolbar.constants.viewShare);
var $usernameButton = module.$userNameButton = $($bar.find('.' + Toolbar.constants.changeUsername));
var editHash;
var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys);
@ -573,14 +619,7 @@ define([
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 */
var $export = Cryptpad.createButton('export', true, {}, exportFile);
@ -606,6 +645,9 @@ define([
if (!readOnly) {
$editShare.append(Cryptpad.createButton('editshare', false, {editHash: editHash}));
if (viewHash) {
$editShare.append($('<hr>'));
}
}
if (viewHash) {
/* add a 'links' button */
@ -617,27 +659,19 @@ define([
// set the hash
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
var onReady = realtimeOptions.onReady = function (info) {
if (!APP.isMaximized) {
if (!module.isMaximized) {
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) {
editor.execCommand('maximize');
editor.execCommand('maximize');
}
APP.isMaximized = true;
}
module.patchText = TextPatcher.create({
@ -664,7 +698,8 @@ define([
console.log("Unlocking editor");
setEditable(true);
initializing = false;
Cryptpad.removeLoadingScreen();
Cryptpad.removeLoadingScreen(emitResize);
// Update the toolbar list:
// Add the current user in the metadata if he has edit rights
if (readOnly) { return; }
@ -758,9 +793,17 @@ define([
var first = function () {
Ckeditor = ifrw.CKEDITOR;
if (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);
} else {
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 name="viewport" content="width=device-width, initial-scale=1.0"/>
<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="/customize/main.css" />
<script data-main="main" src="/bower_components/requirejs/require.js"></script>
@ -22,10 +27,11 @@
color: #000;
line-height: auto;
}
.cryptpad-toolbar {
display: inline-block;
}
.realtime {
display: block;
overflow: auto;
max-height: 100%;
max-width: 100%;
}
@ -43,11 +49,6 @@
font: white;
border: 0px;
}
td label {
border: .5px solid black;
}
table#table {
margin: 0px;
}
@ -95,7 +96,7 @@
}
#description[disabled] {
resize: none;
color: #bbb;
color: #000;
border: 1px solid #444;
}

@ -12,7 +12,7 @@ define([
'/common/notify.js',
'/bower_components/file-saver/FileSaver.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 unlockHTML = '<i class="fa fa-unlock" aria-hidden="true"></i>';
@ -22,6 +22,16 @@ define([
var secret = Cryptpad.getSecrets();
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();
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 */
var change = function (o, n, path, throttle) {
if (path && !Cryptpad.isArray(path)) {
return;
}
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(', '));
}
@ -207,6 +234,7 @@ define([
https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
*/
notify();
if (throttle) {
if (APP.throttled) { window.clearTimeout(APP.throttled); }
@ -235,7 +263,7 @@ define([
var type = input.type.toLowerCase();
var id = getRealtimeId(input);
console.log(input);
debug(input);
var object = APP.proxy;
@ -246,22 +274,22 @@ define([
switch (type) {
case 'text':
console.log("text[rt-id='%s'] [%s]", id, input.value);
if (!input.value) { return void console.log("Hit enter?"); }
debug("text[rt-id='%s'] [%s]", id, input.value);
if (!input.value) { return void debug("Hit enter?"); }
Render.setValue(object, id, input.value);
change(null, null, null, 50);
break;
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) {
Render.setValue(object, id, input.checked);
change();
} else {
console.log('checkbox locked');
debug('checkbox locked');
}
break;
default:
console.log("Input[type='%s']", type);
debug("Input[type='%s']", type);
break;
}
};
@ -301,7 +329,7 @@ define([
} else if (type === 'cell') {
change();
} else {
console.log("UNHANDLED");
debug("UNHANDLED");
}
};
@ -326,10 +354,10 @@ define([
var target = e && e.target;
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;
@ -349,7 +377,7 @@ define([
//console.error(new Error("C'est pas possible!"));
break;
default:
console.log(target, nodeName);
debug(target, nodeName);
break;
}
};
@ -359,14 +387,21 @@ define([
*/
var prepareProxy = function (proxy, schema) {
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;
};
/*
*/
@ -438,8 +473,8 @@ define([
document.title = newTitle;
Cryptpad.renamePad(newTitle, function (err, data) {
if (err) {
console.log("Couldn't set pad title");
console.error(err);
debug("Couldn't set pad title");
error(err);
document.title = oldTitle;
return;
}
@ -456,14 +491,16 @@ define([
var renameCb = function (err, title) {
if (err) { return; }
document.title = title;
APP.proxy.info.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) {
return JSON.parse(JSON.stringify(obj));
};
@ -473,8 +510,8 @@ define([
var $description = $('#description').attr('placeholder', Messages.poll_descriptionHint || 'description');
var ready = function (info, userid, readOnly) {
console.log("READY");
console.log('userid: %s', userid);
debug("READY");
debug('userid: %s', userid);
var proxy = APP.proxy;
var uncommitted = APP.uncommitted = {};
@ -565,6 +602,7 @@ define([
.on('change', ['info'], function (o, n, p) {
if (p[1] === 'title') {
updateTitle(n);
notify();
} else if (p[1] === "userData") {
addToUserData(APP.proxy.info.userData);
} else if (p[1] === 'description') {
@ -581,15 +619,23 @@ define([
el.selectionStart = selects[0];
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('remove', [], change);
addToUserData(APP.proxy.info.userData);
if (Visible.isSupported()) {
Visible.onChange(function (yes) {
if (yes) { unnotify(); }
});
}
getLastName(function (err, lastName) {
APP.ready = true;
@ -633,7 +679,16 @@ define([
});
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 = {
displayed: ['useradmin', 'language', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad'],
userData: userData,
readOnly: readOnly,
title: {
@ -641,8 +696,12 @@ define([
defaultName: defaultName,
suggestName: suggestName
},
userName: {
setName: setName,
lastName: APP.userName
},
ifrw: window,
common: Cryptpad
common: Cryptpad,
};
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 $editShare = $bar.find('.' + Toolbar.constants.editShare);
var $viewShare = $bar.find('.' + Toolbar.constants.viewShare);
// 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);
});
var $usernameButton = APP.$userNameButton = $($bar.find('.' + Toolbar.constants.changeUsername));
/* add a forget button */
var forgetCb = function (err, title) {
@ -685,8 +736,8 @@ define([
Cryptpad.getPadTitle(function (err, title) {
if (err) {
console.error(err);
console.log("Couldn't get pad title");
error(err);
debug("Couldn't get pad title");
return;
}
updateTitle(title || defaultName);
@ -737,7 +788,7 @@ define([
Cryptpad.getAttribute(HIDE_INTRODUCTION_TEXT, function (e, value) {
if (e) { console.error(e); }
if (value === null) {
if (!value) {
Cryptpad.setAttribute(HIDE_INTRODUCTION_TEXT, "1", function (e) {
if (e) { console.error(e); }
});

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

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html class="cp slide">
<head>
<title>CryptPad</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script data-main="main" src="/bower_components/requirejs/require.js"></script>
<script>
@ -50,7 +51,10 @@
</head>
<body>
<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>
</body>
</html>

@ -92,6 +92,9 @@
<span class="cp slide">
<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>
</span>

@ -126,7 +126,7 @@ define([
}
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
}
};
if (!initializing) {
obj.metadata.title = APP.title;
}
if (textColor) {
obj.metadata.color = textColor;
}
@ -384,6 +386,7 @@ define([
var updateMetadata = function(shjson) {
// Extract the user list (metadata) from the hyperjson
var json = (shjson === "") ? "" : JSON.parse(shjson);
var titleUpdated = false;
if (json && json.metadata) {
if (json.metadata.users) {
var userData = json.metadata.users;
@ -394,10 +397,14 @@ define([
updateDefaultTitle(json.metadata.defaultTitle);
}
if (typeof json.metadata.title !== "undefined") {
updateTitle(json.metadata.title);
updateTitle(json.metadata.title || defaultName);
titleUpdated = true;
}
updateColors(json.metadata.color, json.metadata.backColor);
}
if (!titleUpdated) {
updateTitle(defaultName);
}
};
var renameCb = function (err, title) {
@ -409,7 +416,16 @@ define([
var onInit = config.onInit = function (info) {
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 = {
displayed: ['useradmin', 'language', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad'],
userData: userData,
readOnly: readOnly,
ifrw: ifrw,
@ -418,6 +434,10 @@ define([
defaultName: defaultName,
suggestName: suggestName
},
userName: {
setName: setName,
lastName: module.userName
},
common: Cryptpad
};
if (readOnly) {delete config.changeNameID; }
@ -427,6 +447,7 @@ define([
var $userBlock = $bar.find('.' + Toolbar.constants.username);
var $editShare = $bar.find('.' + Toolbar.constants.editShare);
var $viewShare = $bar.find('.' + Toolbar.constants.viewShare);
var $usernameButton = module.$userNameButton = $($bar.find('.' + Toolbar.constants.changeUsername));
var editHash;
var viewHash = Cryptpad.getViewHashFromKeys(info.channel, secret.keys);
@ -435,15 +456,6 @@ define([
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 */
var $export = Cryptpad.createButton('export', true, {}, exportText);
$rightside.append($export);
@ -494,44 +506,40 @@ define([
}
$rightside.append($leavePresent);
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 */
var themeKey = 'CRYPTPAD_CODE_THEME';
var lastTheme = localStorage.getItem(themeKey) || 'default';
/* Let the user select different themes */
var $themeDropdown = $('<select>', {
title: 'color theme',
id: 'display-theme',
'class': 'rightside-element'
var options = [];
Themes.forEach(function (l) {
options.push({
tag: 'a',
attributes: {
'data-value': l.name,
'href': '#',
},
content: l.name // Pretty name of the language value
});
Themes.forEach(function (o) {
$themeDropdown.append($('<option>', {
selected: o.name === lastTheme,
}).val(o.name).text(o.name));
});
var dropdownConfig = {
text: 'Theme', // Button initial text
options: options, // Entries displayed in the menu
left: true, // Open to the left of the button
};
var $block = module.$theme = Cryptpad.createDropdown(dropdownConfig);
var $button = $block.find('.buttonTitle');
setTheme(lastTheme, $block);
$rightside.append($themeDropdown);
var $theme = $bar.find('select#display-theme');
setTheme(lastTheme, $theme);
$theme.on('change', function () {
var theme = $theme.val();
console.log("Setting theme to %s", theme);
setTheme(theme, $theme);
// remember user choices
$block.find('a').click(function (e) {
var theme = $(this).attr('data-value');
setTheme(theme, $block);
$button.text($(this).text());
localStorage.setItem(themeKey, theme);
});
$rightside.append($block);
};
var configureColors = function () {
@ -590,15 +598,6 @@ define([
if (!window.location.hash || window.location.hash === '#') {
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 () {
@ -767,7 +766,9 @@ define([
}
Slide.update(remoteDoc);
if (oldDoc !== remoteDoc) {
notify();
}
};
var onAbort = config.onAbort = function (info) {

@ -202,6 +202,31 @@ define([
};
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) {
if (!Slide.shown) { return; }
switch(e.which) {

@ -25,6 +25,11 @@
display: none;
border-radius: 5px;
}
.register {
display: none;
}
@media (max-width: 1000px) {
div.panel { width: 90%; }
}
@ -49,6 +54,7 @@
<div id="warning" data-localization="login_warning"></div>
<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" class="register" name="confirm" id="confirm_register" data-localization-placeholder="login_confirm"><br />
<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>
@ -83,6 +89,6 @@
<p><span data-localization="username_label"></span>
<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>

@ -23,8 +23,10 @@ define([
// login elements
var $loginBox = $('#login-panel');
var $login = $('#login');
var $login_register = $('#login_register');
var $username = $('#username');
var $password = $('#password');
var $password_register = $('#confirm_register');
var $remember = $('#remember');
// hashing elements
@ -61,7 +63,13 @@ define([
var revealConfirm = APP.revealConfirm = revealer($confirmBox);
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) {
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
confirmPassword(proxy, opt.password, function () {
var todo = function () {
APP.confirming = false;
APP.setAccountName((proxy.login_name = opt.name));
APP.setDisplayName(APP.getDisplayName(proxy));
@ -219,24 +227,45 @@ define([
proxy[k] = map[k];
});
var whenSynced = function () {
delete localStorage.FS_hash;
if (!proxy[USERNAME_KEY]) {
proxy[USERNAME_KEY] = opt.name;
}
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 proxyKeys = Object.keys(proxy);
var now = opt.now = +(new Date());
if (!proxyKeys.length) {
if (opt.register) {
return handleNewUser(proxy, opt, true);
}
return handleNewUser(proxy, opt);
}
handleRegisteredUser(proxy, opt);
@ -287,7 +316,6 @@ define([
};
var rt = APP.rt = Listmap.create(config);
rt.proxy.on('create', function (info) {
APP.realtime = info.realtime;
})
@ -310,6 +338,11 @@ define([
revealUser(true);
} 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);
}
@ -318,9 +351,15 @@ define([
$login.click(function () {
var uname = $username.val().trim();
var passwd = $password.val();
var passwd_confirm = $password_register.val();
var confirm = $confirm.val();
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)) {
return void Cryptpad.alert('invalid username');
}
@ -341,7 +380,7 @@ define([
window.setTimeout(function () {
useBytes(bytes, {
remember: remember,
//register: register,
register: register,
name: uname,
password: passwd,
});
@ -350,5 +389,21 @@ define([
}, 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