diff --git a/customize.dist/ckeditor-config.js b/customize.dist/ckeditor-config.js index 293c4b600..c55f5fe99 100644 --- a/customize.dist/ckeditor-config.js +++ b/customize.dist/ckeditor-config.js @@ -11,8 +11,6 @@ CKEDITOR.editorConfig = function( config ) { config.removePlugins= 'resize,elementspath'; config.resize_enabled= false; //bottom-bar config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock,justify,mediatag,print,blockbase64,mathjax,wordcount,comments'; - // FIXME translation for default? updating to a newer CKEditor seems like it will add 'default' by default - config.fontSize_sizes = '(Default)/unset;8/8px;9/9px;10/10px;11/11px;12/12px;14/14px;16/16px;18/18px;20/20px;22/22px;24/24px;26/26px;28/28px;36/36px;48/48px;72/72px'; config.toolbarGroups= [ // {"name":"clipboard","groups":["clipboard","undo"]}, //{"name":"editing","groups":["find","selection"]}, diff --git a/www/pad/app-pad.less b/www/pad/app-pad.less index ca8b10d18..3f6e0b455 100644 --- a/www/pad/app-pad.less +++ b/www/pad/app-pad.less @@ -24,6 +24,9 @@ body.cp-app-pad { align-items: center; padding: 4px; } + .cke_button__comment_label { + display: inline !important; + } } .cke_wysiwyg_frame { width: 100%; diff --git a/www/pad/comment.js b/www/pad/comment.js index 1bd184139..0a9e78db9 100644 --- a/www/pad/comment.js +++ b/www/pad/comment.js @@ -15,13 +15,8 @@ //icons: 'image', //hidpi: true, onLoad: function () { - - /* - CKEDITOR.addCss( - 'media-tag *{' + - 'width:100%; height:100%;' + - '}'); - */ + CKEDITOR.addCss('comment { background-color: rgba(252, 165, 3, 0.8); }' + + 'comment * { background-color: transparent !important; }'); }, init: function (editor) { var pluginName = 'comment'; @@ -41,31 +36,62 @@ childRule: isUnstylable }; +// XXX define default style +// XXX we can't uncomment if nothing has been added yet +// XXX "styles" is useless because not rebuilt on reload +// XXX and one style can remove all the other ones so no need to store all of them? + // Register the command. var removeStyle = new CKEDITOR.style(styleDef, { 'uid': '' }); editor.addCommand(pluginName, { exec: function (editor, data) { if (editor.readOnly) { return; } editor.focus(); - editor.fire('saveSnapshot'); - // XXX call cryptpad code here - Object.keys(styles).forEach(function (id) { - editor.removeStyle(styles[id]); - }); + + // If we're inside another comment, abort + var isComment = removeStyle.checkActive(editor.elementPath(), editor); + if (isComment) { return; } + + // We can't comment on empty text! + if (!editor.getSelection().getSelectedText()) { return; } + var uid = CKEDITOR.tools.getUniqueId(); - styles[uid] = new CKEDITOR.style(styleDef, { 'uid': uid }); - editor.applyStyle(styles[uid]); + editor.plugins.comments.addComment(uid, function () { + // XXX call cryptpad code here + editor.fire('saveSnapshot'); + editor.removeStyle(removeStyle); + /* + Object.keys(styles).forEach(function (id) { + editor.removeStyle(styles[id]); + }); + */ + styles[uid] = new CKEDITOR.style(styleDef, { 'uid': uid }); + editor.applyStyle(styles[uid]); + + //editor.removeStyle(removeStyle); // XXX to remove comment on the selection + //editor.plugins.comments.addComment(); + // Save the undo snapshot after all changes are affected. + setTimeout( function() { + editor.fire('saveSnapshot'); + }, 0 ); + }); - //editor.removeStyle(removeStyle); // XXX to remove comment on the selection - //editor.plugins.comments.addComment(); - // Save the undo snapshot after all changes are affected. + } + }); + + // XXX Uncomment selection, remove on prod, only used for dev + editor.addCommand('uncomment', { + exec: function (editor, data) { + if (editor.readOnly) { return; } + editor.focus(); + editor.fire('saveSnapshot'); + editor.removeStyle(removeStyle); setTimeout( function() { editor.fire('saveSnapshot'); }, 0 ); } }); - // Register the toolbar button. editor.ui.addButton && editor.ui.addButton('UnComment', { label: 'UNCOMMENT', diff --git a/www/pad/comments.js b/www/pad/comments.js new file mode 100644 index 000000000..e930f65f5 --- /dev/null +++ b/www/pad/comments.js @@ -0,0 +1,105 @@ +define([ + 'json.sortify', + '/common/common-util.js', + '/common/common-interface.js', + '/customize/messages.js' +], function (Sortify, Util, UI, Messages) { + var Comments = {}; + + var COMMENTS = { + authors: {}, + messages: {} + }; + + // XXX function duplicated from www/code/markers.js + var authorUid = function (existing) { + if (!Array.isArray(existing)) { existing = []; } + var n; + var i = 0; + while (!n || existing.indexOf(n) !== -1 && i++ < 1000) { + n = Math.floor(Math.random() * 1000000); + } + // If we can't find a valid number in 1000 iterations, use 0... + if (existing.indexOf(n) !== -1) { n = 0; } + return n; + }; + var getAuthorId = function (Env, curve) { + var existing = Object.keys(Env.comments.authors || {}).map(Number); + if (!Env.common.isLoggedIn()) { return authorUid(existing); } + + var uid; + existing.some(function (id) { + var author = Env.comments.authors[id] || {}; + if (author.curvePublic !== curvePublic) { return; } + uid = Number(id); + return true; + }); + return uid || authorUid(existing); + }; + + var updateAuthorData = function (Env) { + var userData = Env.metadataMgr.getUserData(); + var myAuthorId = getAuthorId(Env, userData.curvePublic); + var data = Env.comments.authors[myAuthorId] = Env.comments.authors[myAuthorId] || {}; + data.name = userData.name; + data.avatar = userData.avatar; + data.profile = userData.profile; + data.curvePublic = userData.curvePublic; + console.log(data); + return myAuthorId; + }; + + var onChange = function (Env) { + var md = Util.clone(Env.metadataMgr.getMetadata()); + Env.comments = md.comments; + if (!Env.comments) { Env.comments = Util.clone(COMMENTS); } + }; + + Comments.create = function (cfg) { + var Env = cfg; + Env.comments = Util.clone(COMMENTS); + + Env.editor.plugins.comments.addComment = function (uid, addMark) { + if (!Env.comments) { Env.comments = Util.clone(COMMENTS); } + + UI.prompt("Message", "", function (val) { // XXX + if (!val) { return; } + if (!editor.getSelection().getSelectedText()) { + // text has been deleted by another user while we were typing our comment? + return void UI.warn(Messages.error); + } + var myId = updateAuthorData(Env); + Env.comments.messages[uid] = { + user: myId, + time: +new Date(), + message: val + }; + var md = Util.clone(Env.metadataMgr.getMetadata()); + md.comments = Util.clone(Env.comments); + metadataMgr.updateMetadata(md); + + addMark(); + + Env.framework.localChange(); + }); + }; + + var call = function (f) { + return function () { + try { + [].unshift.call(arguments, Env); + return f.apply(null, arguments); + } catch (e) { + console.error(e); + } + }; + }; + + Env.metadataMgr.onChange(call(onChange)); + + return { + }; + }; + + return Comments; +}); diff --git a/www/pad/inner.js b/www/pad/inner.js index 591f546cf..68272f83a 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -25,6 +25,7 @@ define([ '/common/TypingTests.js', '/customize/messages.js', '/pad/links.js', + '/pad/comments.js', '/pad/export.js', '/pad/cursor.js', '/bower_components/nthen/index.js', @@ -51,6 +52,7 @@ define([ TypingTest, Messages, Links, + Comments, Exporter, Cursors, nThen, @@ -461,9 +463,9 @@ define([ framework._.sfCommon.addShortcuts(ifrWindow); - var privateData = framework._.sfCommon.getMetadataMgr().getPrivateData(); - var documentBody = ifrWindow.document.body; + var inner = window.inner = documentBody; + var $inner = $(inner); var observer = new MutationObserver(function (muts) { muts.forEach(function (mut) { @@ -483,8 +485,38 @@ define([ childList: true }); - var inner = window.inner = documentBody; - var $inner = $(inner); + var metadataMgr = framework._.sfCommon.getMetadataMgr(); + var privateData = metadataMgr.getPrivateData(); + var userData = metadataMgr.getUserData(); + var common = framework._.sfCommon; + + var comments = Comments.create({ + framework: framework, + metadataMgr: metadataMgr, + common: common, + editor: editor + }); + + editor.plugins.comments.addComment = function (uid, cb) { + if (!comments) { + comments = Util.clone(COMMENTS); + } + // XXX display input + UI.prompt("Message", "", function (val) { + if (!val) { return; } + if (!editor.getSelection().getSelectedText()) { + // text has been deleted by another user while we were typing our comment? + return void UI.warn(Messages.error); + } + var myId = updateAuthorData(); + comments.messages[uid] = { + user: myId, + time: +new Date(), + message: val + }; + cb(); + }); + }; var onLinkClicked = function (e) { var $target = $(e.target); @@ -650,6 +682,12 @@ define([ // off so that we don't end up with multiple identical handlers $links.off('click', openLink).on('click', openLink); } + + // XXX check comments + // new comments + // deleted comments + // check comment authors too + }); framework.setTextContentGetter(function () { @@ -997,9 +1035,6 @@ define([ }).nThen(function () { editor.plugins.mediatag.import = function ($mt) { framework._.sfCommon.importMediaTag($mt); - }; - editor.plugins.comments.addComment = function (uid, cb) { - }; Links.addSupportForOpeningLinksInNewTab(Ckeditor)({editor: editor}); }).nThen(function () {