manual merge of staging

pull/1/head
Caleb James DeLisle 7 years ago
commit f6abdc2b86

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

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

@ -444,7 +444,7 @@ define([
h('div#poll', [ h('div#poll', [
h('div#howItWorks', [ h('div#howItWorks', [
h('h1', 'CryptPoll'), h('h1', 'CryptPoll'),
h('h2', Msg.poll_subtitle), setHTML(h('h2'), Msg.poll_subtitle),
h('p', Msg.poll_p_save), h('p', Msg.poll_p_save),
h('p', Msg.poll_p_encryption) h('p', Msg.poll_p_encryption)
]), ]),

@ -112,17 +112,19 @@ h6 {
padding-top: .65001rem; padding-top: .65001rem;
} }
a:not(.btn) { p {
cursor: pointer; a:not(.btn) {
color: @cp-link; cursor: pointer;
color: @cp-link;
text-decoration: none; text-decoration: none;
&:hover { &:hover {
color: @cp-link-hover; color: @cp-link-hover;
} }
&:visited { &:visited {
color: @cp-link-visited; color: @cp-link-visited;
}
} }
} }
a.btn { a.btn {

@ -21,6 +21,10 @@
margin-right: 0px; margin-right: 0px;
margin-left: 5px; margin-left: 5px;
} }
* {
.unselectable();
cursor: default;
}
} }
.dropdown-bar-content { .dropdown-bar-content {

@ -16,15 +16,28 @@
color: inherit; color: inherit;
} }
#cke_editor1 { .padColor { color: @toolbar-pad-bg; }
.cke_inner { .codeColor { color: @toolbar-code-bg; }
bottom: 0; .slideColor { color: @toolbar-slide-bg; }
right: 0; .pollColor { color: @toolbar-poll-bg; }
display: flex; .fileColor { color: @toolbar-file-bg; }
flex-flow: column; .whiteboardColor { color: @toolbar-whiteboard-bg; }
height: 100vh; .driveColor { color: @toolbar-drive-bg; }
width: 100vw; .defaultColor { color: @toolbar-default-bg; }
}
.toolbar-container {
display: flex;
}
#cke_editor1 .cke_inner {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: flex;
flex-flow: column;
height: 100vh;
width: 100vw;
} }
.cke_toolbox_main { .cke_toolbox_main {
display: inline-block; display: inline-block;
@ -35,17 +48,12 @@
margin-top: -1px; margin-top: -1px;
display: flex; display: flex;
overflow: visible; overflow: visible;
iframe {
height: auto;
width: 100%;
}
} }
.padColor { color: @toolbar-pad-bg; }
.codeColor { color: @toolbar-code-bg; }
.slideColor { color: @toolbar-slide-bg; }
.pollColor { color: @toolbar-poll-bg; }
.fileColor { color: @toolbar-file-bg; }
.whiteboardColor { color: @toolbar-whiteboard-bg; }
.driveColor { color: @toolbar-drive-bg; }
.defaultColor { color: @toolbar-default-bg; }
body .userlist-drawer { body .userlist-drawer {
font: normal normal normal 16px Arial,Helvetica,Tahoma,Verdana,Sans-Serif; font: normal normal normal 16px Arial,Helvetica,Tahoma,Verdana,Sans-Serif;
min-width: 175px; min-width: 175px;
@ -104,6 +112,51 @@ body .userlist-drawer {
background: rgba(0,0,0,0.1); background: rgba(0,0,0,0.1);
margin: 2px 0; margin: 2px 0;
font-size: 16px; font-size: 16px;
display: inline-flex;
align-items: center;
&.clickable {
cursor: pointer;
&:hover {
background-color: rgba(0,0,0,0.3);
}
}
.default, media-tag {
display: inline-flex;
width: 50px;
height: 50px;
justify-content: center;
align-items: center;
margin-right: 5px;
border-radius: 10px / 6px;
overflow: hidden;
border: 1px solid black;
box-sizing: content-box;
}
.default {
.unselectable();
background: white;
color: black;
font-size: 40px;
}
.name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
media-tag {
min-height: 50px;
min-width: 50px;
max-height: 50px;
max-width: 50px;
img {
min-width: 100%;
min-height: 100%;
max-width: none;
max-height: none; // To override 'media-tag img' in slide.less
flex-shrink: 0;
}
}
} }
} }
} }
@ -718,6 +771,8 @@ body .cryptpad-toolbar {
padding: 0px; padding: 0px;
margin: 0; margin: 0;
&::before { &::before {
width: 100%;
text-align: center;
padding-top: 4px; padding-top: 4px;
} }
&:hover { &:hover {
@ -796,11 +851,10 @@ body .cryptpad-toolbar {
width: 64px; width: 64px;
padding: 0; padding: 0;
span { span {
text-align: center;
width: 100%;
cursor: default; cursor: default;
font-size: 32px; font-size: 32px;
.fa {
margin-left: 3px;
}
} }
} }
} }
@ -850,8 +904,10 @@ body .cryptpad-toolbar {
} }
} }
.cryptpad-toolbar-rightside { .cryptpad-toolbar-rightside {
height: 32px; min-height: 32px;
overflow: hidden;
&:empty { &:empty {
min-height: 0;
height: 0; height: 0;
} }
text-align: right; text-align: right;
@ -873,7 +929,7 @@ body .cryptpad-toolbar {
background: @dropdown-bg; background: @dropdown-bg;
display: flex; display: flex;
flex-flow: column; flex-flow: column;
z-index:1000; z-index:10000;
color: black; color: black;
.fa { .fa {
font-size: 17px; font-size: 17px;
@ -881,7 +937,7 @@ body .cryptpad-toolbar {
&> span { &> span {
box-sizing: border-box; box-sizing: border-box;
min-width: 150px; min-width: 150px;
height: 26px; height: 32px;
border-radius: 0; border-radius: 0;
border: 0; border: 0;
} }

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

@ -240,6 +240,18 @@ define(function () {
out.canvas_opacityLabel = "opacité: {0}"; out.canvas_opacityLabel = "opacité: {0}";
out.canvas_widthLabel = "taille: {0}"; out.canvas_widthLabel = "taille: {0}";
// Profile
out.profileButton = "Profil"; // dropdown menu
out.profile_urlPlaceholder = 'URL';
out.profile_namePlaceholder = 'Nom ou pseudo pour le profil';
out.profile_avatar = "Avatar";
out.profile_upload = " Importer un nouvel avatar";
out.profile_error = "Erreur lors de la création du profil : {0}";
out.profile_register = "Vous devez vous inscrire pour pouvoir créer un profil !";
out.profile_create = "Créer un profil";
out.profile_description = "Description";
out.profile_fieldSaved = 'Nouvelle valeur enregistrée: {0}';
// File manager // File manager
out.fm_rootName = "Documents"; out.fm_rootName = "Documents";

@ -241,7 +241,18 @@ define(function () {
out.canvas_opacity = "Opacity"; out.canvas_opacity = "Opacity";
out.canvas_opacityLabel = "opacity: {0}"; out.canvas_opacityLabel = "opacity: {0}";
out.canvas_widthLabel = "Width: {0}"; out.canvas_widthLabel = "Width: {0}";
// Profile
out.profileButton = "Profile"; // dropdown menu
out.profile_urlPlaceholder = 'URL';
out.profile_namePlaceholder = 'Name displayed in your profile';
out.profile_avatar = "Avatar";
out.profile_upload = " Upload a new avatar";
out.profile_error = "Error while creating your profile: {0}";
out.profile_register = "You have to sign up to create a profile!";
out.profile_create = "Create a profile";
out.profile_description = "Description";
out.profile_fieldSaved = 'New value saved: {0}';
// File manager // File manager

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

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

@ -281,7 +281,7 @@ var getUploadSize = function (Env, channel, cb) {
var paths = Env.paths; var paths = Env.paths;
var path = makeFilePath(paths.blob, channel); var path = makeFilePath(paths.blob, channel);
if (!path) { if (!path) {
return cb('INVALID_UPLOAD_ID'); return cb('INVALID_UPLOAD_ID', path);
} }
Fs.stat(path, function (err, stats) { Fs.stat(path, function (err, stats) {
@ -583,9 +583,10 @@ var resetUserPins = function (Env, publicKey, channelList, cb) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
var pinSize = sumChannelSizes(sizes); var pinSize = sumChannelSizes(sizes);
getFreeSpace(Env, publicKey, function (e, free) {
getLimit(Env, publicKey, function (e, limit) {
if (e) { if (e) {
WARN('getFreeSpace', e); WARN('[RESET_ERR]', e);
return void cb(e); return void cb(e);
} }
@ -597,7 +598,7 @@ var resetUserPins = function (Env, publicKey, channelList, cb) {
They will not be able to pin additional pads until they upgrade They will not be able to pin additional pads until they upgrade
or delete enough files to go back under their limit. */ or delete enough files to go back under their limit. */
if (pinSize > free && session.hasPinned) { return void(cb('E_OVER_LIMIT')); } if (pinSize > limit && session.hasPinned) { return void(cb('E_OVER_LIMIT')); }
pinStore.message(publicKey, JSON.stringify(['RESET', channelList]), pinStore.message(publicKey, JSON.stringify(['RESET', channelList]),
function (e) { function (e) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
@ -826,6 +827,13 @@ var upload_status = function (Env, publicKey, filesize, cb) {
}); });
}; };
var isUnauthenticatedCall = function (call) {
return [
'GET_FILE_SIZE',
'GET_MULTIPLE_FILE_SIZE',
].indexOf(call) !== -1;
};
var isAuthenticatedCall = function (call) { var isAuthenticatedCall = function (call) {
return [ return [
'COOKIE', 'COOKIE',
@ -834,11 +842,8 @@ var isAuthenticatedCall = function (call) {
'UNPIN', 'UNPIN',
'GET_HASH', 'GET_HASH',
'GET_TOTAL_SIZE', 'GET_TOTAL_SIZE',
'GET_FILE_SIZE',
'UPDATE_LIMITS', 'UPDATE_LIMITS',
'GET_LIMIT', 'GET_LIMIT',
'GET_MULTIPLE_FILE_SIZE',
//'UPLOAD',
'UPLOAD_COMPLETE', 'UPLOAD_COMPLETE',
'UPLOAD_CANCEL', 'UPLOAD_CANCEL',
].indexOf(call) !== -1; ].indexOf(call) !== -1;
@ -867,6 +872,34 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
var blobPath = paths.blob = keyOrDefaultString('blobPath', './blob'); var blobPath = paths.blob = keyOrDefaultString('blobPath', './blob');
var blobStagingPath = paths.staging = keyOrDefaultString('blobStagingPath', './blobstage'); var blobStagingPath = paths.staging = keyOrDefaultString('blobStagingPath', './blobstage');
var isUnauthenticateMessage = function (msg) {
return msg && msg.length === 2 && isUnauthenticatedCall(msg[0]);
};
var handleUnauthenticatedMessage = function (msg, respond) {
switch (msg[0]) {
case 'GET_FILE_SIZE':
return void getFileSize(Env, msg[1], function (e, size) {
if (e) {
console.error(e);
}
WARN(e, msg[1]);
respond(e, [null, size, null]);
});
case 'GET_MULTIPLE_FILE_SIZE':
return void getMultipleFileSize(Env, msg[1], function (e, dict) {
if (e) {
WARN(e, dict);
return respond(e);
}
respond(e, [null, dict, null]);
});
default:
console.error("unsupported!");
return respond('UNSUPPORTED_RPC_CALL', msg);
}
};
var rpc = function ( var rpc = function (
ctx /*:{ store: Object }*/, ctx /*:{ store: Object }*/,
data /*:Array<Array<any>>*/, data /*:Array<Array<any>>*/,
@ -888,11 +921,19 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
return void respond('INVALID_ARG_FORMAT'); return void respond('INVALID_ARG_FORMAT');
} }
if (isUnauthenticateMessage(msg)) {
return handleUnauthenticatedMessage(msg, respond);
}
var signature = msg.shift(); var signature = msg.shift();
var publicKey = msg.shift(); var publicKey = msg.shift();
// make sure a user object is initialized in the cookie jar // make sure a user object is initialized in the cookie jar
beginSession(Sessions, publicKey); if (publicKey) {
beginSession(Sessions, publicKey);
} else {
console.log("No public key");
}
var cookie = msg[0]; var cookie = msg[0];
if (!isValidCookie(Sessions, publicKey, cookie)) { if (!isValidCookie(Sessions, publicKey, cookie)) {
@ -928,7 +969,8 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
msg.shift(); msg.shift();
var Respond = function (e, msg) { var Respond = function (e, msg) {
var token = Sessions[safeKey].tokens.slice(-1)[0]; var session = Sessions[safeKey];
var token = session? session.tokens.slice(-1)[0]: '';
var cookie = makeCookie(token).join('|'); var cookie = makeCookie(token).join('|');
respond(e, [cookie].concat(typeof(msg) !== 'undefined' ?msg: [])); respond(e, [cookie].concat(typeof(msg) !== 'undefined' ?msg: []));
}; };
@ -1046,8 +1088,8 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
return void handleMessage(false); return void handleMessage(false);
} }
// restrict upload capability unless explicitly disabled // allow unrestricted uploads unless restrictUploads is true
if (config.restrictUploads === false) { if (config.restrictUploads !== true) {
return void handleMessage(true); return void handleMessage(true);
} }

@ -3,33 +3,6 @@
<head> <head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script src="/bower_components/jquery/dist/jquery.min.js"></script> <script src="/bower_components/jquery/dist/jquery.min.js"></script>
<script src="/bower_components/codemirror/lib/codemirror.js"></script>
<link rel="stylesheet" href="/bower_components/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="/bower_components/codemirror/addon/dialog/dialog.css">
<link rel="stylesheet" href="/bower_components/codemirror/addon/fold/foldgutter.css" />
<script src="/bower_components/codemirror/mode/javascript/javascript.js"></script>
<script src="/bower_components/codemirror/addon/mode/loadmode.js"></script>
<script src="/bower_components/codemirror/mode/meta.js"></script>
<script src="/bower_components/codemirror/addon/mode/overlay.js"></script>
<script src="/bower_components/codemirror/addon/mode/multiplex.js"></script>
<script src="/bower_components/codemirror/addon/mode/simple.js"></script>
<script src="/bower_components/codemirror/addon/edit/closebrackets.js"></script>
<script src="/bower_components/codemirror/addon/edit/matchbrackets.js"></script>
<script src="/bower_components/codemirror/addon/edit/trailingspace.js"></script>
<script src="/bower_components/codemirror/addon/selection/active-line.js"></script>
<script src="/bower_components/codemirror/addon/search/search.js"></script>
<script src="/bower_components/codemirror/addon/search/match-highlighter.js"></script>
<script src="/bower_components/codemirror/addon/search/searchcursor.js"></script>
<script src="/bower_components/codemirror/addon/dialog/dialog.js"></script>
<script src="/bower_components/codemirror/addon/fold/foldcode.js"></script>
<script src="/bower_components/codemirror/addon/fold/foldgutter.js"></script>
<script src="/bower_components/codemirror/addon/fold/brace-fold.js"></script>
<script src="/bower_components/codemirror/addon/fold/xml-fold.js"></script>
<script src="/bower_components/codemirror/addon/fold/markdown-fold.js"></script>
<script src="/bower_components/codemirror/addon/fold/comment-fold.js"></script>
<script src="/bower_components/codemirror/addon/display/placeholder.js"></script>
<script async data-bootload="inner.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script> <script async data-bootload="inner.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script>
<style> .loading-hidden { display: none; } </style> <style> .loading-hidden { display: none; } </style>
</head> </head>

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

@ -9,7 +9,6 @@ define([
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/cryptget.js', '/common/cryptget.js',
'/common/diffMarked.js', '/common/diffMarked.js',
'/bower_components/tweetnacl/nacl-fast.min.js', // needed for media-tag
'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less/cryptpad.less' 'less!/customize/src/less/cryptpad.less'
@ -58,7 +57,7 @@ define([
} }
}); });
var CodeMirror = Cryptpad.createCodemirror(CMeditor, ifrw, Cryptpad); var CodeMirror = Cryptpad.createCodemirror(ifrw, Cryptpad, null, CMeditor);
$iframe.find('.CodeMirror').addClass('fullPage'); $iframe.find('.CodeMirror').addClass('fullPage');
editor = CodeMirror.editor; editor = CodeMirror.editor;
@ -429,7 +428,6 @@ define([
}; };
var interval = 100; var interval = 100;
var second = function (CM) { var second = function (CM) {
Cryptpad.ready(function () { Cryptpad.ready(function () {
andThen(CM); andThen(CM);
@ -444,11 +442,9 @@ define([
var first = function () { var first = function () {
if (ifrw.CodeMirror) { if (ifrw.CodeMirror) {
// it exists, call your continuation
second(ifrw.CodeMirror); second(ifrw.CodeMirror);
} else { } else {
console.log("CodeMirror was not defined. Trying again in %sms", interval); console.log("CodeMirror was not defined. Trying again in %sms", interval);
// try again in 'interval' ms
setTimeout(first, interval); setTimeout(first, interval);
} }
}; };

@ -10,6 +10,7 @@ define([], function () {
"json.sortify": "/bower_components/json.sortify/dist/JSON.sortify", "json.sortify": "/bower_components/json.sortify/dist/JSON.sortify",
//"pdfjs-dist/build/pdf": "/bower_components/pdfjs-dist/build/pdf", //"pdfjs-dist/build/pdf": "/bower_components/pdfjs-dist/build/pdf",
//"pdfjs-dist/build/pdf.worker": "/bower_components/pdfjs-dist/build/pdf.worker" //"pdfjs-dist/build/pdf.worker": "/bower_components/pdfjs-dist/build/pdf.worker"
cm: '/bower_components/codemirror'
}, },
map: { map: {
'*': { '*': {

@ -2,18 +2,18 @@ define([
'jquery', 'jquery',
'/common/modes.js', '/common/modes.js',
'/common/themes.js', '/common/themes.js',
'/bower_components/file-saver/FileSaver.min.js' '/bower_components/file-saver/FileSaver.min.js'
], function ($, Modes, Themes) { ], function ($, Modes, Themes) {
var saveAs = window.saveAs; var saveAs = window.saveAs;
var module = {}; var module = {};
module.create = function (CMeditor, ifrw, Cryptpad) { module.create = function (ifrw, Cryptpad, defaultMode, CMeditor) {
var exp = {}; var exp = {};
var Messages = Cryptpad.Messages; var Messages = Cryptpad.Messages;
var CodeMirror = exp.CodeMirror = CMeditor; var CodeMirror = exp.CodeMirror = CMeditor;
CodeMirror.modeURL = "/bower_components/codemirror/mode/%N/%N.js"; CodeMirror.modeURL = "cm/mode/%N/%N";
var $pad = $('#pad-iframe'); var $pad = $('#pad-iframe');
var $textarea = exp.$textarea = $pad.contents().find('#editor1'); var $textarea = exp.$textarea = $pad.contents().find('#editor1');
@ -43,14 +43,16 @@ define([
extraKeys: {"Shift-Ctrl-R": undefined}, extraKeys: {"Shift-Ctrl-R": undefined},
foldGutter: true, foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
mode: "javascript", mode: defaultMode || "javascript",
readOnly: true readOnly: true
}); });
editor.setValue(Messages.codeInitialState); editor.setValue(Messages.codeInitialState);
var setMode = exp.setMode = function (mode, cb) { var setMode = exp.setMode = function (mode, cb) {
exp.highlightMode = mode; exp.highlightMode = mode;
if (mode !== "text") { CMeditor.autoLoadMode(editor, mode); } if (mode !== "text") {
CMeditor.autoLoadMode(editor, mode);
}
editor.setOption('mode', mode); editor.setOption('mode', mode);
if (exp.$language) { if (exp.$language) {
var name = exp.$language.find('a[data-value="' + mode + '"]').text() || undefined; var name = exp.$language.find('a[data-value="' + mode + '"]').text() || undefined;

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

@ -49,8 +49,10 @@ define(function () {
exp.myUserName = myUserNameTemp; exp.myUserName = myUserNameTemp;
myData = {}; myData = {};
myData[exp.myNetfluxId] = { myData[exp.myNetfluxId] = {
name: exp.myUserName, name: exp.myUserName,
uid: Cryptpad.getUid(), uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl()
}; };
addToUserData(myData); addToUserData(myData);
Cryptpad.setAttribute('username', exp.myUserName, function (err) { Cryptpad.setAttribute('username', exp.myUserName, function (err) {
@ -78,6 +80,8 @@ define(function () {
myData[exp.myNetfluxId] = { myData[exp.myNetfluxId] = {
name: "", name: "",
uid: Cryptpad.getUid(), uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl()
}; };
addToUserData(myData); addToUserData(myData);
onLocal(); onLocal();

@ -16,9 +16,10 @@ define([
'/common/clipboard.js', '/common/clipboard.js',
'/common/pinpad.js', '/common/pinpad.js',
'/customize/application_config.js' '/customize/application_config.js',
'/common/media-tag.js',
], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata, ], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata,
CodeMirror, Files, FileCrypto, Clipboard, Pinpad, AppConfig) { CodeMirror, Files, FileCrypto, Clipboard, Pinpad, AppConfig, MediaTag) {
/* This file exposes functionality which is specific to Cryptpad, but not to /* This file exposes functionality which is specific to Cryptpad, but not to
any particular pad type. This includes functions for committing metadata any particular pad type. This includes functions for committing metadata
@ -49,6 +50,7 @@ define([
var store; var store;
var rpc; var rpc;
var anon_rpc;
// import UI elements // import UI elements
common.findCancelButton = UI.findCancelButton; common.findCancelButton = UI.findCancelButton;
@ -76,7 +78,7 @@ define([
var deduplicateString = common.deduplicateString = Util.deduplicateString; var deduplicateString = common.deduplicateString = Util.deduplicateString;
common.uint8ArrayToHex = Util.uint8ArrayToHex; common.uint8ArrayToHex = Util.uint8ArrayToHex;
common.replaceHash = Util.replaceHash; common.replaceHash = Util.replaceHash;
var getHash = common.getHash = Util.getHash; common.getHash = Util.getHash;
common.fixFileName = Util.fixFileName; common.fixFileName = Util.fixFileName;
common.bytesToMegabytes = Util.bytesToMegabytes; common.bytesToMegabytes = Util.bytesToMegabytes;
common.bytesToKilobytes = Util.bytesToKilobytes; common.bytesToKilobytes = Util.bytesToKilobytes;
@ -146,6 +148,16 @@ define([
} }
return; return;
}; };
common.getProfileUrl = function () {
if (store && store.getProfile()) {
return store.getProfile().view;
}
};
common.getAvatarUrl = function () {
if (store && store.getProfile()) {
return store.getProfile().avatar;
}
};
var feedback = common.feedback = function (action, force) { var feedback = common.feedback = function (action, force) {
if (force !== true) { if (force !== true) {
@ -414,9 +426,8 @@ define([
// STORAGE // STORAGE
common.setPadAttribute = function (attr, value, cb) { common.setPadAttribute = function (attr, value, cb) {
getStore().setDrive([getHash(), attr].join('.'), value, function (err, data) { var href = getRelativeHref(window.location.href);
cb(err, data); getStore().setPadAttribute(href, attr, value, cb);
});
}; };
common.setAttribute = function (attr, value, cb) { common.setAttribute = function (attr, value, cb) {
getStore().set(["cryptpad", attr].join('.'), value, function (err, data) { getStore().set(["cryptpad", attr].join('.'), value, function (err, data) {
@ -429,9 +440,8 @@ define([
// STORAGE // STORAGE
common.getPadAttribute = function (attr, cb) { common.getPadAttribute = function (attr, cb) {
getStore().getDrive([getHash(), attr].join('.'), function (err, data) { var href = getRelativeHref(window.location.href);
cb(err, data); getStore().getPadAttribute(href, attr, cb);
});
}; };
common.getAttribute = function (attr, cb) { common.getAttribute = function (attr, cb) {
getStore().get(["cryptpad", attr].join('.'), function (err, data) { getStore().get(["cryptpad", attr].join('.'), function (err, data) {
@ -777,11 +787,32 @@ define([
}; };
common.getFileSize = function (href, cb) { common.getFileSize = function (href, cb) {
if (!pinsReady()) { return void cb('RPC_NOT_READY'); } if (!anon_rpc) { return void cb('ANON_RPC_NOT_READY'); }
//if (!pinsReady()) { return void cb('RPC_NOT_READY'); }
var channelId = Hash.hrefToHexChannelId(href); var channelId = Hash.hrefToHexChannelId(href);
rpc.getFileSize(channelId, function (e, bytes) { anon_rpc.send("GET_FILE_SIZE", channelId, function (e, response) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
cb(void 0, bytes); if (response && response.length && typeof(response[0]) === 'number') {
return void cb(void 0, response[0]);
} else {
cb('INVALID_RESPONSE');
}
});
};
common.getMultipleFileSize = function (files, cb) {
if (!anon_rpc) { return void cb('ANON_RPC_NOT_READY'); }
if (!Array.isArray(files)) {
return void setTimeout(function () { cb('INVALID_FILE_LIST'); });
}
anon_rpc.send('GET_MULTIPLE_FILE_SIZE', files, function (e, res) {
if (e) { return cb(e); }
if (res && res.length && typeof(res[0]) === 'object') {
cb(void 0, res[0]);
} else {
cb('UNEXPECTED_RESPONSE');
}
}); });
}; };
@ -800,7 +831,9 @@ define([
if (!pinsReady()) { return void cb('RPC_NOT_READY'); } if (!pinsReady()) { return void cb('RPC_NOT_READY'); }
var account = common.account; var account = common.account;
if (typeof(account.limit) !== 'number' ||
var ALWAYS_REVALIDATE = true;
if (ALWAYS_REVALIDATE || typeof(account.limit) !== 'number' ||
typeof(account.plan) !== 'string' || typeof(account.plan) !== 'string' ||
typeof(account.note) !== 'string') { typeof(account.note) !== 'string') {
return void rpc.getLimit(function (e, limit, plan, note) { return void rpc.getLimit(function (e, limit, plan, note) {
@ -863,7 +896,6 @@ define([
var $container = $('<span>', {'class':'limit-container'}); var $container = $('<span>', {'class':'limit-container'});
var todo; var todo;
var updateUsage = window.updateUsage = common.notAgainForAnother(function () { var updateUsage = window.updateUsage = common.notAgainForAnother(function () {
console.log("updating usage bar");
common.getPinnedUsage(todo); common.getPinnedUsage(todo);
}, LIMIT_REFRESH_RATE); }, LIMIT_REFRESH_RATE);
@ -933,21 +965,12 @@ define([
}; };
setInterval(function () { setInterval(function () {
var t = updateUsage(); updateUsage();
if (t) {
console.log("usage already updated. eligible for refresh in %sms", t);
}
}, LIMIT_REFRESH_RATE * 3); }, LIMIT_REFRESH_RATE * 3);
updateUsage(); updateUsage();
getProxy().on('change', ['drive'], function () { getProxy().on('change', ['drive'], function () {
var t = updateUsage(); updateUsage();
if (t) {
console.log("usage bar update throttled due to overuse." +
" Eligible for update in %sms", t);
} else {
console.log("usage bar updated");
}
}); });
cb(null, $container); cb(null, $container);
}; };
@ -1162,67 +1185,132 @@ define([
return button; return button;
}; };
var emoji_patt = /([\uD800-\uDBFF][\uDC00-\uDFFF])/;
var isEmoji = function (str) {
return emoji_patt.test(str);
};
var emojiStringToArray = function (str) {
var split = str.split(emoji_patt);
var arr = [];
for (var i=0; i<split.length; i++) {
var char = split[i];
if (char !== "") {
arr.push(char);
}
}
return arr;
};
var getFirstEmojiOrCharacter = function (str) {
if (!str || !str.trim()) { return '?'; }
var emojis = emojiStringToArray(str);
return isEmoji(emojis[0])? emojis[0]: str[0];
};
$(window.document).on('decryption', function (e) {
var decrypted = e.originalEvent;
if (decrypted.callback) {
var cb = decrypted.callback;
cb(function (mediaObject) {
if (mediaObject.type !== 'download') { return; }
var root = mediaObject.rootElement;
if (!root) { return; }
var metadata = decrypted.metadata;
var title = '';
var size = 0;
if (metadata && metadata.name) {
title = metadata.name;
}
if (decrypted.blob) {
size = decrypted.blob.size;
}
var sizeMb = common.bytesToMegabytes(size);
var $btn = $(root).find('button');
$btn.addClass('btn btn-success')
.attr('type', 'download')
.html(function () {
var text = Messages.download_mt_button + '<br>';
if (title) {
text += '<b>' + common.fixHTML(title) + '</b><br>';
}
if (size) {
text += '<em>' + Messages._getKey('formattedMB', [sizeMb]) + '</em>';
}
return text;
});
});
}
});
common.avatarAllowedTypes = [ common.avatarAllowedTypes = [
'image/png', 'image/png',
'image/jpeg', 'image/jpeg',
'image/jpg', 'image/jpg',
'image/gif', 'image/gif',
]; ];
common.displayAvatar = function ($container, href) { common.displayAvatar = function ($container, href, name, cb) {
var MutationObserver = window.MutationObserver; var MutationObserver = window.MutationObserver;
$container.html(''); var displayDefault = function () {
if (href) { var text = getFirstEmojiOrCharacter(name);
var parsed = common.parsePadUrl(href); var $avatar = $('<span>', {'class': 'default'}).text(text);
var secret = common.getSecrets('file', parsed.hash); $container.append($avatar);
if (secret.keys && secret.channel) { if (cb) { cb(); }
var cryptKey = secret.keys && secret.keys.fileKeyStr; };
var hexFileName = common.base64ToHex(secret.channel);
var src = common.getBlobPathFromHex(hexFileName); if (!href) { return void displayDefault(); }
common.getFileSize(href, function (e, data) { var parsed = common.parsePadUrl(href);
if (e) { return void console.error(e); } var secret = common.getSecrets('file', parsed.hash);
if (typeof data !== "number") { return; } if (secret.keys && secret.channel) {
if (common.bytesToMegabytes(data) > 0.5) { return; } var cryptKey = secret.keys && secret.keys.fileKeyStr;
var $img = $('<media-tag>').appendTo($container); var hexFileName = common.base64ToHex(secret.channel);
$img.attr('src', src); var src = common.getBlobPathFromHex(hexFileName);
$img.attr('data-crypto-key', 'cryptpad:' + cryptKey); common.getFileSize(href, function (e, data) {
require(['/common/media-tag.js'], function (MediaTag) { if (e) {
MediaTag.CryptoFilter.setAllowedMediaTypes(common.avatarAllowedTypes); displayDefault();
MediaTag($img[0]); return void console.error(e);
var observer = new MutationObserver(function(mutations) { }
mutations.forEach(function(mutation) { if (typeof data !== "number") { return void displayDefault(); }
if (mutation.type === 'childList' && mutation.addedNodes.length) { if (common.bytesToMegabytes(data) > 0.5) { return void displayDefault(); }
console.log(mutation); var $img = $('<media-tag>').appendTo($container);
if (mutation.addedNodes.length > 1 || $img.attr('src', src);
mutation.addedNodes[0].nodeName !== 'IMG') { $img.attr('data-crypto-key', 'cryptpad:' + cryptKey);
$img.remove(); MediaTag($img[0]);
return; var observer = new MutationObserver(function(mutations) {
//TODO display default avatar mutations.forEach(function(mutation) {
} if (mutation.type === 'childList' && mutation.addedNodes.length) {
var $image = $img.find('img'); if (mutation.addedNodes.length > 1 ||
var onLoad = function () { mutation.addedNodes[0].nodeName !== 'IMG') {
var w = $image.width(); $img.remove();
var h = $image.height(); return void displayDefault();
if (w>h) { }
$image.css('max-height', '100%'); var $image = $img.find('img');
$img.css('flex-direction', 'row'); var onLoad = function () {
return; var w = $image.width();
} var h = $image.height();
$image.css('max-width', '100%'); if (w>h) {
$img.css('flex-direction', 'column'); $image.css('max-height', '100%');
}; $img.css('flex-direction', 'row');
if ($image[0].complete) { onLoad(); } if (cb) { cb($img); }
$image.on('load', onLoad); return;
} }
}); $image.css('max-width', '100%');
}); $img.css('flex-direction', 'column');
observer.observe($img[0], { if (cb) { cb($img); }
attributes: false, };
childList: true, if ($image[0].complete) { onLoad(); }
characterData: false $image.on('load', onLoad);
}); }
});
observer.observe($img[0], {
attributes: false,
childList: true,
characterData: false
}); });
}); });
} });
} }
}; };
@ -1301,7 +1389,7 @@ define([
setActive($val); setActive($val);
$innerblock.scrollTop($val.position().top + $innerblock.scrollTop()); $innerblock.scrollTop($val.position().top + $innerblock.scrollTop());
} }
if (config.feedback) { common.feedback(config.feedback); } if (config.feedback && store) { common.feedback(config.feedback); }
}; };
$container.click(function (e) { $container.click(function (e) {
@ -1457,6 +1545,13 @@ define([
content: Messages.user_rename content: Messages.user_rename
}); });
} }
if (account) {
options.push({
tag: 'a',
attributes: {'class': 'profile'},
content: Messages.profileButton
});
}
if (parsed && (!parsed.type || parsed.type !== 'settings')) { if (parsed && (!parsed.type || parsed.type !== 'settings')) {
options.push({ options.push({
tag: 'a', tag: 'a',
@ -1516,6 +1611,13 @@ define([
window.location.href = '/settings/'; window.location.href = '/settings/';
} }
}); });
$userAdmin.find('a.profile').click(function () {
if (parsed && parsed.type) {
window.open('/profile/');
} else {
window.location.href = '/profile/';
}
});
$userAdmin.find('a.login').click(function () { $userAdmin.find('a.login').click(function () {
if (window.location.pathname !== "/") { if (window.location.pathname !== "/") {
sessionStorage.redirectTo = window.location.href; sessionStorage.redirectTo = window.location.href;
@ -1666,6 +1768,21 @@ define([
console.log('pinning disabled'); console.log('pinning disabled');
} }
block++;
require([
'/common/rpc.js',
], function (Rpc) {
Rpc.createAnonymous(network, function (e, call) {
if (e) {
console.error(e);
return void cb();
}
anon_rpc = common.anon_rpc = env.anon_rpc = call;
cb();
});
});
// Everything's ready, continue... // Everything's ready, continue...
if($('#pad-iframe').length) { if($('#pad-iframe').length) {
block++; block++;

@ -171,11 +171,6 @@ define([
} }
}; };
$(window.document).on('decryption', function (e) {
var decrypted = e.originalEvent;
if (decrypted.callback) { decrypted.callback(); }
});
return DiffMd; return DiffMd;
}); });

@ -60,6 +60,9 @@ define([
cb(void 0, res); cb(void 0, res);
}; };
ret.setPadAttribute = filesOp.setAttribute;
ret.getPadAttribute = filesOp.getAttribute;
ret.getDrive = function (key, cb) { ret.getDrive = function (key, cb) {
cb(void 0, storeObj.drive[key]); cb(void 0, storeObj.drive[key]);
}; };
@ -211,6 +214,10 @@ define([
if (typeof(n) !== "string") { return; } if (typeof(n) !== "string") { return; }
Cryptpad.changeDisplayName(n); Cryptpad.changeDisplayName(n);
}); });
proxy.on('change', ['profile'], function () {
// Trigger userlist update when the avatar has changed
Cryptpad.changeDisplayName(proxy[Cryptpad.displayNameKey]);
});
proxy.on('change', [tokenKey], function () { proxy.on('change', [tokenKey], function () {
console.log('wut'); console.log('wut');
var localToken = tryParsing(localStorage.getItem(tokenKey)); var localToken = tryParsing(localStorage.getItem(tokenKey));

File diff suppressed because one or more lines are too long

@ -86,7 +86,7 @@ define([
exp.anonDriveIntoUser = function (proxy, cb) { exp.anonDriveIntoUser = function (proxy, cb) {
// Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb // Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb
if (!localStorage.FS_hash || !Cryptpad.isLoggedIn()) { if (!localStorage.FS_hash || !Cryptpad.isLoggedIn()) {
if (typeof(cb) === "function") { cb(); } if (typeof(cb) === "function") { return void cb(); }
} }
// Get the content of FS_hash and then merge the objects, remove the migration key and cb // Get the content of FS_hash and then merge the objects, remove the migration key and cb
var todo = function (err, doc) { var todo = function (err, doc) {

@ -54,11 +54,9 @@ define(function () {
"jsx jsx .jsx", "jsx jsx .jsx",
"julia julia", "julia julia",
"livescript livescript", "livescript livescript",
"loadmode.js loadmode.js",
"lua lua", "lua lua",
"markdown markdown .md", "markdown markdown .md",
"mathematica mathematica", "mathematica mathematica",
"meta.js meta.js",
"mirc mirc", "mirc mirc",
"mllike mllike", "mllike mllike",
"modelica modelica", "modelica modelica",

@ -100,21 +100,6 @@ define([
}); });
}; };
// take a list of channels and return a dictionary of their sizes
exp.getMultipleFileSize = function (files, cb) {
if (!Array.isArray(files)) {
return window.setTimeout(function () {
cb('[TypeError] pin expects an array');
});
}
rpc.send('GET_MULTIPLE_FILE_SIZE', files, function (e, res) {
if (e) { return void cb(e); }
if (typeof(res) !== 'object') {
return void cb('INVALID_RESPONSE');
}
});
};
// get the combined size of all channels (in bytes) for all the // get the combined size of all channels (in bytes) for all the
// channels which the server has pinned for your publicKey // channels which the server has pinned for your publicKey
exp.getFileListSize = function (cb) { exp.getFileListSize = function (cb) {

@ -99,7 +99,12 @@ types of messages:
delete ctx.pending[txid]; delete ctx.pending[txid];
return; return;
} }
console.error("received message [%s] for txid[%s] with no callback", msg, txid);
// HACK to hide messages from the anon rpc
if (parsed.length !== 4) {
console.log(parsed);
console.error("received message [%s] for txid[%s] with no callback", msg, txid);
}
}; };
var create = function (network, edPrivateKey, edPublicKey, cb) { var create = function (network, edPrivateKey, edPublicKey, cb) {
@ -217,5 +222,104 @@ types of messages:
}); });
}; };
return { create: create }; var onAnonMsg = function (ctx, msg) {
var parsed = parse(msg);
if (!parsed) {
return void console.error(new Error('could not parse message: %s', msg));
}
// RPC messages are always arrays.
if (!Array.isArray(parsed)) { return; }
var txid = parsed[0];
// txid must be a string, or this message is not meant for us
if (typeof(txid) !== 'string') { return; }
var pending = ctx.pending[txid];
if (!(parsed && parsed.slice)) {
// RPC responses are arrays. this message isn't meant for us.
return;
}
if (/FULL_HISTORY/.test(parsed[0])) { return; }
var response = parsed.slice(2);
if (typeof(pending) === 'function') {
if (parsed[1] === 'ERROR') {
pending(parsed[2]);
delete ctx.pending[txid];
return;
}
pending(void 0, response);
// if successful, delete the callback...
delete ctx.pending[txid];
return;
}
// HACK: filter out ugly messages we don't care about
if (typeof(msg) !== 'string') {
console.error("received message [%s] for txid[%s] with no callback", msg, txid);
}
};
var createAnonymous = function (network, cb) {
var ctx = {
network: network,
timeouts: {}, // timeouts
pending: {}, // callbacks
cookie: null,
connected: true,
};
var send = ctx.send = function (type, msg, cb) {
if (!ctx.connected) {
return void window.setTimeout(function () {
cb('DISCONNECTED');
});
}
// construct an unsigned message...
var data = [type, msg];
// [sig, edPublicKey, cookie, type, msg]
return sendMsg(ctx, data, cb);
};
ctx.resend = function (txid) {
var pending = ctx.pending[txid];
if (pending.called) {
console.error("[%s] called too many times", txid);
return true;
}
pending.called++;
try {
return ctx.network.sendto(ctx.network.historyKeeper,
JSON.stringify([txid, pending.data]));
} catch (e) {
console.log("failed to resend");
console.error(e);
}
};
network.on('message', function (msg) {
onAnonMsg(ctx, msg);
});
network.on('disconnect', function () {
ctx.connected = false;
});
network.on('reconnect', function () {
ctx.connected = true;
});
cb(void 0, {
send: send
});
};
return { create: create, createAnonymous: createAnonymous };
}); });

@ -81,15 +81,31 @@ define([
var $rightside = $toolbar.find('.'+RIGHTSIDE_CLS); var $rightside = $toolbar.find('.'+RIGHTSIDE_CLS);
if (!config.hideDrawer) { if (!config.hideDrawer) {
var $drawerContent = $('<div>', {'class': DRAWER_CLS}).appendTo($rightside).hide(); var $drawerContent = $('<div>', {
'class': DRAWER_CLS,// + ' dropdown-bar-content cryptpad-dropdown'
'tabindex': 1
}).appendTo($rightside).hide();
var $drawer = Cryptpad.createButton('more', true).appendTo($rightside); var $drawer = Cryptpad.createButton('more', true).appendTo($rightside);
$drawer.click(function () { $drawer.click(function () {
$drawerContent.toggle(); $drawerContent.toggle();
$drawer.removeClass('active'); $drawer.removeClass('active');
if ($drawerContent.is(':visible')) { if ($drawerContent.is(':visible')) {
$drawer.addClass('active'); $drawer.addClass('active');
$drawerContent.focus();
} }
}); });
var onBlur = function (e) {
if (e.relatedTarget) {
if ($(e.relatedTarget).is('.drawer-button')) { return; }
if ($(e.relatedTarget).parents('.'+DRAWER_CLS).length) {
$(e.relatedTarget).blur(onBlur);
return;
}
}
$drawer.removeClass('active');
$drawerContent.hide();
};
$drawerContent.blur(onBlur);
} }
// The 'notitle' class removes the line added for the title with a small screen // The 'notitle' class removes the line added for the title with a small screen
@ -149,6 +165,7 @@ define([
return $.inArray(i, b) > -1; return $.inArray(i, b) > -1;
}); });
}; };
var avatars = {};
var updateUserList = function (toolbar, config) { var updateUserList = function (toolbar, config) {
// Make sure the elements are displayed // Make sure the elements are displayed
var $userButtons = toolbar.userlist; var $userButtons = toolbar.userlist;
@ -189,7 +206,25 @@ define([
// Editors // Editors
editUsersNames.forEach(function (data) { editUsersNames.forEach(function (data) {
var name = data.name || Messages.anonymous; var name = data.name || Messages.anonymous;
var $span = $('<span>', {'title': name}).text(name); var $name = $('<span>', {'class': 'name'}).text(name);
var $span = $('<span>', {'title': name});
if (data.profile) {
$span.addClass('clickable');
$span.click(function () {
window.open('/profile/#' + data.profile);
});
}
if (data.avatar && avatars[data.avatar]) {
$span.append(avatars[data.avatar]);
$span.append($name);
} else {
Cryptpad.displayAvatar($span, data.avatar, name, function ($img) {
if (data.avatar && $img) {
avatars[data.avatar] = $img[0].outerHTML;
}
$span.append($name);
});
}
$span.data('uid', data.uid); $span.data('uid', data.uid);
$editUsersList.append($span); $editUsersList.append($span);
}); });
@ -197,9 +232,9 @@ define([
// Viewers // Viewers
if (numberOfViewUsers > 0) { if (numberOfViewUsers > 0) {
var viewText = '<span class="viewer">'; var viewText = '<div class="viewer">';
var viewerText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer; var viewerText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer;
viewText += numberOfViewUsers + ' ' + viewerText + '</span>'; viewText += numberOfViewUsers + ' ' + viewerText + '</div>';
$editUsers.append(viewText); $editUsers.append(viewText);
} }
@ -299,6 +334,11 @@ define([
if ($content.is(':visible')) { return void show(); } if ($content.is(':visible')) { return void show(); }
hide(); hide();
}); });
$(window).on('resize', function () {
mobile = $('body').width() <= 600;
var h = $ck.is(':visible') ? -$ck.height() : 0;
$content.css('margin-top', h+'px');
});
$closeIcon.click(hide); $closeIcon.click(hide);
$button.click(function () { $button.click(function () {
var visible = $content.is(':visible'); var visible = $content.is(':visible');
@ -553,17 +593,17 @@ define([
// We need to override the "a" tag action here because it is inside the iframe! // We need to override the "a" tag action here because it is inside the iframe!
var $aTag = $('<a>', { var $aTag = $('<a>', {
href: "/", href: "/drive/",
title: Messages.header_logoTitle, title: Messages.header_logoTitle,
'class': "cryptpad-logo fa fa-hdd-o" 'class': "cryptpad-logo fa fa-hdd-o"
}); });
var onClick = function (e) { var onClick = function (e) {
e.preventDefault(); e.preventDefault();
if (e.ctrlKey) { if (e.ctrlKey) {
window.open('/drive'); window.open('/drive/');
return; return;
} }
window.location = "/drive"; window.location = "/drive/";
}; };
var onContext = function (e) { e.stopPropagation(); }; var onContext = function (e) { e.stopPropagation(); };

@ -132,6 +132,21 @@ define([
if (type === 'name') { return data.filename; } if (type === 'name') { return data.filename; }
return data.filename || data.title || NEW_FILE_NAME; return data.filename || data.title || NEW_FILE_NAME;
}; };
exp.getAttribute = function (href, attr, cb) {
cb = cb || $.noop;
var id = exp.getIdFromHref(href);
if (!id) { return void cb(null, undefined); }
var data = getFileData(id);
cb(null, data[attr]);
};
exp.setAttribute = function (href, attr, value, cb) {
cb = cb || $.noop;
var id = exp.getIdFromHref(href);
if (!id) { return void cb("E_INVAL_HREF"); }
if (!attr || !attr.trim()) { return void cb("E_INVAL_ATTR"); }
var data = getFileData(id);
data[attr] = value;
};
// PATHS // PATHS
@ -970,6 +985,20 @@ define([
us.splice(idx, 1); us.splice(idx, 1);
}); });
}; };
var migrateAttributes = function (el, id, parsed) {
// Migrate old pad attributes
['userid', 'previewMode'].forEach(function (attr) {
var key = parsed.hash + '.' + attr;
var key2 = parsed.hash.slice(0,-1) + '.' + attr;// old pads not ending with /
if (files[key] || files[key2]) {
debug("Migrating pad attribute", attr, "for pad", id);
el[attr] = files[key] || files[key2];
}
delete files[key];
delete files[key2];
});
// Migration done
};
var fixFilesData = function () { var fixFilesData = function () {
if (typeof files[FILES_DATA] !== "object") { debug("OLD_FILES_DATA was not an object"); files[FILES_DATA] = {}; } if (typeof files[FILES_DATA] !== "object") { debug("OLD_FILES_DATA was not an object"); files[FILES_DATA] = {}; }
var fd = files[FILES_DATA]; var fd = files[FILES_DATA];
@ -989,6 +1018,15 @@ define([
toClean.push(id); toClean.push(id);
continue; continue;
} }
var parsed = Cryptpad.parsePadUrl(el.href);
if (!parsed.hash) {
debug("Removing an element in filesData with a invalid href.", el);
toClean.push(id);
continue;
}
migrateAttributes(el, id, parsed);
if ((Cryptpad.isLoggedIn() || config.testMode) && rootFiles.indexOf(id) === -1) { if ((Cryptpad.isLoggedIn() || config.testMode) && rootFiles.indexOf(id) === -1) {
debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el); debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el);
var newName = Cryptpad.createChannelId(); var newName = Cryptpad.createChannelId();
@ -1001,12 +1039,19 @@ define([
}); });
}; };
var fixDrive = function () {
Object.keys(files).forEach(function (key) {
if (key.slice(0,1) === '/') { delete files[key]; }
});
};
fixRoot(); fixRoot();
fixTrashRoot(); fixTrashRoot();
if (!workgroup) { if (!workgroup) {
fixTemplate(); fixTemplate();
fixFilesData(); fixFilesData();
} }
fixDrive();
if (JSON.stringify(files) !== before) { if (JSON.stringify(files) !== before) {
debug("Your file system was corrupted. It has been cleaned so that the pads you visit can be stored safely"); debug("Your file system was corrupted. It has been cleaned so that the pads you visit can be stored safely");

@ -621,7 +621,7 @@ span {
margin: 0; margin: 0;
} }
button { button {
height: 100%; height: 32px;
padding: 0 10px; padding: 0 10px;
border: none; border: none;
border-radius: 0; border-radius: 0;

@ -1170,7 +1170,7 @@ define([
var element = filesOp.find(newPath); var element = filesOp.find(newPath);
var $icon = !isFolder ? getFileIcon(element) : undefined; var $icon = !isFolder ? getFileIcon(element) : undefined;
var ro = filesOp.isReadOnlyFile(element); var ro = filesOp.isReadOnlyFile(element);
// ro undefined mens it's an old hash which doesn't support read-only // ro undefined means it's an old hash which doesn't support read-only
var roClass = typeof(ro) === 'undefined' ? ' noreadonly' : ro ? ' readonly' : ''; var roClass = typeof(ro) === 'undefined' ? ' noreadonly' : ro ? ' readonly' : '';
var liClass = 'file-item file-element element' + roClass; var liClass = 'file-item file-element element' + roClass;
if (isFolder) { if (isFolder) {
@ -2140,6 +2140,7 @@ define([
$trashContextMenu.hide(); $trashContextMenu.hide();
$contentContextMenu.hide(); $contentContextMenu.hide();
$defaultContextMenu.hide(); $defaultContextMenu.hide();
$iframe.find('.cryptpad-dropdown').hide();
}; };
var stringifyPath = function (path) { var stringifyPath = function (path) {

@ -242,6 +242,18 @@ define([
fo.migrate(todo); fo.migrate(todo);
}, "DRIVE4: migration and fixFiles with a pad in trash not root"); }, "DRIVE4: migration and fixFiles with a pad in trash not root");
// Pad attributes migration
assert(function (cb) {
console.log('START PAD ATTRIBUTES');
var files = JSON.parse(JSON.stringify(example));
files[href1.slice(6) + '.userid'] = 'value';
files[href1.slice(6) + '.previewMode'] = true;
var fo = FO.init(files, config);
fo.fixFiles();
return cb(files.filesData[id1].userid === 'value'
&& files.filesData[id1].previewMode);
}, "PAD ATTRIBUTES");
// userObject Tests // userObject Tests
// UTILS // UTILS

@ -7,12 +7,14 @@ define([
'/common/visible.js', '/common/visible.js',
'/common/notify.js', '/common/notify.js',
'/file/file-crypto.js', '/file/file-crypto.js',
'/common/media-tag.js',
'/bower_components/file-saver/FileSaver.min.js', '/bower_components/file-saver/FileSaver.min.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less/cryptpad.less', 'less!/customize/src/less/cryptpad.less',
], function ($, Crypto, realtimeInput, Toolbar, Cryptpad, Visible, Notify, FileCrypto) { ], function ($, Crypto, realtimeInput, Toolbar, Cryptpad, Visible, Notify, FileCrypto, MediaTag) {
var Messages = Cryptpad.Messages; var Messages = Cryptpad.Messages;
var saveAs = window.saveAs; var saveAs = window.saveAs;
var Nacl = window.nacl; var Nacl = window.nacl;
@ -91,7 +93,9 @@ define([
$(window.document).on('decryption', function (e) { $(window.document).on('decryption', function (e) {
var decrypted = e.originalEvent; var decrypted = e.originalEvent;
if (decrypted.callback) { decrypted.callback(); } if (decrypted.callback) {
decrypted.callback();
}
console.log(decrypted); console.log(decrypted);
$dlview.show(); $dlview.show();
@ -131,32 +135,30 @@ define([
console.log(progress.percent); console.log(progress.percent);
}); });
require(['/common/media-tag.js'], function (MediaTag) { /**
/** * Allowed mime types that have to be set for a rendering after a decryption.
* Allowed mime types that have to be set for a rendering after a decryption. *
* * @type {Array}
* @type {Array} */
*/ var allowedMediaTypes = [
var allowedMediaTypes = [ 'image/png',
'image/png', 'image/jpeg',
'image/jpeg', 'image/jpg',
'image/jpg', 'image/gif',
'image/gif', 'audio/mp3',
'audio/mp3', 'audio/ogg',
'audio/ogg', 'audio/wav',
'audio/wav', 'audio/webm',
'audio/webm', 'video/mp4',
'video/mp4', 'video/ogg',
'video/ogg', 'video/webm',
'video/webm', 'application/pdf',
'application/pdf', 'application/dash+xml',
'application/dash+xml', 'download'
'download' ];
]; MediaTag.CryptoFilter.setAllowedMediaTypes(allowedMediaTypes);
MediaTag.CryptoFilter.setAllowedMediaTypes(allowedMediaTypes);
MediaTag($mt[0]);
MediaTag($mt[0]);
});
}; };
var todoBigFile = function (sizeMb) { var todoBigFile = function (sizeMb) {
@ -181,9 +183,6 @@ define([
}; };
Cryptpad.getFileSize(window.location.href, function (e, data) { Cryptpad.getFileSize(window.location.href, function (e, data) {
if (e) { if (e) {
// TODO when GET_FILE_SIZE is made unauthenticated
// you won't need to handle this error (there won't be one)
if (e === 'RPC_NOT_READY') { return todoBigFile(); }
return void Cryptpad.errorLoadingScreen(e); return void Cryptpad.errorLoadingScreen(e);
} }
var size = Cryptpad.bytesToMegabytes(data); var size = Cryptpad.bytesToMegabytes(data);

@ -14,6 +14,7 @@
#cke_1_top { #cke_1_top {
overflow: visible; overflow: visible;
padding: 0px; padding: 0px;
display: flex;
} }
#cke_1_toolbox { #cke_1_toolbox {
display: inline-block; display: inline-block;

@ -570,16 +570,10 @@ define([
// this should only ever get called once, when the chain syncs // this should only ever get called once, when the chain syncs
realtimeOptions.onReady = function (info) { realtimeOptions.onReady = function (info) {
if (!module.isMaximized) { if (!module.isMaximized) {
editor.execCommand('maximize');
module.isMaximized = true; module.isMaximized = true;
// We have to call it 3 times in Safari $iframe.find('iframe.cke_wysiwyg_frame').css('width', '');
// in order to have the editor fully maximized -_- $iframe.find('iframe.cke_wysiwyg_frame').css('height', '');
if ((''+window.navigator.vendor).indexOf('Apple') !== -1) {
editor.execCommand('maximize');
editor.execCommand('maximize');
}
} }
// editor.execCommand('maximize') removes all the classes from the body tag
$iframe.find('body').addClass('app-pad'); $iframe.find('body').addClass('app-pad');
if (module.realtime !== info.realtime) { if (module.realtime !== info.realtime) {
@ -727,12 +721,11 @@ define([
if (Ckeditor) { if (Ckeditor) {
// mobile configuration // mobile configuration
Ckeditor.config.toolbarCanCollapse = true; Ckeditor.config.toolbarCanCollapse = true;
Ckeditor.config.height = '72vh';
if (screen.height < 800) { if (screen.height < 800) {
Ckeditor.config.toolbarStartupExpanded = false; Ckeditor.config.toolbarStartupExpanded = false;
$('meta[name=viewport]').attr('content', 'width=device-width, initial-scale=1.0, user-scalable=no'); $('meta[name=viewport]').attr('content', 'width=device-width, initial-scale=1.0, user-scalable=no');
} else { } else {
$('meta[name=viewport]').attr('content', 'width=device-width, initial-scale=1.0, user-scalable=yes'); $('meta[name=viewport]').attr('content', 'width=device-width, initial-scale=1.0, user-scalable=yes');
} }
second(Ckeditor); second(Ckeditor);
} else { } else {

@ -552,11 +552,19 @@ var ready = function (info, userid, readOnly) {
} else { } else {
APP.proxy.info.defaultTitle = Title.defaultTitle; APP.proxy.info.defaultTitle = Title.defaultTitle;
} }
var andThen = function () {
if (readOnly) { return; }
Cryptpad.setPadAttribute('userid', userid, function (e) {
if (e) { console.error(e); }
});
};
if (Cryptpad.initialName && !APP.proxy.info.title) { if (Cryptpad.initialName && !APP.proxy.info.title) {
APP.proxy.info.title = Cryptpad.initialName; APP.proxy.info.title = Cryptpad.initialName;
Title.updateTitle(Cryptpad.initialName); Title.updateTitle(Cryptpad.initialName, null, andThen);
} else { } else {
Title.updateTitle(APP.proxy.info.title || Title.defaultTitle); Title.updateTitle(APP.proxy.info.title || Title.defaultTitle, null, andThen);
} }
// Description // Description
@ -621,6 +629,7 @@ var ready = function (info, userid, readOnly) {
} else { } else {
publish(true); publish(true);
} }
Cryptpad.removeLoadingScreen(); Cryptpad.removeLoadingScreen();
if (readOnly) { return; } if (readOnly) { return; }
@ -760,10 +769,7 @@ var create = function (info) {
if (e) { console.error(e); } if (e) { console.error(e); }
if (!userid) { userid = Render.coluid(); } if (!userid) { userid = Render.coluid(); }
APP.userid = userid; APP.userid = userid;
Cryptpad.setPadAttribute('userid', userid, function (e) { ready(info, userid, readOnly);
if (e) { console.error(e); }
ready(info, userid, readOnly);
});
}); });
}) })
.on('disconnect', disconnect) .on('disconnect', disconnect)

@ -1,422 +0,0 @@
html,
body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
border: 0px;
}
body {
display: flex;
flex-flow: column;
}
#content {
display: flex;
flex: 1;
}
#content #poll {
flex: 1;
}
.cryptpad-toolbar h2 {
font: normal normal normal 12px Arial, Helvetica, Tahoma, Verdana, Sans-Serif;
color: #000;
line-height: auto;
}
.cryptpad-toolbar {
display: inline-block;
}
.realtime {
display: block;
max-height: 100%;
max-width: 100%;
}
.realtime input[type="text"] {
height: 1em;
margin: 0px;
}
.text-cell input[type="text"] {
width: 400px;
}
input[type="text"][disabled],
textarea[disabled] {
background-color: transparent;
font: white;
border: 0px;
}
input[type="text"]::placeholder {
color: #666;
}
table#table {
margin: 0px;
}
#tableContainer {
position: relative;
padding: 29px;
padding-right: 79px;
}
#tableContainer button {
height: 2rem;
display: none;
}
#publish {
display: none;
}
#publish,
#admin {
margin-top: 15px;
margin-bottom: 15px;
}
#create-user {
position: absolute;
display: inline-block;
/*left: 0px;*/
top: 55px;
width: 50px;
overflow: hidden;
}
#create-option {
width: 50px;
}
#tableScroll {
overflow-y: hidden;
overflow-x: auto;
margin-left: calc(30% - 50px + 31px);
max-width: 70%;
width: auto;
display: inline-block;
}
#description {
padding: 15px;
margin: auto;
min-width: 80%;
width: 80%;
min-height: 5em;
font-size: 20px;
font-weight: bold;
}
#description[disabled] {
resize: none;
color: #000;
border: 1px solid #444;
}
#commit {
width: 100%;
}
#howItWorks {
width: 80%;
margin: auto;
}
div.upper {
width: 80%;
margin: auto;
}
table {
border-collapse: collapse;
border-spacing: 0;
margin: 20px;
}
tbody {
border: 1px solid #555;
}
tbody * {
box-sizing: border-box;
}
tbody tr {
text-align: center;
}
tbody tr:first-of-type th {
font-size: 20px;
border-top: 0px;
font-weight: bold;
padding: 10px;
text-decoration: underline;
}
tbody tr:first-of-type th.table-refresh {
color: #46E981;
text-decoration: none;
cursor: pointer;
}
tbody tr:nth-child(odd) {
background-color: #ffffff;
}
tbody tr th:first-of-type {
border-left: 0px;
}
tbody tr th {
box-sizing: border-box;
border: 1px solid #555;
}
tbody tr th,
tbody tr td {
color: #555;
}
tbody tr th.remove,
tbody tr td.remove {
cursor: pointer;
}
tbody tr th:last-child {
border-right: 0px;
}
tbody td {
border-right: 1px solid #555;
padding: 12px;
padding-top: 0px;
padding-bottom: 0px;
}
tbody td:last-child {
border-right: none;
}
form.realtime,
div.realtime {
padding: 0px;
margin: 0px;
}
form.realtime > textarea,
div.realtime > textarea {
width: 50%;
height: 15vh;
}
form.realtime table,
div.realtime table {
border-collapse: collapse;
width: calc(100% - 1px);
}
form.realtime table .editing,
div.realtime table .editing {
background-color: #88b8cc;
}
form.realtime table tr td:first-child,
div.realtime table tr td:first-child {
position: absolute;
left: 29px;
top: auto;
width: calc(30% - 50px);
}
form.realtime table tr td,
div.realtime table tr td {
padding: 0px;
margin: 0px;
}
form.realtime table tr td div.text-cell,
div.realtime table tr td div.text-cell {
padding: 0px;
margin: 0px;
height: 100%;
}
form.realtime table tr td div.text-cell input,
div.realtime table tr td div.text-cell input {
width: 80%;
width: 90%;
height: 100%;
border: 0px;
}
form.realtime table tr td div.text-cell input[disabled],
div.realtime table tr td div.text-cell input[disabled] {
background-color: transparent;
color: #000;
font-weight: bold;
}
form.realtime table tr td.checkbox-cell,
div.realtime table tr td.checkbox-cell {
margin: 0px;
padding: 0px;
height: 100%;
min-width: 150px;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain,
div.realtime table tr td.checkbox-cell div.checkbox-contain {
display: inline-block;
height: 100%;
width: 100%;
position: relative;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain label,
div.realtime table tr td.checkbox-cell div.checkbox-contain label {
background-color: transparent;
display: block;
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable),
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) {
display: none;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover {
font-weight: bold;
color: #000;
display: block;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover:after,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover:after {
height: 100%;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover.yes,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover.yes {
background-color: #46E981;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover.uncommitted,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover.uncommitted {
background: #ddd;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover.mine,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"]:not(.editable) ~ .cover.mine {
display: none;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="0"] ~ .cover,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="0"] ~ .cover {
background-color: #FA5858;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="0"] ~ .cover:after,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="0"] ~ .cover:after {
content: "✖";
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="1"] ~ .cover,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="1"] ~ .cover {
background-color: #46E981;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="1"] ~ .cover:after,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="1"] ~ .cover:after {
content: "✔";
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="2"] ~ .cover,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="2"] ~ .cover {
background-color: #ff5;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="2"] ~ .cover:after,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="2"] ~ .cover:after {
content: "~";
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="3"] ~ .cover,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="3"] ~ .cover {
background-color: #ccc;
}
form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="3"] ~ .cover:after,
div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="number"][value="3"] ~ .cover:after {
content: "?";
}
form.realtime table input[type="text"],
div.realtime table input[type="text"] {
height: auto;
border: 1px solid #fff;
width: 80%;
}
form.realtime table span,
div.realtime table span {
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
form.realtime table thead td,
div.realtime table thead td {
padding: 0px 5px;
background: #aaa;
border-radius: 20px 20px 0 0;
}
form.realtime table thead td:nth-of-type(2),
div.realtime table thead td:nth-of-type(2) {
background: #999;
}
form.realtime table thead td:nth-of-type(2) .lock,
div.realtime table thead td:nth-of-type(2) .lock {
cursor: default;
}
form.realtime table thead td input[type="text"],
div.realtime table thead td input[type="text"] {
width: 100%;
box-sizing: border-box;
padding: 1px 5px;
}
form.realtime table thead td input[type="text"][disabled],
div.realtime table thead td input[type="text"][disabled] {
color: #000;
border: 1px solid transparent;
}
form.realtime table tbody td:not(.editing) .text-cell,
div.realtime table tbody td:not(.editing) .text-cell {
background: #aaa;
}
form.realtime table tbody .text-cell input[type="text"],
div.realtime table tbody .text-cell input[type="text"] {
width: calc(100% - 50px);
}
form.realtime table tbody .text-cell .edit,
div.realtime table tbody .text-cell .edit {
float: right;
margin: 0 10px 0 0;
}
form.realtime table tbody .text-cell .remove,
div.realtime table tbody .text-cell .remove {
float: left;
margin: 0 0 0 10px;
}
form.realtime table tbody tr:not(:first-child) td:not(:first-child) label,
div.realtime table tbody tr:not(:first-child) td:not(:first-child) label {
border-top: 1px solid #555;
}
form.realtime table .edit,
div.realtime table .edit {
color: #000;
cursor: pointer;
float: left;
margin-left: 10px;
}
form.realtime table .lock,
div.realtime table .lock {
margin-left: calc(50% - 0.5em);
cursor: pointer;
width: 1em;
text-align: center;
}
form.realtime table .remove,
div.realtime table .remove {
float: right;
margin-right: 10px;
}
form.realtime table thead tr th input[type="text"][disabled],
div.realtime table thead tr th input[type="text"][disabled] {
background-color: transparent;
color: #555;
font-weight: bold;
}
form.realtime table thead tr th .remove,
div.realtime table thead tr th .remove {
cursor: pointer;
font-size: 20px;
}
form.realtime table tfoot tr,
div.realtime table tfoot tr {
border: none;
}
form.realtime table tfoot tr td,
div.realtime table tfoot tr td {
border: none;
text-align: center;
}
form.realtime table tfoot tr td .save,
div.realtime table tfoot tr td .save {
padding: 15px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
form.realtime #adduser,
div.realtime #adduser,
form.realtime #addoption,
div.realtime #addoption {
color: #46E981;
border: 1px solid #46E981;
padding: 15px;
cursor: pointer;
}
form.realtime #adduser,
div.realtime #adduser {
border-top-left-radius: 5px;
}
form.realtime #addoption,
div.realtime #addoption {
border-bottom-left-radius: 5px;
}

@ -22,6 +22,7 @@ html, body {
body { body {
display: flex; display: flex;
flex-flow: column; flex-flow: column;
overflow-x: hidden;
} }
#content { #content {
display: flex; display: flex;

@ -11,7 +11,6 @@ define([
'/bower_components/marked/marked.min.js', '/bower_components/marked/marked.min.js',
'cm/lib/codemirror', 'cm/lib/codemirror',
'cm/mode/markdown/markdown', 'cm/mode/markdown/markdown',
'/bower_components/tweetnacl/nacl-fast.min.js',
'less!/profile/main.less', 'less!/profile/main.less',
], function ($, Cryptpad, Listmap, Crypto, Marked, CodeMirror) { ], function ($, Cryptpad, Listmap, Crypto, Marked, CodeMirror) {
@ -105,7 +104,7 @@ define([
if (err) { return void console.error(err); } if (err) { return void console.error(err); }
Cryptpad.whenRealtimeSyncs(realtime, function () { Cryptpad.whenRealtimeSyncs(realtime, function () {
lastVal = newVal; lastVal = newVal;
Cryptpad.log('TODO: '+name+' saved'); Cryptpad.log(Messages._getKey('profile_fieldSaved', [newVal]));
editing = false; editing = false;
}); });
}); });
@ -156,11 +155,11 @@ define([
var getValue = function (cb) { var getValue = function (cb) {
cb(APP.lm.proxy.name); cb(APP.lm.proxy.name);
}; };
var placeholder = Messages.anonymous; var placeholder = Messages.profile_namePlaceholder;
if (APP.readOnly) { if (APP.readOnly) {
var $span = $('<span>', {'class': DISPLAYNAME_ID}).appendTo($block); var $span = $('<span>', {'class': DISPLAYNAME_ID}).appendTo($block);
getValue(function (value) { getValue(function (value) {
$span.text(value || placeholder); $span.text(value || Messages.anonymous);
}); });
return; return;
} }
@ -196,7 +195,7 @@ define([
cb(); cb();
}; };
var rt = APP.lm.realtime; var rt = APP.lm.realtime;
var placeholder = "URL"; //XXX var placeholder = Messages.profile_urlPlaceholder;
createEditableInput($block, LINK_ID, placeholder, getValue, setValue, rt); createEditableInput($block, LINK_ID, placeholder, getValue, setValue, rt);
}; };
@ -209,7 +208,7 @@ define([
if (!APP.lm.proxy.avatar) { if (!APP.lm.proxy.avatar) {
$('<img>', { $('<img>', {
src: '/customize/images/avatar.png', src: '/customize/images/avatar.png',
title: 'Avatar', // XXX title: Messages.profile_avatar,
alt: 'Avatar' alt: 'Avatar'
}).appendTo($span); }).appendTo($span);
return; return;
@ -218,21 +217,20 @@ define([
if (APP.readOnly) { return; } if (APP.readOnly) { return; }
var $delButton = $('<button>', {'class': 'delete btn btn-danger fa fa-times'}); //XXX var $delButton = $('<button>', {
'class': 'delete btn btn-danger fa fa-times',
title: Messages.fc_delete
});
$span.append($delButton); $span.append($delButton);
$delButton.click(function () { $delButton.click(function () {
console.log('clicked');
var oldChanId = Cryptpad.hrefToHexChannelId(APP.lm.proxy.avatar); var oldChanId = Cryptpad.hrefToHexChannelId(APP.lm.proxy.avatar);
Cryptpad.unpinPads([oldChanId], function (e) { Cryptpad.unpinPads([oldChanId], function (e) {
if (e) { Cryptpad.log(e); } if (e) { Cryptpad.log(e); }
console.log('unpinned');
delete APP.lm.proxy.avatar; delete APP.lm.proxy.avatar;
delete Cryptpad.getProxy().profile.avatar; delete Cryptpad.getProxy().profile.avatar;
Cryptpad.whenRealtimeSyncs(APP.lm.realtime, function () { Cryptpad.whenRealtimeSyncs(APP.lm.realtime, function () {
console.log('synced1');
var driveRt = Cryptpad.getStore().getProxy().info.realtime; var driveRt = Cryptpad.getStore().getProxy().info.realtime;
Cryptpad.whenRealtimeSyncs(driveRt, function () { Cryptpad.whenRealtimeSyncs(driveRt, function () {
console.log('synced2');
displayAvatar(); displayAvatar();
}); });
}); });
@ -285,7 +283,7 @@ define([
accept: ".gif,.jpg,.jpeg,.png" accept: ".gif,.jpg,.jpeg,.png"
}; };
var $upButton = Cryptpad.createButton('upload', false, data); var $upButton = Cryptpad.createButton('upload', false, data);
$upButton.text(" Upload a new avatar"); $upButton.text(Messages.profile_upload);
$upButton.prepend($('<span>', {'class': 'fa fa-upload'})); $upButton.prepend($('<span>', {'class': 'fa fa-upload'}));
$block.append($upButton); $block.append($upButton);
}; };
@ -300,6 +298,7 @@ define([
$div.html(val); $div.html(val);
return; return;
} }
$('<h3>').text(Messages.profile_description).insertBefore($block);
var $ok = $('<span>', {'class': 'ok fa fa-check', title: Messages.saved}).appendTo($block); var $ok = $('<span>', {'class': 'ok fa fa-check', title: Messages.saved}).appendTo($block);
var $spinner = $('<span>', {'class': 'spin fa fa-spinner fa-pulse'}).appendTo($block); var $spinner = $('<span>', {'class': 'spin fa fa-spinner fa-pulse'}).appendTo($block);
@ -388,17 +387,16 @@ define([
if (e === 'E_OVER_LIMIT') { if (e === 'E_OVER_LIMIT') {
Cryptpad.alert(Messages.pinLimitNotPinned, null, true); Cryptpad.alert(Messages.pinLimitNotPinned, null, true);
} }
return void Cryptpad.log('Error while creating your profile: ' + e); // XXX return void Cryptpad.log(Messages._getKey('profile_error', [e]));
} }
obj.profile.edit = Cryptpad.getEditHashFromKeys(channel, secret.keys); obj.profile.edit = Cryptpad.getEditHashFromKeys(channel, secret.keys);
obj.profile.view = Cryptpad.getViewHashFromKeys(channel, secret.keys); obj.profile.view = Cryptpad.getViewHashFromKeys(channel, secret.keys);
obj.profile.name = APP.rt.proxy[Cryptpad.displayNameKey] || '';
andThen(obj.profile.edit); andThen(obj.profile.edit);
}); });
}; };
if (!Cryptpad.isLoggedIn()) { // XXX if (!Cryptpad.isLoggedIn()) {
var $p = $('<p>').text('TODO: You have to register to create a profile'); var $p = $('<p>', {id: CREATE_ID}).append(Messages.profile_register);
var $a = $('<a>', { var $a = $('<a>', {
href: '/register/' href: '/register/'
}); });
@ -411,7 +409,7 @@ define([
} }
var $create = $('<div>', {id: CREATE_ID}); var $create = $('<div>', {id: CREATE_ID});
var $button = $('<button>', {'class': 'btn btn-success'}); var $button = $('<button>', {'class': 'btn btn-success'});
$button.text('TODO: create a profile?').click(todo).appendTo($create); // XXX $button.text(Messages.profile_create).click(todo).appendTo($create);
APP.$container.append($create); APP.$container.append($create);
}; };

@ -4,8 +4,6 @@
width: 1000px; width: 1000px;
max-width: 90%; max-width: 90%;
margin: auto; margin: auto;
display: flex;
justify-content: center;
#container { #container {
font-size: 25px; font-size: 25px;
width: 100%; width: 100%;
@ -129,4 +127,11 @@
} }
} }
} }
#createProfile {
height: 100%;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
}
} }

@ -118,6 +118,7 @@ define([
return void Cryptpad.alert(Messages.register_mustAcceptTerms); return void Cryptpad.alert(Messages.register_mustAcceptTerms);
} }
setTimeout(function () {
Cryptpad.confirm("<h2 class='bright'>" + Messages.register_warning + "</h2>", Cryptpad.confirm("<h2 class='bright'>" + Messages.register_warning + "</h2>",
function (yes) { function (yes) {
if (!yes) { return; } if (!yes) { return; }
@ -200,6 +201,24 @@ define([
}, true, function ($dialog) { }, true, function ($dialog) {
$dialog.find('> div').addClass('half'); $dialog.find('> div').addClass('half');
}); });
}, 150);
});
var clickRegister = Cryptpad.notAgainForAnother(function () {
$register.click();
}, 500);
$register.on('keypress', function (e) {
e.preventDefault();
e.stopPropagation();
console.error(e.which);
switch (e.which) {
case 13: return clickRegister();
case 13: return clickRegister();
default:
//console.log(e.which);
}
}); });
Test(function () { Test(function () {

@ -6,34 +6,6 @@
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css"> <link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<script src="/bower_components/jquery/dist/jquery.min.js"></script> <script src="/bower_components/jquery/dist/jquery.min.js"></script>
<script src="/bower_components/codemirror/lib/codemirror.js"></script>
<link rel="stylesheet" href="/bower_components/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="/bower_components/codemirror/addon/dialog/dialog.css">
<link rel="stylesheet" href="/bower_components/codemirror/addon/fold/foldgutter.css" />
<script src="/bower_components/codemirror/mode/javascript/javascript.js"></script>
<script src="/bower_components/codemirror/addon/mode/loadmode.js"></script>
<script src="/bower_components/codemirror/mode/meta.js"></script>
<script src="/bower_components/codemirror/addon/mode/overlay.js"></script>
<script src="/bower_components/codemirror/addon/mode/multiplex.js"></script>
<script src="/bower_components/codemirror/addon/mode/simple.js"></script>
<script src="/bower_components/codemirror/addon/edit/closebrackets.js"></script>
<script src="/bower_components/codemirror/addon/edit/matchbrackets.js"></script>
<script src="/bower_components/codemirror/addon/edit/trailingspace.js"></script>
<script src="/bower_components/codemirror/addon/selection/active-line.js"></script>
<script src="/bower_components/codemirror/addon/search/search.js"></script>
<script src="/bower_components/codemirror/addon/search/match-highlighter.js"></script>
<script src="/bower_components/codemirror/addon/search/searchcursor.js"></script>
<script src="/bower_components/codemirror/addon/dialog/dialog.js"></script>
<script src="/bower_components/codemirror/addon/fold/foldcode.js"></script>
<script src="/bower_components/codemirror/addon/fold/foldgutter.js"></script>
<script src="/bower_components/codemirror/addon/fold/brace-fold.js"></script>
<script src="/bower_components/codemirror/addon/fold/xml-fold.js"></script>
<script src="/bower_components/codemirror/addon/fold/markdown-fold.js"></script>
<script src="/bower_components/codemirror/addon/fold/comment-fold.js"></script>
<script src="/bower_components/codemirror/addon/display/placeholder.js"></script>
<script async data-bootload="inner.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script> <script async data-bootload="inner.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.1.15"></script>
<style>.loading-hidden { display: none; } </style> <style>.loading-hidden { display: none; } </style>
</head> </head>

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

@ -9,7 +9,6 @@ define([
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/cryptget.js', '/common/cryptget.js',
'/slide/slide.js', '/slide/slide.js',
'/bower_components/tweetnacl/nacl-fast.min.js', // needed for media-tag
'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less/cryptpad.less', 'less!/customize/src/less/cryptpad.less',
@ -53,7 +52,7 @@ define([
var andThen = function (CMeditor) { var andThen = function (CMeditor) {
var $iframe = $('#pad-iframe').contents(); var $iframe = $('#pad-iframe').contents();
var $contentContainer = $iframe.find('#editorContainer'); var $contentContainer = $iframe.find('#editorContainer');
var CodeMirror = Cryptpad.createCodemirror(CMeditor, ifrw, Cryptpad); var CodeMirror = Cryptpad.createCodemirror(ifrw, Cryptpad, null, CMeditor);
editor = CodeMirror.editor; editor = CodeMirror.editor;
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox'); var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
@ -468,7 +467,10 @@ define([
Cryptpad.feedback('PRINT_SLIDES'); Cryptpad.feedback('PRINT_SLIDES');
//$('body').append(createPrintDialog()); //$('body').append(createPrintDialog());
}).append($('<span>', {'class': 'drawer'}).text(Messages.printText)); }).append($('<span>', {'class': 'drawer'}).text(Messages.printText));
$drawer.append($printButton);
// TODO reenable this when it is working again
$printButton = $printButton;
//$drawer.append($printButton);
var $slideOptions = $('<button>', { var $slideOptions = $('<button>', {
title: Messages.slideOptionsTitle, title: Messages.slideOptionsTitle,
@ -590,7 +592,7 @@ define([
Cryptpad.getPadAttribute('previewMode', function (e, data) { Cryptpad.getPadAttribute('previewMode', function (e, data) {
if (e) { return void console.error(e); } if (e) { return void console.error(e); }
if (data === true && APP.$previewButton) { if ([true, undefined].indexOf(data) !== -1 && APP.$previewButton) {
APP.$previewButton.click(); APP.$previewButton.click();
} }
}); });

@ -19,6 +19,7 @@ html, body{
position: relative; position: relative;
} }
body { body {
font-size: unset;
display: flex; display: flex;
flex-flow: column; flex-flow: column;
} }
@ -238,6 +239,9 @@ div.modal, div#modal {
transition: margin-left 1s; transition: margin-left 1s;
} }
} }
media-tag button {
max-height: none;
}
} }
box-sizing: border-box; box-sizing: border-box;
@ -321,8 +325,6 @@ div#modal #content, #print {
img { img {
position: relative; position: relative;
min-width: 1%; min-width: 1%;
max-width: 90%;
max-height: 90%;
margin: auto; margin: auto;
} }
.slideNumber { .slideNumber {

Loading…
Cancel
Save