diff --git a/bower.json b/bower.json index 0fb80b9f9..b04ff3a6b 100644 --- a/bower.json +++ b/bower.json @@ -19,12 +19,12 @@ ], "dependencies": { "jquery": "~2.1.3", - "tweetnacl": "~0.12.2", + "tweetnacl": "0.12.2", "components-font-awesome": "^4.6.3", "ckeditor": "~4.7", "codemirror": "^5.19.0", - "requirejs": "~2.1.15", - "marked": "~0.3.5", + "requirejs": "2.1.15", + "marked": "0.3.5", "rangy": "rangy-release#~1.3.0", "json.sortify": "~2.1.0", "secure-fabric.js": "secure-v1.7.9", @@ -33,12 +33,12 @@ "chainpad-json-validator": "^0.2.0", "chainpad-crypto": "^0.1.3", "chainpad-listmap": "^0.3.0", - "file-saver": "^1.3.1", - "diff-dom": "^2.1.1", - "alertifyjs": "^1.0.11", - "scrypt-async": "^1.2.0", - "require-css": "^0.1.10", + "file-saver": "1.3.1", + "alertifyjs": "1.0.11", + "scrypt-async": "1.2.0", + "require-css": "0.1.10", "less": "^2.7.2", - "bootstrap": "#v4.0.0-alpha.6" + "bootstrap": "#v4.0.0-alpha.6", + "diff-dom": "2.1.1" } } diff --git a/customize.dist/alt-favicon.png b/customize.dist/alt-favicon.png index 9a622d18a..169ce4ef2 100644 Binary files a/customize.dist/alt-favicon.png and b/customize.dist/alt-favicon.png differ diff --git a/customize.dist/bg3.jpg b/customize.dist/bg3.jpg index 96a73590e..04d3a04e0 100644 Binary files a/customize.dist/bg3.jpg and b/customize.dist/bg3.jpg differ diff --git a/customize.dist/cryptofist_mini.png b/customize.dist/cryptofist_mini.png index 82a5218ef..73845e455 100644 Binary files a/customize.dist/cryptofist_mini.png and b/customize.dist/cryptofist_mini.png differ diff --git a/customize.dist/cryptofist_small.png b/customize.dist/cryptofist_small.png index e1340eb7c..9c818eb45 100644 Binary files a/customize.dist/cryptofist_small.png and b/customize.dist/cryptofist_small.png differ diff --git a/customize.dist/images/code.png b/customize.dist/images/code.png index e5b966143..3c57fd546 100644 Binary files a/customize.dist/images/code.png and b/customize.dist/images/code.png differ diff --git a/customize.dist/images/hash.png b/customize.dist/images/hash.png index b1b662912..474f3815b 100644 Binary files a/customize.dist/images/hash.png and b/customize.dist/images/hash.png differ diff --git a/customize.dist/images/key_small.png b/customize.dist/images/key_small.png index 2810ffb7d..bbf2673b2 100644 Binary files a/customize.dist/images/key_small.png and b/customize.dist/images/key_small.png differ diff --git a/customize.dist/images/organize.png b/customize.dist/images/organize.png index 6dd9dc11f..c7417332e 100644 Binary files a/customize.dist/images/organize.png and b/customize.dist/images/organize.png differ diff --git a/customize.dist/images/pad.png b/customize.dist/images/pad.png index deb962bd7..184b0e722 100644 Binary files a/customize.dist/images/pad.png and b/customize.dist/images/pad.png differ diff --git a/customize.dist/images/poll.png b/customize.dist/images/poll.png index 776864526..6fc629197 100644 Binary files a/customize.dist/images/poll.png and b/customize.dist/images/poll.png differ diff --git a/customize.dist/images/realtime_small.png b/customize.dist/images/realtime_small.png index 64c4ba0a3..5a101d563 100644 Binary files a/customize.dist/images/realtime_small.png and b/customize.dist/images/realtime_small.png differ diff --git a/customize.dist/images/slide.png b/customize.dist/images/slide.png index 9affdc923..2ec30667c 100644 Binary files a/customize.dist/images/slide.png and b/customize.dist/images/slide.png differ diff --git a/customize.dist/images/useraccount.png b/customize.dist/images/useraccount.png index 8bcec5b39..22209ac84 100644 Binary files a/customize.dist/images/useraccount.png and b/customize.dist/images/useraccount.png differ diff --git a/customize.dist/images/zeroknowledge_small.png b/customize.dist/images/zeroknowledge_small.png index 4b314f4a7..dcef74f22 100644 Binary files a/customize.dist/images/zeroknowledge_small.png and b/customize.dist/images/zeroknowledge_small.png differ diff --git a/customize.dist/images/zk.png b/customize.dist/images/zk.png index 4433b257b..97da83ea6 100644 Binary files a/customize.dist/images/zk.png and b/customize.dist/images/zk.png differ diff --git a/customize.dist/main-favicon.png b/customize.dist/main-favicon.png index 697d11b32..54169c07e 100644 Binary files a/customize.dist/main-favicon.png and b/customize.dist/main-favicon.png differ diff --git a/customize.dist/main.js b/customize.dist/main.js index f2580bbe1..633f4c573 100644 --- a/customize.dist/main.js +++ b/customize.dist/main.js @@ -171,6 +171,10 @@ define([ $('button.gotodrive').click(function () { document.location.href = '/drive/'; }); + + $('button#loggedInLogout').click(function () { + $('#user-menu .logout').click(); + }); }; displayCreateButtons(); diff --git a/customize.dist/pages.js b/customize.dist/pages.js index 4258f34ca..ce25d010b 100644 --- a/customize.dist/pages.js +++ b/customize.dist/pages.js @@ -444,7 +444,7 @@ define([ h('div#poll', [ h('div#howItWorks', [ 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_encryption) ]), diff --git a/customize.dist/src/less/cryptpad.less b/customize.dist/src/less/cryptpad.less index c1a9b9c59..17cb35d56 100644 --- a/customize.dist/src/less/cryptpad.less +++ b/customize.dist/src/less/cryptpad.less @@ -112,17 +112,19 @@ h6 { padding-top: .65001rem; } -a:not(.btn) { - cursor: pointer; - color: @cp-link; +p { + a:not(.btn) { + cursor: pointer; + color: @cp-link; - text-decoration: none; + text-decoration: none; - &:hover { - color: @cp-link-hover; - } - &:visited { - color: @cp-link-visited; + &:hover { + color: @cp-link-hover; + } + &:visited { + color: @cp-link-visited; + } } } a.btn { diff --git a/customize.dist/src/less/dropdown.less b/customize.dist/src/less/dropdown.less index e6fc7105c..28daed328 100644 --- a/customize.dist/src/less/dropdown.less +++ b/customize.dist/src/less/dropdown.less @@ -21,6 +21,10 @@ margin-right: 0px; margin-left: 5px; } + * { + .unselectable(); + cursor: default; + } } .dropdown-bar-content { diff --git a/customize.dist/src/less/toolbar.less b/customize.dist/src/less/toolbar.less index dd189671c..4edd3800c 100644 --- a/customize.dist/src/less/toolbar.less +++ b/customize.dist/src/less/toolbar.less @@ -16,15 +16,28 @@ color: inherit; } -#cke_editor1 { - .cke_inner { - bottom: 0; - right: 0; - display: flex; - flex-flow: column; - height: 100vh; - width: 100vw; - } +.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; } + +.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 { display: inline-block; @@ -35,17 +48,12 @@ margin-top: -1px; display: flex; 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 { font: normal normal normal 16px Arial,Helvetica,Tahoma,Verdana,Sans-Serif; min-width: 175px; @@ -104,6 +112,51 @@ body .userlist-drawer { background: rgba(0,0,0,0.1); margin: 2px 0; 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; margin: 0; &::before { + width: 100%; + text-align: center; padding-top: 4px; } &:hover { @@ -796,11 +851,10 @@ body .cryptpad-toolbar { width: 64px; padding: 0; span { + text-align: center; + width: 100%; cursor: default; font-size: 32px; - .fa { - margin-left: 3px; - } } } } @@ -850,8 +904,10 @@ body .cryptpad-toolbar { } } .cryptpad-toolbar-rightside { - height: 32px; + min-height: 32px; + overflow: hidden; &:empty { + min-height: 0; height: 0; } text-align: right; @@ -873,7 +929,7 @@ body .cryptpad-toolbar { background: @dropdown-bg; display: flex; flex-flow: column; - z-index:1000; + z-index:10000; color: black; .fa { font-size: 17px; @@ -881,7 +937,7 @@ body .cryptpad-toolbar { &> span { box-sizing: border-box; min-width: 150px; - height: 26px; + height: 32px; border-radius: 0; border: 0; } diff --git a/customize.dist/src/less/topbar.less b/customize.dist/src/less/topbar.less index a394c4eeb..3e8812ddd 100644 --- a/customize.dist/src/less/topbar.less +++ b/customize.dist/src/less/topbar.less @@ -72,7 +72,7 @@ } } } - a { + &.link a { font-weight: 500; font-size: 0.75em; color: @cp-link; diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 0e4cce6a7..bca33cb26 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -240,6 +240,18 @@ define(function () { out.canvas_opacityLabel = "opacité: {0}"; out.canvas_widthLabel = "taille: {0}"; + // Profile + out.profileButton = "Profil"; // dropdown menu + out.profile_urlPlaceholder = 'URL'; + out.profile_namePlaceholder = 'Nom ou pseudo pour le profil'; + out.profile_avatar = "Avatar"; + out.profile_upload = " Importer un nouvel avatar"; + out.profile_error = "Erreur lors de la création du profil : {0}"; + out.profile_register = "Vous devez vous inscrire pour pouvoir créer un profil !"; + out.profile_create = "Créer un profil"; + out.profile_description = "Description"; + out.profile_fieldSaved = 'Nouvelle valeur enregistrée: {0}'; + // File manager out.fm_rootName = "Documents"; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 5bd46fabd..86ce50f9d 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -241,7 +241,18 @@ define(function () { out.canvas_opacity = "Opacity"; out.canvas_opacityLabel = "opacity: {0}"; out.canvas_widthLabel = "Width: {0}"; - + + // Profile + out.profileButton = "Profile"; // dropdown menu + out.profile_urlPlaceholder = 'URL'; + out.profile_namePlaceholder = 'Name displayed in your profile'; + out.profile_avatar = "Avatar"; + out.profile_upload = " Upload a new avatar"; + out.profile_error = "Error while creating your profile: {0}"; + out.profile_register = "You have to sign up to create a profile!"; + out.profile_create = "Create a profile"; + out.profile_description = "Description"; + out.profile_fieldSaved = 'New value saved: {0}'; // File manager diff --git a/package.json b/package.json index 96ebe9ff1..ed0f25695 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cryptpad", "description": "realtime collaborative visual editor with zero knowlege server", - "version": "1.9.0-1", + "version": "1.10.0", "dependencies": { "chainpad-server": "^1.0.1", "express": "~4.10.1", diff --git a/pinneddata.js b/pinneddata.js index 8dbbc7fec..2ecf8605d 100644 --- a/pinneddata.js +++ b/pinneddata.js @@ -30,7 +30,7 @@ const sizeForHashes = (hashes, dsFileStats) => { let sum = 0; hashes.forEach((h) => { const s = dsFileStats[h]; - if (typeof(s) !== 'number') { + if (typeof(s) !== 'object' || typeof(s.size) !== 'number') { //console.log('missing ' + h + ' ' + typeof(s)); } else { sum += s.size; @@ -62,11 +62,26 @@ nThen((waitFor) => { }); }); }).nThen((waitFor) => { + + Fs.readdir('./blob', waitFor((err, list) => { + if (err) { throw err; } + dirList = list; + })); +}).nThen((waitFor) => { + dirList.forEach((f) => { + sema.take((returnAfter) => { + Fs.readdir('./blob/' + f, waitFor(returnAfter((err, list2) => { + if (err) { throw err; } + list2.forEach((ff) => { fileList.push('./blob/' + f + '/' + ff); }); + }))); + }); + }); +}).nThen((waitFor) => { fileList.forEach((f) => { sema.take((returnAfter) => { Fs.stat(f, waitFor(returnAfter((err, st) => { if (err) { throw err; } - dsFileStats[f.replace(/^.*\/([^\/]*)\.ndjson$/, (all, a) => (a))] = st; + dsFileStats[f.replace(/^.*\/([^\/\.]*)(\.ndjson)?$/, (all, a) => (a))] = st; }))); }); }); diff --git a/rpc.js b/rpc.js index d00ee93b7..5b5d058b9 100644 --- a/rpc.js +++ b/rpc.js @@ -281,7 +281,7 @@ var getUploadSize = function (Env, channel, cb) { var paths = Env.paths; var path = makeFilePath(paths.blob, channel); if (!path) { - return cb('INVALID_UPLOAD_ID'); + return cb('INVALID_UPLOAD_ID', path); } Fs.stat(path, function (err, stats) { @@ -583,9 +583,10 @@ var resetUserPins = function (Env, publicKey, channelList, cb) { if (e) { return void cb(e); } var pinSize = sumChannelSizes(sizes); - getFreeSpace(Env, publicKey, function (e, free) { + + getLimit(Env, publicKey, function (e, limit) { if (e) { - WARN('getFreeSpace', e); + WARN('[RESET_ERR]', e); return void cb(e); } @@ -597,7 +598,7 @@ var resetUserPins = function (Env, publicKey, channelList, cb) { They will not be able to pin additional pads until they upgrade or delete enough files to go back under their limit. */ - if (pinSize > free && session.hasPinned) { return void(cb('E_OVER_LIMIT')); } + if (pinSize > limit && session.hasPinned) { return void(cb('E_OVER_LIMIT')); } pinStore.message(publicKey, JSON.stringify(['RESET', channelList]), function (e) { if (e) { return void cb(e); } @@ -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) { return [ 'COOKIE', @@ -834,11 +842,8 @@ var isAuthenticatedCall = function (call) { 'UNPIN', 'GET_HASH', 'GET_TOTAL_SIZE', - 'GET_FILE_SIZE', 'UPDATE_LIMITS', 'GET_LIMIT', - 'GET_MULTIPLE_FILE_SIZE', - //'UPLOAD', 'UPLOAD_COMPLETE', 'UPLOAD_CANCEL', ].indexOf(call) !== -1; @@ -867,6 +872,34 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) var blobPath = paths.blob = keyOrDefaultString('blobPath', './blob'); var blobStagingPath = paths.staging = keyOrDefaultString('blobStagingPath', './blobstage'); + var isUnauthenticateMessage = function (msg) { + return msg && msg.length === 2 && isUnauthenticatedCall(msg[0]); + }; + + var handleUnauthenticatedMessage = function (msg, respond) { + switch (msg[0]) { + case 'GET_FILE_SIZE': + return void getFileSize(Env, msg[1], function (e, size) { + if (e) { + console.error(e); + } + WARN(e, msg[1]); + respond(e, [null, size, null]); + }); + case 'GET_MULTIPLE_FILE_SIZE': + return void getMultipleFileSize(Env, msg[1], function (e, dict) { + if (e) { + WARN(e, dict); + return respond(e); + } + respond(e, [null, dict, null]); + }); + default: + console.error("unsupported!"); + return respond('UNSUPPORTED_RPC_CALL', msg); + } + }; + var rpc = function ( ctx /*:{ store: Object }*/, data /*:Array>*/, @@ -888,11 +921,19 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) return void respond('INVALID_ARG_FORMAT'); } + if (isUnauthenticateMessage(msg)) { + return handleUnauthenticatedMessage(msg, respond); + } + var signature = msg.shift(); var publicKey = msg.shift(); // make sure a user object is initialized in the cookie jar - beginSession(Sessions, publicKey); + if (publicKey) { + beginSession(Sessions, publicKey); + } else { + console.log("No public key"); + } var cookie = msg[0]; if (!isValidCookie(Sessions, publicKey, cookie)) { @@ -928,7 +969,8 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) msg.shift(); var Respond = function (e, msg) { - var token = Sessions[safeKey].tokens.slice(-1)[0]; + var session = Sessions[safeKey]; + var token = session? session.tokens.slice(-1)[0]: ''; var cookie = makeCookie(token).join('|'); respond(e, [cookie].concat(typeof(msg) !== 'undefined' ?msg: [])); }; @@ -1046,8 +1088,8 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) return void handleMessage(false); } - // restrict upload capability unless explicitly disabled - if (config.restrictUploads === false) { + // allow unrestricted uploads unless restrictUploads is true + if (config.restrictUploads !== true) { return void handleMessage(true); } diff --git a/www/code/inner.html b/www/code/inner.html index e832c026c..6a224ba7d 100644 --- a/www/code/inner.html +++ b/www/code/inner.html @@ -3,33 +3,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/www/code/inner.js b/www/code/inner.js index d2dac0968..57d154950 100644 --- a/www/code/inner.js +++ b/www/code/inner.js @@ -1,8 +1,37 @@ define([ 'jquery', + + 'cm/lib/codemirror', + 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/code/code.less', 'less!/customize/src/less/toolbar.less', -], 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'); }); diff --git a/www/code/main.js b/www/code/main.js index 226e3a235..68d9c68bc 100644 --- a/www/code/main.js +++ b/www/code/main.js @@ -9,7 +9,6 @@ define([ '/common/cryptpad-common.js', '/common/cryptget.js', '/common/diffMarked.js', - '/bower_components/tweetnacl/nacl-fast.min.js', // needed for media-tag 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/customize/src/less/cryptpad.less' @@ -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'); editor = CodeMirror.editor; @@ -429,7 +428,6 @@ define([ }; var interval = 100; - var second = function (CM) { Cryptpad.ready(function () { andThen(CM); @@ -444,11 +442,9 @@ define([ var first = function () { if (ifrw.CodeMirror) { - // it exists, call your continuation second(ifrw.CodeMirror); } else { console.log("CodeMirror was not defined. Trying again in %sms", interval); - // try again in 'interval' ms setTimeout(first, interval); } }; diff --git a/www/common/boot2.js b/www/common/boot2.js index da1ca2bc3..928b57d48 100644 --- a/www/common/boot2.js +++ b/www/common/boot2.js @@ -10,6 +10,7 @@ define([], function () { "json.sortify": "/bower_components/json.sortify/dist/JSON.sortify", //"pdfjs-dist/build/pdf": "/bower_components/pdfjs-dist/build/pdf", //"pdfjs-dist/build/pdf.worker": "/bower_components/pdfjs-dist/build/pdf.worker" + cm: '/bower_components/codemirror' }, map: { '*': { diff --git a/www/common/common-codemirror.js b/www/common/common-codemirror.js index 6202a9ce3..333059a1d 100644 --- a/www/common/common-codemirror.js +++ b/www/common/common-codemirror.js @@ -2,18 +2,18 @@ define([ 'jquery', '/common/modes.js', '/common/themes.js', + '/bower_components/file-saver/FileSaver.min.js' ], function ($, Modes, Themes) { var saveAs = window.saveAs; var module = {}; - module.create = function (CMeditor, ifrw, Cryptpad) { + module.create = function (ifrw, Cryptpad, defaultMode, CMeditor) { var exp = {}; - var Messages = Cryptpad.Messages; var CodeMirror = exp.CodeMirror = CMeditor; - CodeMirror.modeURL = "/bower_components/codemirror/mode/%N/%N.js"; + CodeMirror.modeURL = "cm/mode/%N/%N"; var $pad = $('#pad-iframe'); var $textarea = exp.$textarea = $pad.contents().find('#editor1'); @@ -43,14 +43,16 @@ define([ extraKeys: {"Shift-Ctrl-R": undefined}, foldGutter: true, gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], - mode: "javascript", + mode: defaultMode || "javascript", readOnly: true }); editor.setValue(Messages.codeInitialState); var setMode = exp.setMode = function (mode, cb) { exp.highlightMode = mode; - if (mode !== "text") { CMeditor.autoLoadMode(editor, mode); } + if (mode !== "text") { + CMeditor.autoLoadMode(editor, mode); + } editor.setOption('mode', mode); if (exp.$language) { var name = exp.$language.find('a[data-value="' + mode + '"]').text() || undefined; diff --git a/www/common/common-title.js b/www/common/common-title.js index 3b71ec63e..dcdbed055 100644 --- a/www/common/common-title.js +++ b/www/common/common-title.js @@ -1,4 +1,4 @@ -define(function () { +define(['jquery'], function ($) { var module = {}; module.create = function (cfg, onLocal, Cryptpad) { @@ -45,7 +45,8 @@ define(function () { }; // update title: href is optional; if not specified, we use window.location.href - exp.updateTitle = function (newTitle, href) { + exp.updateTitle = function (newTitle, href, cb) { + cb = cb || $.noop; if (newTitle === exp.title) { return; } // Change the title now, and set it back to the old value if there is an error var oldTitle = exp.title; @@ -54,9 +55,10 @@ define(function () { console.log("Couldn't set pad title"); console.error(err); updateLocalTitle(oldTitle); - return; + return void cb(err); } updateLocalTitle(data); + cb(null, data); if (!$title) { return; } $title.find('span.title').text(data); $title.find('input').val(data); diff --git a/www/common/common-userlist.js b/www/common/common-userlist.js index 7b62d6566..2e1fe4adf 100644 --- a/www/common/common-userlist.js +++ b/www/common/common-userlist.js @@ -49,8 +49,10 @@ define(function () { exp.myUserName = myUserNameTemp; myData = {}; myData[exp.myNetfluxId] = { - name: exp.myUserName, - uid: Cryptpad.getUid(), + name: exp.myUserName, + uid: Cryptpad.getUid(), + avatar: Cryptpad.getAvatarUrl(), + profile: Cryptpad.getProfileUrl() }; addToUserData(myData); Cryptpad.setAttribute('username', exp.myUserName, function (err) { @@ -78,6 +80,8 @@ define(function () { myData[exp.myNetfluxId] = { name: "", uid: Cryptpad.getUid(), + avatar: Cryptpad.getAvatarUrl(), + profile: Cryptpad.getProfileUrl() }; addToUserData(myData); onLocal(); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 291062e83..901d33079 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -16,9 +16,10 @@ define([ '/common/clipboard.js', '/common/pinpad.js', - '/customize/application_config.js' + '/customize/application_config.js', + '/common/media-tag.js', ], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata, - CodeMirror, Files, FileCrypto, Clipboard, Pinpad, AppConfig) { + CodeMirror, Files, FileCrypto, Clipboard, Pinpad, AppConfig, MediaTag) { /* This file exposes functionality which is specific to Cryptpad, but not to any particular pad type. This includes functions for committing metadata @@ -49,6 +50,7 @@ define([ var store; var rpc; + var anon_rpc; // import UI elements common.findCancelButton = UI.findCancelButton; @@ -76,7 +78,7 @@ define([ var deduplicateString = common.deduplicateString = Util.deduplicateString; common.uint8ArrayToHex = Util.uint8ArrayToHex; common.replaceHash = Util.replaceHash; - var getHash = common.getHash = Util.getHash; + common.getHash = Util.getHash; common.fixFileName = Util.fixFileName; common.bytesToMegabytes = Util.bytesToMegabytes; common.bytesToKilobytes = Util.bytesToKilobytes; @@ -146,6 +148,16 @@ define([ } 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) { if (force !== true) { @@ -414,9 +426,8 @@ define([ // STORAGE common.setPadAttribute = function (attr, value, cb) { - getStore().setDrive([getHash(), attr].join('.'), value, function (err, data) { - cb(err, data); - }); + var href = getRelativeHref(window.location.href); + getStore().setPadAttribute(href, attr, value, cb); }; common.setAttribute = function (attr, value, cb) { getStore().set(["cryptpad", attr].join('.'), value, function (err, data) { @@ -429,9 +440,8 @@ define([ // STORAGE common.getPadAttribute = function (attr, cb) { - getStore().getDrive([getHash(), attr].join('.'), function (err, data) { - cb(err, data); - }); + var href = getRelativeHref(window.location.href); + getStore().getPadAttribute(href, attr, cb); }; common.getAttribute = function (attr, cb) { getStore().get(["cryptpad", attr].join('.'), function (err, data) { @@ -777,11 +787,32 @@ define([ }; 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); - rpc.getFileSize(channelId, function (e, bytes) { + anon_rpc.send("GET_FILE_SIZE", channelId, function (e, response) { if (e) { return void cb(e); } - cb(void 0, bytes); + if (response && response.length && typeof(response[0]) === 'number') { + return void cb(void 0, response[0]); + } else { + cb('INVALID_RESPONSE'); + } + }); + }; + + common.getMultipleFileSize = function (files, cb) { + if (!anon_rpc) { return void cb('ANON_RPC_NOT_READY'); } + if (!Array.isArray(files)) { + return void setTimeout(function () { cb('INVALID_FILE_LIST'); }); + } + + anon_rpc.send('GET_MULTIPLE_FILE_SIZE', files, function (e, res) { + if (e) { return cb(e); } + if (res && res.length && typeof(res[0]) === 'object') { + cb(void 0, res[0]); + } else { + cb('UNEXPECTED_RESPONSE'); + } }); }; @@ -800,7 +831,9 @@ define([ if (!pinsReady()) { return void cb('RPC_NOT_READY'); } 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.note) !== 'string') { return void rpc.getLimit(function (e, limit, plan, note) { @@ -863,7 +896,6 @@ define([ var $container = $('', {'class':'limit-container'}); var todo; var updateUsage = window.updateUsage = common.notAgainForAnother(function () { - console.log("updating usage bar"); common.getPinnedUsage(todo); }, LIMIT_REFRESH_RATE); @@ -933,21 +965,12 @@ define([ }; setInterval(function () { - var t = updateUsage(); - if (t) { - console.log("usage already updated. eligible for refresh in %sms", t); - } + updateUsage(); }, LIMIT_REFRESH_RATE * 3); updateUsage(); getProxy().on('change', ['drive'], function () { - var t = updateUsage(); - if (t) { - console.log("usage bar update throttled due to overuse." + - " Eligible for update in %sms", t); - } else { - console.log("usage bar updated"); - } + updateUsage(); }); cb(null, $container); }; @@ -1162,67 +1185,132 @@ define([ return button; }; + + var emoji_patt = /([\uD800-\uDBFF][\uDC00-\uDFFF])/; + var isEmoji = function (str) { + return emoji_patt.test(str); + }; + var emojiStringToArray = function (str) { + var split = str.split(emoji_patt); + var arr = []; + for (var i=0; i'; + if (title) { + text += '' + common.fixHTML(title) + '
'; + } + if (size) { + text += '' + Messages._getKey('formattedMB', [sizeMb]) + ''; + } + return text; + }); + }); + } + }); common.avatarAllowedTypes = [ 'image/png', 'image/jpeg', 'image/jpg', 'image/gif', ]; - common.displayAvatar = function ($container, href) { + common.displayAvatar = function ($container, href, name, cb) { var MutationObserver = window.MutationObserver; - $container.html(''); - if (href) { - var parsed = common.parsePadUrl(href); - var secret = common.getSecrets('file', parsed.hash); - if (secret.keys && secret.channel) { - var cryptKey = secret.keys && secret.keys.fileKeyStr; - var hexFileName = common.base64ToHex(secret.channel); - var src = common.getBlobPathFromHex(hexFileName); - common.getFileSize(href, function (e, data) { - if (e) { return void console.error(e); } - if (typeof data !== "number") { return; } - if (common.bytesToMegabytes(data) > 0.5) { return; } - var $img = $('').appendTo($container); - $img.attr('src', src); - $img.attr('data-crypto-key', 'cryptpad:' + cryptKey); - require(['/common/media-tag.js'], function (MediaTag) { - MediaTag.CryptoFilter.setAllowedMediaTypes(common.avatarAllowedTypes); - MediaTag($img[0]); - var observer = new MutationObserver(function(mutations) { - mutations.forEach(function(mutation) { - if (mutation.type === 'childList' && mutation.addedNodes.length) { - console.log(mutation); - if (mutation.addedNodes.length > 1 || - mutation.addedNodes[0].nodeName !== 'IMG') { - $img.remove(); - return; - //TODO display default avatar - } - var $image = $img.find('img'); - var onLoad = function () { - var w = $image.width(); - var h = $image.height(); - if (w>h) { - $image.css('max-height', '100%'); - $img.css('flex-direction', 'row'); - return; - } - $image.css('max-width', '100%'); - $img.css('flex-direction', 'column'); - }; - if ($image[0].complete) { onLoad(); } - $image.on('load', onLoad); + var displayDefault = function () { + var text = getFirstEmojiOrCharacter(name); + var $avatar = $('', {'class': 'default'}).text(text); + $container.append($avatar); + if (cb) { cb(); } + }; + + if (!href) { return void displayDefault(); } + var parsed = common.parsePadUrl(href); + var secret = common.getSecrets('file', parsed.hash); + if (secret.keys && secret.channel) { + var cryptKey = secret.keys && secret.keys.fileKeyStr; + var hexFileName = common.base64ToHex(secret.channel); + var src = common.getBlobPathFromHex(hexFileName); + common.getFileSize(href, function (e, data) { + if (e) { + displayDefault(); + return void console.error(e); + } + if (typeof data !== "number") { return void displayDefault(); } + if (common.bytesToMegabytes(data) > 0.5) { return void displayDefault(); } + var $img = $('').appendTo($container); + $img.attr('src', src); + $img.attr('data-crypto-key', 'cryptpad:' + cryptKey); + MediaTag($img[0]); + var observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + if (mutation.type === 'childList' && mutation.addedNodes.length) { + if (mutation.addedNodes.length > 1 || + mutation.addedNodes[0].nodeName !== 'IMG') { + $img.remove(); + return void displayDefault(); + } + var $image = $img.find('img'); + var onLoad = function () { + var w = $image.width(); + var h = $image.height(); + if (w>h) { + $image.css('max-height', '100%'); + $img.css('flex-direction', 'row'); + if (cb) { cb($img); } + return; } - }); - }); - observer.observe($img[0], { - attributes: false, - childList: true, - characterData: false - }); + $image.css('max-width', '100%'); + $img.css('flex-direction', 'column'); + if (cb) { cb($img); } + }; + if ($image[0].complete) { onLoad(); } + $image.on('load', onLoad); + } + }); + observer.observe($img[0], { + attributes: false, + childList: true, + characterData: false }); }); - } + }); } }; @@ -1301,7 +1389,7 @@ define([ setActive($val); $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) { @@ -1457,6 +1545,13 @@ define([ content: Messages.user_rename }); } + if (account) { + options.push({ + tag: 'a', + attributes: {'class': 'profile'}, + content: Messages.profileButton + }); + } if (parsed && (!parsed.type || parsed.type !== 'settings')) { options.push({ tag: 'a', @@ -1516,6 +1611,13 @@ define([ window.location.href = '/settings/'; } }); + $userAdmin.find('a.profile').click(function () { + if (parsed && parsed.type) { + window.open('/profile/'); + } else { + window.location.href = '/profile/'; + } + }); $userAdmin.find('a.login').click(function () { if (window.location.pathname !== "/") { sessionStorage.redirectTo = window.location.href; @@ -1666,6 +1768,21 @@ define([ console.log('pinning disabled'); } + block++; + require([ + '/common/rpc.js', + ], function (Rpc) { + Rpc.createAnonymous(network, function (e, call) { + if (e) { + console.error(e); + return void cb(); + } + anon_rpc = common.anon_rpc = env.anon_rpc = call; + cb(); + }); + }); + + // Everything's ready, continue... if($('#pad-iframe').length) { block++; diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js index 6d27920aa..a4ce0927f 100644 --- a/www/common/diffMarked.js +++ b/www/common/diffMarked.js @@ -171,11 +171,6 @@ define([ } }; - $(window.document).on('decryption', function (e) { - var decrypted = e.originalEvent; - if (decrypted.callback) { decrypted.callback(); } - }); - return DiffMd; }); diff --git a/www/common/fsStore.js b/www/common/fsStore.js index 05503beb8..b47d3c50e 100644 --- a/www/common/fsStore.js +++ b/www/common/fsStore.js @@ -60,6 +60,9 @@ define([ cb(void 0, res); }; + ret.setPadAttribute = filesOp.setAttribute; + ret.getPadAttribute = filesOp.getAttribute; + ret.getDrive = function (key, cb) { cb(void 0, storeObj.drive[key]); }; @@ -211,6 +214,10 @@ define([ if (typeof(n) !== "string") { return; } Cryptpad.changeDisplayName(n); }); + proxy.on('change', ['profile'], function () { + // Trigger userlist update when the avatar has changed + Cryptpad.changeDisplayName(proxy[Cryptpad.displayNameKey]); + }); proxy.on('change', [tokenKey], function () { console.log('wut'); var localToken = tryParsing(localStorage.getItem(tokenKey)); diff --git a/www/common/media-tag.js b/www/common/media-tag.js index 264ef602c..2d9a0180e 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.MediaTag=t():e.MediaTag=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=86)}([function(e,t,n){"use strict";var r={IMAGE:"image",AUDIO:"audio",VIDEO:"video",PDF:"pdf",DASH:"dash",DOWNLOAD:"download",CRYPTO:"crypto",CLEAR_KEY:"clear-key",MEDIA_OBJECT:"media-object"};e.exports=r},function(e,t,n){"use strict";var r={MATCHER:"matcher",RENDERER:"renderer",FILTER:"filter",SANITIZER:"sanitizer"};e.exports=r},function(e,t,n){"use strict";function r(e){if(e instanceof Array){var t=[];return e.forEach(function(e){e.mediaObject?t.push(e.mediaObject):t.push(r.start(e))}),t}var n=e;return r.start(n)}var o=n(25),i=n(24),u=n(44),a=n(45),c=n(23),s=n(22);r.createMediaObject=function(e){var t=new s(e),n=c.parse(t);return t.setProperties(n),t},r.start=function(e){var t=r.createMediaObject(e);return r.processingEngine.start(t)},r.pluginStore=r.pluginStore||new u,r.uriStore=r.uriStore||new a("../plugins"),r.processingEngine=r.processingEngine||new o(r.pluginStore),r.matchingEngine=r.matchingEngine||new i(r.pluginStore,r.uriStore),r.loadingEngine=null,e.exports=r},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=function(){function e(e,t){for(var n=0;n=0&&f.mediaTypes.splice(t,1)},f.removeAllAllowedMediaTypes=function(e){e.forEach(function(e){f.removeAllowedMediaType(e)})},f.isAllowedMediaType=function(e){return f.mediaTypes.some(function(t){return t===e})},e.exports=f},,function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var o=function(){function e(e,t){for(var n=0;n MediaTag cannot find a plugin able to renderer your content

","Download");r.processingEngine.setDefaultPlugin(b),r.CryptoFilter=f;var d=["image/png","image/jpeg","image/jpg","image/gif","audio/mp3","audio/ogg","audio/wav","audio/webm","video/mp4","video/ogg","video/webm","application/pdf","application/dash+xml","download"];r.CryptoFilter.setAllowedMediaTypes(d);var g=n(21),v=(n(13),n(0),new g);r.processingEngine.configure(v),e.exports=r},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=n(1),a=n(5),c=n(6),s=function(e){function t(e){return r(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,u.SANITIZER,a.EVERY))}return i(t,e),t}(c);e.exports=s},,,function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){var n=t.type.split("/"),r=t.type,o=n[0],i=n[1];s.isAllowedMediaType(r)?(e.setAttribute("data-type",t.type),e.type=o,e.extension=i,e.mime=r):console.log("Not allowed metadata, allowed ones are : ",s.getAllowedMediaTypes()),e.name=t.name,e.setAttribute("data-attr-type",t.type)}function i(e){var t=e.getAttribute("src"),n=e.getAttribute("data-crypto-key"),r=p.getKeyFromStr(n),i=new XMLHttpRequest;i.open("GET",t,!0),i.responseType="arraybuffer";var u=function(e){var t=new Event("decryptionError");t.message="string"==typeof e?e:e.message,window.document.dispatchEvent(t)};i.onload=function(){if(/^4/.test(""+this.status))return u("XHR_ERROR",""+this.status);var t=i.response;if(t){var n=new Uint8Array(t);p.decrypt(n,r,function(t,n){if(t)return u(t);var r=n.content,i=y.getBlobUrl(r,e.getMimeType()),a=new Event("decryption");a.blob=new Blob([r],{type:e.getMimeType()}),a.metadata=n.metadata,s.addAllowedMediaType("audio/mpeg"),e.setAttribute("src",i),e.removeAttribute("data-crypto-key"),console.log(n.metadata),/audio\/(mp3|ogg|wav|webm|mpeg)/.test(n.metadata.type)||/application\/pdf/.test(n.metadata.type)||/video\//.test(n.metadata.type)||/image\/(png|jpeg|jpg|gif)/.test(n.metadata.type)||(n.metadata.type="download"),console.log(n.metadata),o(e,n.metadata),a.callback=function(){c.processingEngine.return(e)},window.document.dispatchEvent(a)})}},i.send(null)}var u=function(){function e(e,t){for(var n=0;n1;){if(f){if("number"!=typeof e[t])throw new Error("E_UNSAFE_TYPE");if(e[t]>255)throw new Error("E_OUT_OF_BOUNDS")}if(255!==e[t])return void e[t]++;if(e[t]=0,0===t)throw new Error("E_NONCE_TOO_LARGE")}}},{key:"encodePrefix",value:function(e){return[65280,255].map(function(t,n){return(e&t)>>8*(1-n)})}},{key:"decodePrefix",value:function(e){return e[0]<<8|e[1]}},{key:"joinChunks",value:function(e){return new Blob(e)}},{key:"slice",value:function(e){return Array.prototype.slice.call(e)}},{key:"getRandomKeyStr",value:function(){var t=e.Nacl,n=t.randomBytes(18);return t.util.encodeBase64(n)}},{key:"getKeyFromStr",value:function(t){return e.Nacl.util.decodeBase64(t)}},{key:"encrypt",value:function(t,n){var r=t,o=e.Nacl.randomBytes(24),i=e.Nacl.secretbox(r,o,n);if(i)return new Uint8Array(e.slice(o).concat(e.slice(i)));throw new Error}},{key:"decrypt",value:function(t,n,r){var o=e.Nacl,i=function(e){var n=new Event("decryptionProgress");n.percent=e/t.length*100,window.document.dispatchEvent(n)},u=e.createNonce(),a=0,c=t.subarray(0,2),s=e.decodePrefix(c),f={metadata:void 0},p=new Uint8Array(t.subarray(2,2+s)),y=o.secretbox.open(p,u,n);e.increment(u);try{f.metadata=JSON.parse(o.util.encodeUTF8(y))}catch(e){return r("E_METADATA_DECRYPTION")}if(!f.metadata)return r("NO_METADATA");var h=function(r){setTimeout(function(){var c=a*l+2+s,f=c+l;a++;var p=new Uint8Array(t.subarray(c,f)),y=o.secretbox.open(p,u,n);if(e.increment(u),!y)return void r("DECRYPTION_FAILURE");i(Math.min(f,t.length)),r(void 0,y)})},b=[];!function n(){h(function(o,i){return o?setTimeout(function(){r(o)}):i?a*l1?t[0]:window.location.protocol}},{key:"hostname",value:function(e){var t=e.getAttribute("src").split("://");return t.length>1?t[1].split("/")[0]:window.location.hostname}},{key:"source",value:function(e){return e.getAttribute("src")}},{key:"schemes",value:function(e){return/\w+:/.exec(e.getAttribute("src"))}},{key:"parse",value:function(t){return{protocol:e.protocol(t),hostname:e.hostname(t),src:e.source(t),type:e.type(t),extension:e.extension(t),mime:e.mime(t)}}}]),e}();e.exports=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var o=function(){function e(e,t){for(var n=0;n=e.STACK_LIMIT)throw console.error(this.snapshots[n]),new Error("Plugin stack size exceed");if(this.snapshots[n].length>=e.SNAPSHOT_LIMIT)throw console.error(this.snapshots[n]),new Error("Plugin snapshots size exceed");var r=0;if(this.stacks[n].forEach(function(e){e.type===a.RENDERER&&r++}),r>1)throw console.error(this.snapshots[n]),new Error("More of one renderer in the stack");if(0===this.stacks[n].length&&!this.stats[n][a.RENDERER]){if(!this.defaultPlugin)throw new Error("No default plugin assignated");this.stacks[n].unshift(this.defaultPlugin)}}},{key:"return",value:function(e){var t=e.getId(),n=this.unstack(e);this.stats[t]||(this.stats[t]={}),this.stats[t][n.type]?this.stats[t][n.type]+=1:this.stats[t][n.type]=1,0===this.stacks[t].length&&n.type===a.RENDERER?this.run(e):n.type!==a.SANITIZER&&this.fill(e),this.snapshot(e),this.check(e),this.run(e)}},{key:"process",value:function(e){var t=e.getId(),n=this.stacks[t].length,r=this.stacks[t][n-1];if(!r)throw console.log(this.stacks),new Error("Impossible to run a undefined plugin");r.process(e)}},{key:"isStacked",value:function(e,t){var n=e.getId();return!(!this.stacks[n]||!this.stacks[n].includes(t))}},{key:"setDefaultPlugin",value:function(e){this.defaultPlugin=e}}]),e}();f.STACK_LIMIT=100,f.SNAPSHOT_LIMIT=100,e.exports=f},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=function(){function e(e,t){for(var n=0;n=0){CryptoFilter.mediaTypes.splice(index,1)}};CryptoFilter.removeAllAllowedMediaTypes=function(mediaTypes){mediaTypes.forEach(function(mediaType){CryptoFilter.removeAllowedMediaType(mediaType)})};CryptoFilter.isAllowedMediaType=function(mediaType){return CryptoFilter.mediaTypes.some(function(type){return type===mediaType})};module.exports=CryptoFilter},,function(module,exports,__webpack_require__){"use strict";var _createClass=function(){function defineProperties(target,props){for(var i=0;i MediaTag cannot find a plugin able to renderer your content

","Download");MediaTag.processingEngine.setDefaultPlugin(defaultPlugin);MediaTag.CryptoFilter=CryptoFilter;var allowedMediaTypes=["image/png","image/jpeg","image/jpg","image/gif","audio/mp3","audio/ogg","audio/wav","audio/webm","video/mp4","video/ogg","video/webm","application/pdf","application/dash+xml","download"];MediaTag.CryptoFilter.setAllowedMediaTypes(allowedMediaTypes);var Configuration=__webpack_require__(21);var Permission=__webpack_require__(13);var Identifier=__webpack_require__(0);var configuration=new Configuration;MediaTag.processingEngine.configure(configuration);module.exports=MediaTag},function(module,exports,__webpack_require__){"use strict";function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function")}}function _possibleConstructorReturn(self,call){if(!self){throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}return call&&(typeof call==="object"||typeof call==="function")?call:self}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function, not "+typeof superClass)}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass}var Type=__webpack_require__(1);var Occurrence=__webpack_require__(5);var Plugin=__webpack_require__(6);var Sanitizer=function(_Plugin){_inherits(Sanitizer,_Plugin);function Sanitizer(identifier){_classCallCheck(this,Sanitizer);return _possibleConstructorReturn(this,(Sanitizer.__proto__||Object.getPrototypeOf(Sanitizer)).call(this,identifier,Type.SANITIZER,Occurrence.EVERY))}return Sanitizer}(Plugin);module.exports=Sanitizer},,,function(module,exports,__webpack_require__){"use strict";var _createClass=function(){function defineProperties(target,props){for(var i=0;i1){if(PARANOIA){if(typeof N[l]!=="number"){throw new Error("E_UNSAFE_TYPE")}if(N[l]>255){throw new Error("E_OUT_OF_BOUNDS")}}if(N[l]!==255){return void N[l]++}N[l]=0;if(l===0){throw new Error("E_NONCE_TOO_LARGE")}}}},{key:"encodePrefix",value:function encodePrefix(p){return[65280,255].map(function(n,i){return(p&n)>>(1-i)*8})}},{key:"decodePrefix",value:function decodePrefix(A){return A[0]<<8|A[1]}},{key:"joinChunks",value:function joinChunks(chunks){return new Blob(chunks)}},{key:"slice",value:function slice(u8){return Array.prototype.slice.call(u8)}},{key:"getRandomKeyStr",value:function getRandomKeyStr(){window.nacl.randomBytes(18);return window.nacl.util.encodeBase64(rdm)}},{key:"getKeyFromStr",value:function getKeyFromStr(str){return window.nacl.util.decodeBase64(str)}},{key:"encrypt",value:function encrypt(u8,key){var array=u8;var nonce=window.nacl.randomBytes(24);var packed=window.nacl.secretbox(array,nonce,key);if(packed){return new Uint8Array(Cryptopad.slice(nonce).concat(Cryptopad.slice(packed)))}throw new Error}},{key:"decrypt",value:function decrypt(u8,key,done){var Nacl=window.nacl;var progress=function progress(offset){var ev=new Event("decryptionProgress");ev.percent=offset/u8.length*100;window.document.dispatchEvent(ev)};var nonce=Cryptopad.createNonce();var i=0;var prefix=u8.subarray(0,2);var metadataLength=Cryptopad.decodePrefix(prefix);var res={metadata:undefined};var metaBox=new Uint8Array(u8.subarray(2,2+metadataLength));var metaChunk=window.nacl.secretbox.open(metaBox,nonce,key);Cryptopad.increment(nonce);try{res.metadata=JSON.parse(window.nacl.util.encodeUTF8(metaChunk))}catch(e){return done("E_METADATA_DECRYPTION")}if(!res.metadata){return done("NO_METADATA")}var takeChunk=function takeChunk(cb){setTimeout(function(){var start=i*cypherChunkLength+2+metadataLength;var end=start+cypherChunkLength;i++;var box=new Uint8Array(u8.subarray(start,end));var plaintext=window.nacl.secretbox.open(box,nonce,key);Cryptopad.increment(nonce);if(!plaintext){return void cb("DECRYPTION_FAILURE")}progress(Math.min(end,u8.length));cb(void 0,plaintext)})};var chunks=[];var again=function again(){takeChunk(function(e,plaintext){if(e){return setTimeout(function(){done(e)})}if(plaintext){if(i*cypherChunkLength1){return array[0]}return window.location.protocol}},{key:"hostname",value:function hostname(mediaObject){var array=mediaObject.getAttribute("src").split("://");if(array.length>1){return array[1].split("/")[0]}return window.location.hostname}},{key:"source",value:function source(mediaObject){var source=mediaObject.getAttribute("src");return source}},{key:"schemes",value:function schemes(mediaObject){return/\w+:/.exec(mediaObject.getAttribute("src"))}},{key:"parse",value:function parse(mediaObject){return{protocol:Parser.protocol(mediaObject),hostname:Parser.hostname(mediaObject),src:Parser.source(mediaObject),type:Parser.type(mediaObject),extension:Parser.extension(mediaObject),mime:Parser.mime(mediaObject)}}}]);return Parser}();module.exports=Parser},function(module,exports,__webpack_require__){"use strict";var _createClass=function(){function defineProperties(target,props){for(var i=0;i=ProcessingEngine.STACK_LIMIT){console.error(this.snapshots[stackId]);throw new Error("Plugin stack size exceed")}if(this.snapshots[stackId].length>=ProcessingEngine.SNAPSHOT_LIMIT){console.error(this.snapshots[stackId]);throw new Error("Plugin snapshots size exceed")}var rendererCount=0;this.stacks[stackId].forEach(function(plugin){if(plugin.type===Type.RENDERER){rendererCount++}});if(rendererCount>1){console.error(this.snapshots[stackId]);throw new Error("More of one renderer in the stack")}if(this.stacks[stackId].length===0&&!this.stats[stackId][Type.RENDERER]){if(!this.defaultPlugin){throw new Error("No default plugin assignated")}this.stacks[stackId].unshift(this.defaultPlugin)}}},{key:"return",value:function _return(mediaObject){var stackId=mediaObject.getId();var plugin=this.unstack(mediaObject);if(!this.stats[stackId]){this.stats[stackId]={}}if(this.stats[stackId][plugin.type]){this.stats[stackId][plugin.type]+=1}else{this.stats[stackId][plugin.type]=1}if(this.stacks[stackId].length===0&&plugin.type===Type.RENDERER){this.run(mediaObject)}else if(plugin.type!==Type.SANITIZER){this.fill(mediaObject)}this.snapshot(mediaObject);this.check(mediaObject);this.run(mediaObject)}},{key:"process",value:function process(mediaObject){var stackId=mediaObject.getId();var size=this.stacks[stackId].length;var plugin=this.stacks[stackId][size-1];if(plugin){plugin.process(mediaObject)}else{console.log(this.stacks);throw new Error("Impossible to run a undefined plugin")}}},{key:"isStacked",value:function isStacked(mediaObject,plugin){var stackId=mediaObject.getId();if(this.stacks[stackId]){if(this.stacks[stackId].includes(plugin)){return true}}return false}},{key:"setDefaultPlugin",value:function setDefaultPlugin(plugin){this.defaultPlugin=plugin}}]);return ProcessingEngine}();ProcessingEngine.STACK_LIMIT=100;ProcessingEngine.SNAPSHOT_LIMIT=100;module.exports=ProcessingEngine},function(module,exports,__webpack_require__){"use strict";var _createClass=function(){function defineProperties(target,props){for(var i=0;i', {'class': DRAWER_CLS}).appendTo($rightside).hide(); + var $drawerContent = $('
', { + 'class': DRAWER_CLS,// + ' dropdown-bar-content cryptpad-dropdown' + 'tabindex': 1 + }).appendTo($rightside).hide(); var $drawer = Cryptpad.createButton('more', true).appendTo($rightside); $drawer.click(function () { $drawerContent.toggle(); $drawer.removeClass('active'); if ($drawerContent.is(':visible')) { $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 @@ -149,6 +165,7 @@ define([ return $.inArray(i, b) > -1; }); }; + var avatars = {}; var updateUserList = function (toolbar, config) { // Make sure the elements are displayed var $userButtons = toolbar.userlist; @@ -189,7 +206,25 @@ define([ // Editors editUsersNames.forEach(function (data) { var name = data.name || Messages.anonymous; - var $span = $('', {'title': name}).text(name); + var $name = $('', {'class': 'name'}).text(name); + var $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); $editUsersList.append($span); }); @@ -197,9 +232,9 @@ define([ // Viewers if (numberOfViewUsers > 0) { - var viewText = ''; + var viewText = '
'; var viewerText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer; - viewText += numberOfViewUsers + ' ' + viewerText + ''; + viewText += numberOfViewUsers + ' ' + viewerText + '
'; $editUsers.append(viewText); } @@ -299,6 +334,11 @@ define([ if ($content.is(':visible')) { return void show(); } 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); $button.click(function () { 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! var $aTag = $('', { - href: "/", + href: "/drive/", title: Messages.header_logoTitle, 'class': "cryptpad-logo fa fa-hdd-o" }); var onClick = function (e) { e.preventDefault(); if (e.ctrlKey) { - window.open('/drive'); + window.open('/drive/'); return; } - window.location = "/drive"; + window.location = "/drive/"; }; var onContext = function (e) { e.stopPropagation(); }; diff --git a/www/common/userObject.js b/www/common/userObject.js index b21fb739a..7a0dba83c 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -132,6 +132,21 @@ define([ if (type === 'name') { return data.filename; } 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 @@ -970,6 +985,20 @@ define([ 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 () { if (typeof files[FILES_DATA] !== "object") { debug("OLD_FILES_DATA was not an object"); files[FILES_DATA] = {}; } var fd = files[FILES_DATA]; @@ -989,6 +1018,15 @@ define([ toClean.push(id); 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) { debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el); 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(); fixTrashRoot(); if (!workgroup) { fixTemplate(); fixFilesData(); } + fixDrive(); 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"); diff --git a/www/drive/file.less b/www/drive/file.less index 2a158cf12..9bb4eccf4 100644 --- a/www/drive/file.less +++ b/www/drive/file.less @@ -621,7 +621,7 @@ span { margin: 0; } button { - height: 100%; + height: 32px; padding: 0 10px; border: none; border-radius: 0; diff --git a/www/drive/main.js b/www/drive/main.js index b67d11c74..d31c701a6 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -1170,7 +1170,7 @@ define([ var element = filesOp.find(newPath); var $icon = !isFolder ? getFileIcon(element) : undefined; 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 liClass = 'file-item file-element element' + roClass; if (isFolder) { @@ -2140,6 +2140,7 @@ define([ $trashContextMenu.hide(); $contentContextMenu.hide(); $defaultContextMenu.hide(); + $iframe.find('.cryptpad-dropdown').hide(); }; var stringifyPath = function (path) { diff --git a/www/drive/tests.js b/www/drive/tests.js index ddc3b0fb7..5861e5410 100644 --- a/www/drive/tests.js +++ b/www/drive/tests.js @@ -242,6 +242,18 @@ define([ fo.migrate(todo); }, "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 // UTILS diff --git a/www/file/main.js b/www/file/main.js index 638db697d..db094d421 100644 --- a/www/file/main.js +++ b/www/file/main.js @@ -7,12 +7,14 @@ define([ '/common/visible.js', '/common/notify.js', '/file/file-crypto.js', + + '/common/media-tag.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', '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 saveAs = window.saveAs; var Nacl = window.nacl; @@ -91,7 +93,9 @@ define([ $(window.document).on('decryption', function (e) { var decrypted = e.originalEvent; - if (decrypted.callback) { decrypted.callback(); } + if (decrypted.callback) { + decrypted.callback(); + } console.log(decrypted); $dlview.show(); @@ -131,32 +135,30 @@ define([ 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. - * - * @type {Array} - */ - var allowedMediaTypes = [ - 'image/png', - 'image/jpeg', - 'image/jpg', - 'image/gif', - 'audio/mp3', - 'audio/ogg', - 'audio/wav', - 'audio/webm', - 'video/mp4', - 'video/ogg', - 'video/webm', - 'application/pdf', - 'application/dash+xml', - 'download' - ]; - MediaTag.CryptoFilter.setAllowedMediaTypes(allowedMediaTypes); + /** + * Allowed mime types that have to be set for a rendering after a decryption. + * + * @type {Array} + */ + var allowedMediaTypes = [ + 'image/png', + 'image/jpeg', + 'image/jpg', + 'image/gif', + 'audio/mp3', + 'audio/ogg', + 'audio/wav', + 'audio/webm', + 'video/mp4', + 'video/ogg', + 'video/webm', + 'application/pdf', + 'application/dash+xml', + 'download' + ]; + MediaTag.CryptoFilter.setAllowedMediaTypes(allowedMediaTypes); - MediaTag($mt[0]); - }); + MediaTag($mt[0]); }; var todoBigFile = function (sizeMb) { @@ -181,9 +183,6 @@ define([ }; Cryptpad.getFileSize(window.location.href, function (e, data) { 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); } var size = Cryptpad.bytesToMegabytes(data); diff --git a/www/pad/inner.html b/www/pad/inner.html index 78e079285..ce0556eeb 100644 --- a/www/pad/inner.html +++ b/www/pad/inner.html @@ -14,6 +14,7 @@ #cke_1_top { overflow: visible; padding: 0px; + display: flex; } #cke_1_toolbox { display: inline-block; diff --git a/www/pad/main.js b/www/pad/main.js index bb046b5cb..ea360c7d4 100644 --- a/www/pad/main.js +++ b/www/pad/main.js @@ -570,16 +570,10 @@ define([ // this should only ever get called once, when the chain syncs realtimeOptions.onReady = function (info) { if (!module.isMaximized) { - editor.execCommand('maximize'); 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'); - } + $iframe.find('iframe.cke_wysiwyg_frame').css('width', ''); + $iframe.find('iframe.cke_wysiwyg_frame').css('height', ''); } - // editor.execCommand('maximize') removes all the classes from the body tag $iframe.find('body').addClass('app-pad'); if (module.realtime !== info.realtime) { @@ -727,12 +721,11 @@ define([ if (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'); + 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'); + $('meta[name=viewport]').attr('content', 'width=device-width, initial-scale=1.0, user-scalable=yes'); } second(Ckeditor); } else { diff --git a/www/poll/main.js b/www/poll/main.js index b8085f56a..ecb308a36 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -552,11 +552,19 @@ var ready = function (info, userid, readOnly) { } else { 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) { APP.proxy.info.title = Cryptpad.initialName; - Title.updateTitle(Cryptpad.initialName); + Title.updateTitle(Cryptpad.initialName, null, andThen); } else { - Title.updateTitle(APP.proxy.info.title || Title.defaultTitle); + Title.updateTitle(APP.proxy.info.title || Title.defaultTitle, null, andThen); } // Description @@ -621,6 +629,7 @@ var ready = function (info, userid, readOnly) { } else { publish(true); } + Cryptpad.removeLoadingScreen(); if (readOnly) { return; } @@ -760,10 +769,7 @@ var create = function (info) { if (e) { console.error(e); } if (!userid) { userid = Render.coluid(); } APP.userid = userid; - Cryptpad.setPadAttribute('userid', userid, function (e) { - if (e) { console.error(e); } - ready(info, userid, readOnly); - }); + ready(info, userid, readOnly); }); }) .on('disconnect', disconnect) diff --git a/www/poll/poll.css b/www/poll/poll.css deleted file mode 100644 index 620cf8710..000000000 --- a/www/poll/poll.css +++ /dev/null @@ -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; -} diff --git a/www/poll/poll.less b/www/poll/poll.less index 47e419bfb..6153f320e 100644 --- a/www/poll/poll.less +++ b/www/poll/poll.less @@ -22,6 +22,7 @@ html, body { body { display: flex; flex-flow: column; + overflow-x: hidden; } #content { display: flex; diff --git a/www/profile/main.js b/www/profile/main.js index eb836d024..e87bc1f4b 100644 --- a/www/profile/main.js +++ b/www/profile/main.js @@ -11,7 +11,6 @@ define([ '/bower_components/marked/marked.min.js', 'cm/lib/codemirror', 'cm/mode/markdown/markdown', - '/bower_components/tweetnacl/nacl-fast.min.js', 'less!/profile/main.less', ], function ($, Cryptpad, Listmap, Crypto, Marked, CodeMirror) { @@ -105,7 +104,7 @@ define([ if (err) { return void console.error(err); } Cryptpad.whenRealtimeSyncs(realtime, function () { lastVal = newVal; - Cryptpad.log('TODO: '+name+' saved'); + Cryptpad.log(Messages._getKey('profile_fieldSaved', [newVal])); editing = false; }); }); @@ -156,11 +155,11 @@ define([ var getValue = function (cb) { cb(APP.lm.proxy.name); }; - var placeholder = Messages.anonymous; + var placeholder = Messages.profile_namePlaceholder; if (APP.readOnly) { var $span = $('', {'class': DISPLAYNAME_ID}).appendTo($block); getValue(function (value) { - $span.text(value || placeholder); + $span.text(value || Messages.anonymous); }); return; } @@ -196,7 +195,7 @@ define([ cb(); }; var rt = APP.lm.realtime; - var placeholder = "URL"; //XXX + var placeholder = Messages.profile_urlPlaceholder; createEditableInput($block, LINK_ID, placeholder, getValue, setValue, rt); }; @@ -209,7 +208,7 @@ define([ if (!APP.lm.proxy.avatar) { $('', { src: '/customize/images/avatar.png', - title: 'Avatar', // XXX + title: Messages.profile_avatar, alt: 'Avatar' }).appendTo($span); return; @@ -218,21 +217,20 @@ define([ if (APP.readOnly) { return; } - var $delButton = $('