Merge remote-tracking branch 'origin/communities-comments' into staging
commit
84909f8c30
|
@ -9,7 +9,7 @@
|
|||
@alertify-input-bg: @colortheme_modal-input;
|
||||
@alertify-input-fg: @colortheme_modal-input-fg;
|
||||
|
||||
input:not(.form-control), textarea {
|
||||
input:not(.form-control), textarea, div.cp-textarea {
|
||||
// background-color: @alertify-input-fg;
|
||||
color: @cryptpad_text_col;
|
||||
border: 1px solid @alertify-input-bg;
|
||||
|
@ -44,18 +44,29 @@
|
|||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
textarea, div.cp-textarea {
|
||||
padding: 8px;
|
||||
&[readonly] {
|
||||
overflow: hidden;
|
||||
resize: none;
|
||||
}
|
||||
}
|
||||
div.cp-textarea {
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
cursor: text;
|
||||
outline: none;
|
||||
white-space: pre-wrap;
|
||||
overflow-y: auto;
|
||||
word-break: break-word;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
div.cp-button-confirm {
|
||||
display: inline-block;
|
||||
button {
|
||||
margin: 0;
|
||||
margin: 0 !important;
|
||||
}
|
||||
.cp-button-timer {
|
||||
height: 3px;
|
||||
|
|
|
@ -7,37 +7,66 @@
|
|||
.comments_main() {
|
||||
@data-color: #888;
|
||||
overflow-y: auto;
|
||||
color: @cryptpad_text_col;
|
||||
|
||||
&.cp-comments-readonly {
|
||||
.cp-comment-actions {
|
||||
display: none !important;
|
||||
}
|
||||
.cp-comment-form {
|
||||
display: none !important;
|
||||
}
|
||||
.cp-comment-edit {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.buttons_main();
|
||||
|
||||
.cp-comment-reply {
|
||||
margin-left: 40px;
|
||||
margin-left: 30px;
|
||||
}
|
||||
.cp-comment-deleted {
|
||||
background: white;
|
||||
font-size: 14px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
|
||||
.cp-comment-form {
|
||||
&:not(:last-child) {
|
||||
padding: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
.cp-comment-form-input {
|
||||
.avatar_main(40px);
|
||||
.cp-avatar {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
textarea {
|
||||
div.cp-textarea {
|
||||
flex: 1;
|
||||
height: 50px;
|
||||
padding: 2px 8px;
|
||||
min-height: 52px; // 22px per line + 8 (padding+border)
|
||||
height: unset !important;
|
||||
max-height: 140px; // 6 lines
|
||||
padding: 3px 5px;
|
||||
}
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.cp-comment-form-actions {
|
||||
text-align: right;
|
||||
margin-left: -30px;
|
||||
button:not(:last-child) {
|
||||
margin-right: 10px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
#cp-comments-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cp-comment-container {
|
||||
outline: none;
|
||||
|
@ -52,6 +81,12 @@
|
|||
margin-top: 5px;
|
||||
}
|
||||
padding: 5px;
|
||||
&:nth-child(2) {
|
||||
margin-top: 10px;
|
||||
};
|
||||
&:last-child {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
.cp-comment {
|
||||
&:not(:first-child) {
|
||||
|
@ -63,6 +98,9 @@
|
|||
align-items: center;
|
||||
display: flex;
|
||||
background-color: white;
|
||||
position: relative;
|
||||
padding: 5px;
|
||||
box-sizing: content-box;
|
||||
.avatar_main(40px);
|
||||
.cp-comment-metadata {
|
||||
flex: 1;
|
||||
|
@ -74,12 +112,32 @@
|
|||
color: @data-color;
|
||||
}
|
||||
}
|
||||
.cp-comment-edit {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
&:hover {
|
||||
color: lighten(@cryptpad_text_col, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-comment-content {
|
||||
background-color: white;
|
||||
padding: 10px 5px 5px;
|
||||
padding: 0px 5px 5px 5px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
}
|
||||
.cp-comment-edited {
|
||||
background-color: white;
|
||||
font-size: 13px;
|
||||
color: @data-color;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.cp-comment-actions {
|
||||
display: none;
|
||||
|
@ -89,7 +147,7 @@
|
|||
margin-bottom: 0 !important;
|
||||
}
|
||||
button:not(:last-child) {
|
||||
margin-right: 10px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.cp-comment-active {
|
||||
|
|
|
@ -1,11 +1,30 @@
|
|||
@import (reference) "./colortheme-all.less";
|
||||
@import (reference) "./tools.less";
|
||||
@import (reference) "./avatar.less";
|
||||
|
||||
/* The container <div> - needed to position the dropdown content */
|
||||
.dropdown_main () {
|
||||
--LessLoader_require: LessLoader_currentFile();
|
||||
}
|
||||
& {
|
||||
.cp-autocomplete-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.cp-avatar {
|
||||
.avatar_main(30px);
|
||||
padding: 1px;
|
||||
}
|
||||
& > span:last-child {
|
||||
flex: 1;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
span {
|
||||
margin: 0px !important;
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
.cp-dropdown-container {
|
||||
@dropdown_font: @colortheme_app-font-size @colortheme_font;
|
||||
position: relative;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
@import (reference) "./messenger.less";
|
||||
@import (reference) "./cursor.less";
|
||||
@import (reference) "./usergrid.less";
|
||||
@import (reference) "./mentions.less";
|
||||
@import (reference) "./modals-ui-elements.less";
|
||||
|
||||
.framework_main(@bg-color, @warn-color, @color) {
|
||||
|
@ -44,6 +45,7 @@
|
|||
.messenger_main();
|
||||
.cursor_main();
|
||||
.usergrid_main();
|
||||
.mentions_main();
|
||||
.creation_main(
|
||||
@bg-color: @bg-color,
|
||||
@color: @color
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
@import (reference) "./tools.less";
|
||||
@import (reference) "./avatar.less";
|
||||
|
||||
.mentions_main() {
|
||||
--LessLoader_require: LessLoader_currentFile();
|
||||
}
|
||||
|
||||
& {
|
||||
.cp-mentions {
|
||||
.avatar_main(20px);
|
||||
.tools_unselectable();
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
vertical-align: bottom;
|
||||
background-color: #eee;
|
||||
|
||||
span.cp-mentions-name {
|
||||
max-width: 150px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0px 3px;
|
||||
}
|
||||
&.cp-mentions-clickable {
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3666,5 +3666,268 @@ define([
|
|||
UI.proposal(div, todo);
|
||||
};
|
||||
|
||||
var insertTextAtCursor = function (text) {
|
||||
var selection = window.getSelection();
|
||||
var range = selection.getRangeAt(0);
|
||||
range.deleteContents();
|
||||
var node = document.createTextNode(text);
|
||||
range.insertNode(node);
|
||||
|
||||
for (var position = 0; position !== text.length; position++) {
|
||||
selection.modify("move", "right", "character");
|
||||
}
|
||||
};
|
||||
|
||||
var getSource = {};
|
||||
getSource['contacts'] = function (common, sources) {
|
||||
var priv = common.getMetadataMgr().getPrivateData();
|
||||
Object.keys(priv.friends || {}).forEach(function (key) {
|
||||
if (key === 'me') { return; }
|
||||
var f = priv.friends[key];
|
||||
if (!f.curvePublic || sources[f.curvePublic]) { return; }
|
||||
sources[f.curvePublic] = {
|
||||
avatar: f.avatar,
|
||||
name: f.displayName,
|
||||
curvePublic: f.curvePublic,
|
||||
profile: f.profile,
|
||||
notifications: f.notifications
|
||||
};
|
||||
});
|
||||
};
|
||||
UIElements.addMentions = function (common, options) {
|
||||
if (!options.$input) { return; }
|
||||
var $t = options.$input;
|
||||
|
||||
var getValue = function () { return $t.val(); };
|
||||
var setValue = function (val) { $t.val(val); };
|
||||
|
||||
var div = false;
|
||||
if (options.contenteditable) {
|
||||
div = true;
|
||||
getValue = function () { return $t.html(); };
|
||||
setValue = function () {}; // Not used, we insert data at the node level
|
||||
$t.on('paste', function (e) {
|
||||
try {
|
||||
insertTextAtCursor(e.originalEvent.clipboardData.getData('text'));
|
||||
e.preventDefault();
|
||||
} catch (err) { console.error(err); }
|
||||
});
|
||||
|
||||
// Fix backspace with "contenteditable false" children
|
||||
$t.on('keydown', function (e) {
|
||||
if (e.which !== 8 && e.which !== 46) { return; } // Backspace or del
|
||||
var sel = document.getSelection();
|
||||
if (sel.anchorNode.nodeType !== Node.TEXT_NODE) { return; } // text nodes only
|
||||
|
||||
// Only fix node located after mentions
|
||||
var n = sel.anchorNode;
|
||||
var prev = n && n.previousSibling;
|
||||
// Check if our caret is just after a mention
|
||||
if (!prev || !prev.classList || !prev.classList.contains('cp-mentions')) { return; }
|
||||
|
||||
// Del: if we're at offset 0, make sure we won't delete the text node
|
||||
if (e.which === 46) {
|
||||
if (!sel.anchorOffset && sel.anchorNode.length === 1) {
|
||||
sel.anchorNode.nodeValue = " ";
|
||||
e.preventDefault();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Backspace
|
||||
// If we're not at offset 0, make sure we won't delete the text node
|
||||
if (e.which === 8 && sel.anchorOffset) {
|
||||
if (sel.anchorNode.length === 1) {
|
||||
sel.anchorNode.nodeValue = " ";
|
||||
e.preventDefault();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// If we're at offset 0, We're just after a mention: delete it
|
||||
prev.parentElement.removeChild(prev);
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
// Add the sources
|
||||
// NOTE: Sources must have a "name". They can have an "avatar".
|
||||
var sources = options.sources || {};
|
||||
if (!getSource[options.type]) { return; }
|
||||
getSource[options.type](common, sources);
|
||||
|
||||
|
||||
// Sort autocomplete result by label
|
||||
var sort = function (a, b) {
|
||||
var _a = a.label.toLowerCase();
|
||||
var _b = b.label.toLowerCase();
|
||||
if (_a.label < _b.label) { return -1; }
|
||||
if (_b.label < _a.label) { return 1; }
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Get the text between the last @ before the cursor and the cursor
|
||||
var extractLast = function (term, offset) {
|
||||
offset = typeof(offset) !== "undefined" ? offset : $t[0].selectionStart;
|
||||
var startOffset = term.slice(0,offset).lastIndexOf('@');
|
||||
return term.slice(startOffset+1, offset);
|
||||
};
|
||||
// Insert the autocomplete value in the input field
|
||||
var insertValue = function (value, offset, content) {
|
||||
offset = typeof(offset) !== "undefined" ? offset : $t[0].selectionStart;
|
||||
content = content || getValue();
|
||||
var startOffset = content.slice(0,offset).lastIndexOf('@');
|
||||
var length = offset - startOffset;
|
||||
if (length <= 0) { return; }
|
||||
var result = content.slice(0,startOffset) + value + content.slice(offset);
|
||||
if (content) {
|
||||
return {
|
||||
result: result,
|
||||
startOffset: startOffset
|
||||
};
|
||||
}
|
||||
setValue(result);
|
||||
};
|
||||
// Set the value to receive from the autocomplete
|
||||
var toInsert = function (data, key) {
|
||||
var name = data.name.replace(/[^a-zA-Z0-9]+/g, "-");
|
||||
return "[@"+name+"|"+key+"]";
|
||||
};
|
||||
|
||||
// Fix the functions when suing a contenteditable div
|
||||
if (div) {
|
||||
var _extractLast = extractLast;
|
||||
// Use getSelection to get the cursor position in contenteditable
|
||||
extractLast = function () {
|
||||
var sel = document.getSelection();
|
||||
if (sel.anchorNode.nodeType !== Node.TEXT_NODE) { return; }
|
||||
return _extractLast(sel.anchorNode.nodeValue, sel.anchorOffset);
|
||||
};
|
||||
var _insertValue = insertValue;
|
||||
insertValue = function (value) {
|
||||
// Get the selected node
|
||||
var sel = document.getSelection();
|
||||
if (sel.anchorNode.nodeType !== Node.TEXT_NODE) { return; }
|
||||
var node = sel.anchorNode;
|
||||
|
||||
// Remove the "term"
|
||||
var insert =_insertValue("", sel.anchorOffset, node.nodeValue);
|
||||
if (insert) {
|
||||
node.nodeValue = insert.result;
|
||||
}
|
||||
var breakAt = insert ? insert.startOffset : sel.anchorOffset;
|
||||
|
||||
var el;
|
||||
if (typeof(value) === "string") { el = document.createTextNode(value); }
|
||||
else { el = value; }
|
||||
|
||||
node.parentNode.insertBefore(el, node.splitText(breakAt));
|
||||
var next = el.nextSibling;
|
||||
if (!next) {
|
||||
next = document.createTextNode(" ");
|
||||
el.parentNode.appendChild(next);
|
||||
} else if (next.nodeType === Node.TEXT_NODE && !next.nodeValue) {
|
||||
next.nodeValue = " ";
|
||||
}
|
||||
var range = document.createRange();
|
||||
range.setStart(next, 0);
|
||||
range.setEnd(next, 0);
|
||||
var newSel = window.getSelection();
|
||||
newSel.removeAllRanges();
|
||||
newSel.addRange(range);
|
||||
};
|
||||
|
||||
// Inserting contacts into contenteditable: use mention UI
|
||||
if (options.type === "contacts") {
|
||||
toInsert = function (data) {
|
||||
var avatar = h('span.cp-avatar', {
|
||||
contenteditable: false
|
||||
});
|
||||
common.displayAvatar($(avatar), data.avatar, data.name);
|
||||
return h('span.cp-mentions', {
|
||||
'data-curve': data.curvePublic,
|
||||
'data-notifications': data.notifications,
|
||||
'data-profile': data.profile,
|
||||
'data-name': Util.fixHTML(data.name),
|
||||
'data-avatar': data.avatar || "",
|
||||
}, [
|
||||
avatar,
|
||||
h('span.cp-mentions-name', {
|
||||
contenteditable: false
|
||||
}, data.name)
|
||||
]);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// don't navigate away from the field on tab when selecting an item
|
||||
$t.on("keydown", function(e) {
|
||||
// Tab or enter
|
||||
if ((e.which === 13 || e.which === 9)) {
|
||||
try {
|
||||
var visible = $t.autocomplete("instance").menu.activeMenu.is(':visible');
|
||||
if (visible) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
} catch (err) { console.error(err, $t); }
|
||||
}
|
||||
}).autocomplete({
|
||||
minLength: 0,
|
||||
source: function(data, cb) {
|
||||
var term = data.term;
|
||||
var results = [];
|
||||
if (term.indexOf("@") >= 0) {
|
||||
term = extractLast(data.term) || '';
|
||||
results = Object.keys(sources).filter(function (key) {
|
||||
var data = sources[key];
|
||||
return data.name.toLowerCase().indexOf(term.toLowerCase()) !== -1;
|
||||
}).map(function (key) {
|
||||
var data = sources[key];
|
||||
return {
|
||||
label: data.name,
|
||||
value: key
|
||||
};
|
||||
});
|
||||
results.sort(sort);
|
||||
}
|
||||
cb(results);
|
||||
// Set max-height to the autocomplete dropdown
|
||||
try {
|
||||
var max = window.innerHeight;
|
||||
var pos = $t[0].getBoundingClientRect();
|
||||
var menu = $t.autocomplete("instance").menu.activeMenu;
|
||||
menu.css({
|
||||
'overflow-y': 'auto',
|
||||
'max-height': (max-pos.bottom)+'px'
|
||||
});
|
||||
} catch (e) {}
|
||||
},
|
||||
focus: function() {
|
||||
// prevent value inserted on focus
|
||||
return false;
|
||||
},
|
||||
select: function(event, ui) {
|
||||
// add the selected item
|
||||
var key = ui.item.value;
|
||||
var data = sources[key];
|
||||
var value = toInsert(data, key);
|
||||
insertValue(value);
|
||||
return false;
|
||||
}
|
||||
}).autocomplete( "instance" )._renderItem = function( ul, item ) {
|
||||
var key = item.value;
|
||||
var obj = sources[key];
|
||||
if (!obj) { return; }
|
||||
var avatar = h('span.cp-avatar');
|
||||
common.displayAvatar($(avatar), obj.avatar, obj.name);
|
||||
var li = h('li.cp-autocomplete-value', [
|
||||
avatar,
|
||||
h('span', obj.name)
|
||||
]);
|
||||
return $(li).appendTo(ul);
|
||||
};
|
||||
};
|
||||
|
||||
return UIElements;
|
||||
});
|
||||
|
|
|
@ -315,6 +315,59 @@ define([
|
|||
}
|
||||
};
|
||||
|
||||
Messages.comments_notification = 'Replies to your comment "{0}" in <b>{1}</b>'; // XXX
|
||||
Messages.unknownPad = "Unknown pad"; // XXX
|
||||
handlers['COMMENT_REPLY'] = function (common, data) {
|
||||
var content = data.content;
|
||||
var msg = content.msg;
|
||||
|
||||
// Display the notification
|
||||
//var name = Util.fixHTML(msg.content.user.displayName) || Messages.anonymous;
|
||||
var comment = Util.fixHTML(msg.content.comment).slice(0,20).trim();
|
||||
if (msg.content.comment.length > 20) {
|
||||
comment += '...';
|
||||
}
|
||||
var title = Util.fixHTML(msg.content.title || Messages.unknownPad);
|
||||
var href = msg.content.href;
|
||||
|
||||
content.getFormatText = function () {
|
||||
return Messages._getKey('comments_notification', [comment, title]);
|
||||
};
|
||||
if (href) {
|
||||
content.handler = function () {
|
||||
common.openURL(href);
|
||||
defaultDismiss(common, data)();
|
||||
};
|
||||
}
|
||||
if (!content.archived) {
|
||||
content.dismissHandler = defaultDismiss(common, data);
|
||||
}
|
||||
};
|
||||
|
||||
Messages.mentions_notification = '{0} has mentionned you in <b>{1}</b>'; // XXX
|
||||
handlers['MENTION'] = function (common, data) {
|
||||
var content = data.content;
|
||||
var msg = content.msg;
|
||||
|
||||
// Display the notification
|
||||
var name = Util.fixHTML(msg.content.user.displayName) || Messages.anonymous;
|
||||
var title = Util.fixHTML(msg.content.title || Messages.unknownPad);
|
||||
var href = msg.content.href;
|
||||
|
||||
content.getFormatText = function () {
|
||||
return Messages._getKey('mentions_notification', [name, title]);
|
||||
};
|
||||
if (href) {
|
||||
content.handler = function () {
|
||||
common.openURL(href);
|
||||
defaultDismiss(common, data)();
|
||||
};
|
||||
}
|
||||
if (!content.archived) {
|
||||
content.dismissHandler = defaultDismiss(common, data);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// NOTE: don't forget to fixHTML everything returned by "getFormatText"
|
||||
|
||||
|
|
|
@ -528,7 +528,7 @@ define([
|
|||
/////////////////////// Store ////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
var getAllStores = function () {
|
||||
var getAllStores = Store.getAllStores = function () {
|
||||
var stores = [store];
|
||||
var teamModule = store.modules['team'];
|
||||
if (teamModule) {
|
||||
|
@ -2307,6 +2307,7 @@ define([
|
|||
return;
|
||||
}
|
||||
store.mailbox = Mailbox.init({
|
||||
Store: Store,
|
||||
store: store,
|
||||
updateMetadata: function () {
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
|
@ -2415,7 +2416,6 @@ define([
|
|||
loadUniversal(Profile, 'profile', waitFor);
|
||||
loadUniversal(Team, 'team', waitFor);
|
||||
loadUniversal(History, 'history', waitFor);
|
||||
loadMailbox(waitFor);
|
||||
cleanFriendRequests();
|
||||
}).nThen(function () {
|
||||
var requestLogin = function () {
|
||||
|
@ -2506,6 +2506,8 @@ define([
|
|||
proxy.on('change', [Constants.tokenKey], function () {
|
||||
broadcast([], "UPDATE_TOKEN", { token: proxy[Constants.tokenKey] });
|
||||
});
|
||||
|
||||
loadMailbox();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -527,6 +527,117 @@ define([
|
|||
};
|
||||
|
||||
|
||||
// Hide duplicates when receiving a SHARE_PAD notification:
|
||||
// Keep only one notification per channel: the stronger and more recent one
|
||||
var comments = {};
|
||||
handlers['COMMENT_REPLY'] = function (ctx, box, data, cb) {
|
||||
var msg = data.msg;
|
||||
var hash = data.hash;
|
||||
var content = msg.content;
|
||||
|
||||
if (Util.find(ctx.store.proxy, ['settings', 'pad', 'disableNotif'])) {
|
||||
return void cb(true);
|
||||
}
|
||||
|
||||
var channel = content.channel;
|
||||
if (!channel) { return void cb(true); }
|
||||
|
||||
var title, href;
|
||||
ctx.Store.getAllStores().some(function (s) {
|
||||
var res = s.manager.findChannel(channel);
|
||||
// Check if the pad is in our drive
|
||||
return res.some(function (obj) {
|
||||
if (!obj.data) { return; }
|
||||
if (href && !obj.data.href) { return; } // We already have the VIEW url, we need EDIT
|
||||
href = obj.data.href || obj.data.roHref;
|
||||
title = obj.data.filename || obj.data.title;
|
||||
if (obj.data.href) { return true; } // Abort only if we have the EDIT url
|
||||
});
|
||||
});
|
||||
|
||||
// If we don't have the edit url, ignore this notification
|
||||
if (!href) { return void cb(true); }
|
||||
|
||||
// Add the title
|
||||
content.href = href;
|
||||
content.title = title;
|
||||
|
||||
// Remove duplicates
|
||||
var old = comments[channel];
|
||||
var toRemove = old ? old.data : undefined;
|
||||
|
||||
// Update the data
|
||||
comments[channel] = {
|
||||
data: {
|
||||
type: box.type,
|
||||
hash: hash
|
||||
}
|
||||
};
|
||||
|
||||
cb(false, toRemove);
|
||||
};
|
||||
removeHandlers['COMMENT_REPLY'] = function (ctx, box, data, hash) {
|
||||
var content = data.content;
|
||||
var channel = content.channel;
|
||||
var old = comments[channel];
|
||||
if (old && old.data && old.data.hash === hash) {
|
||||
delete comments[channel];
|
||||
}
|
||||
};
|
||||
|
||||
// Hide duplicates when receiving a SHARE_PAD notification:
|
||||
// Keep only one notification per channel: the stronger and more recent one
|
||||
var mentions = {};
|
||||
handlers['MENTION'] = function (ctx, box, data, cb) {
|
||||
var msg = data.msg;
|
||||
var hash = data.hash;
|
||||
var content = msg.content;
|
||||
|
||||
if (isMuted(ctx, data)) { return void cb(true); }
|
||||
|
||||
var channel = content.channel;
|
||||
if (!channel) { return void cb(true); }
|
||||
|
||||
var title, href;
|
||||
ctx.Store.getAllStores().some(function (s) {
|
||||
var res = s.manager.findChannel(channel);
|
||||
// Check if the pad is in our drive
|
||||
return res.some(function (obj) {
|
||||
if (!obj.data) { return; }
|
||||
if (href && !obj.data.href) { return; } // We already have the VIEW url, we need EDIT
|
||||
href = obj.data.href || obj.data.roHref;
|
||||
title = obj.data.filename || obj.data.title;
|
||||
if (obj.data.href) { return true; } // Abort only if we have the EDIT url
|
||||
});
|
||||
});
|
||||
|
||||
// Add the title
|
||||
content.href = href;
|
||||
content.title = title;
|
||||
|
||||
// Remove duplicates
|
||||
var old = mentions[channel];
|
||||
var toRemove = old ? old.data : undefined;
|
||||
|
||||
// Update the data
|
||||
mentions[channel] = {
|
||||
data: {
|
||||
type: box.type,
|
||||
hash: hash
|
||||
}
|
||||
};
|
||||
|
||||
cb(false, toRemove);
|
||||
};
|
||||
removeHandlers['MENTION'] = function (ctx, box, data, hash) {
|
||||
var content = data.content;
|
||||
var channel = content.channel;
|
||||
var old = mentions[channel];
|
||||
if (old && old.data && old.data.hash === hash) {
|
||||
delete mentions[channel];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
add: function (ctx, box, data, cb) {
|
||||
|
|
|
@ -431,6 +431,7 @@ proxy.mailboxes = {
|
|||
var mailbox = {};
|
||||
var store = cfg.store;
|
||||
var ctx = {
|
||||
Store: cfg.Store,
|
||||
store: store,
|
||||
pinPads: cfg.pinPads,
|
||||
updateMetadata: cfg.updateMetadata,
|
||||
|
|
|
@ -265,6 +265,7 @@ define([
|
|||
if (!bool && update) { onRemote(); }
|
||||
};
|
||||
|
||||
/*
|
||||
var hasChanged = function (content) {
|
||||
try {
|
||||
var oldValue = JSON.parse(cpNfInner.chainpad.getUserDoc());
|
||||
|
@ -276,8 +277,9 @@ define([
|
|||
} catch (e) {}
|
||||
return false;
|
||||
};
|
||||
*/
|
||||
|
||||
onLocal = function (padChange) {
|
||||
onLocal = function (/*padChange*/) {
|
||||
if (state !== STATE.READY) { return; }
|
||||
if (readOnly) { return; }
|
||||
|
||||
|
@ -289,9 +291,11 @@ define([
|
|||
throw new Error("Content must be an object or array, type is " + typeof(content));
|
||||
}
|
||||
|
||||
/*
|
||||
if (padChange && hasChanged(content)) {
|
||||
//cpNfInner.metadataMgr.addAuthor();
|
||||
}
|
||||
*/
|
||||
oldContent = content;
|
||||
|
||||
if (Array.isArray(content)) {
|
||||
|
|
|
@ -103,6 +103,7 @@ define([
|
|||
funcs.getBurnAfterReadingWarning = callWithCommon(UIElements.getBurnAfterReadingWarning);
|
||||
funcs.createNewPadModal = callWithCommon(UIElements.createNewPadModal);
|
||||
funcs.onServerError = callWithCommon(UIElements.onServerError);
|
||||
funcs.addMentions = callWithCommon(UIElements.addMentions);
|
||||
funcs.importMediaTagMenu = callWithCommon(MT.importMediaTagMenu);
|
||||
funcs.getMediaTagPreview = callWithCommon(MT.getMediaTagPreview);
|
||||
|
||||
|
|
|
@ -221,7 +221,7 @@
|
|||
"poll_admin_button": "Admin",
|
||||
"poll_create_user": "Neuen Benutzer hinzufügen",
|
||||
"poll_create_option": "Neue Option hinzufügen",
|
||||
"poll_commit": "Einchecken",
|
||||
"poll_commit": "Bestätigen",
|
||||
"poll_closeWizardButton": "Assistenten schließen",
|
||||
"poll_closeWizardButtonTitle": "Assistenten schließen",
|
||||
"poll_wizardComputeButton": "Optionen übernehmen",
|
||||
|
|
|
@ -1353,5 +1353,17 @@
|
|||
"oo_login": "Veuillez vous connecter ou vous inscrire pour améliorer la performance des feuilles de calcul.",
|
||||
"pad_usePageWidth": "Mode page",
|
||||
"pad_useFullWidth": "Mode large",
|
||||
"cba_title": "Couleurs par auteurs"
|
||||
"cba_title": "Couleurs par auteurs",
|
||||
"settings_padNotifCheckbox": "Désactiver les notifications de commentaires",
|
||||
"settings_padNotifHint": "Ignorer les notifications lorsque quelqu'un répond à l'un de vos commentaires",
|
||||
"settings_padNotifTitle": "Notifications de commentaires",
|
||||
"comments_comment": "Commenter",
|
||||
"comments_resolve": "Fermer",
|
||||
"comments_reply": "Répondre",
|
||||
"comments_submit": "Valider",
|
||||
"comments_edited": "Édité",
|
||||
"comments_deleted": "Commentaire effacé par son auteur",
|
||||
"mentions_notification": "{0} vous a mentionné dans <b>{1}</b>",
|
||||
"unknownPad": "Pad inconnu",
|
||||
"comments_notification": "Réponses à votre commentaire \"{0}\" sur <b>{1}</b>"
|
||||
}
|
||||
|
|
|
@ -169,5 +169,13 @@
|
|||
"user_rename": "表示名を変更",
|
||||
"users": "ユーザー",
|
||||
"saved": "保存しました",
|
||||
"error": "エラー"
|
||||
"error": "エラー",
|
||||
"deleted": "削除しました",
|
||||
"notifications_title": "未読の通知があります",
|
||||
"profile_editDescription": "説明を編集",
|
||||
"profile_addDescription": "説明を追加",
|
||||
"profileButton": "プロフィール",
|
||||
"profile_urlPlaceholder": "URL",
|
||||
"profile_avatar": "アバター",
|
||||
"profile_upload": " 新しいアバターをアップロード"
|
||||
}
|
||||
|
|
|
@ -1353,5 +1353,17 @@
|
|||
"oo_login": "Please log in or register to improve the performance of spreadsheets.",
|
||||
"pad_useFullWidth": "Full-width mode",
|
||||
"pad_usePageWidth": "Page mode",
|
||||
"cba_title": "Author colors"
|
||||
"cba_title": "Author colors",
|
||||
"comments_notification": "Replies to your comment \"{0}\" in <b>{1}</b>",
|
||||
"unknownPad": "Unknown pad",
|
||||
"mentions_notification": "{0} has mentioned you in <b>{1}</b>",
|
||||
"comments_deleted": "Comment deleted by its author",
|
||||
"comments_edited": "Edited",
|
||||
"comments_submit": "Submit",
|
||||
"comments_reply": "Reply",
|
||||
"comments_resolve": "Resolve",
|
||||
"comments_comment": "Comment",
|
||||
"settings_padNotifTitle": "Comment notifications",
|
||||
"settings_padNotifHint": "Ignore notifications when someone replies to one of your comments",
|
||||
"settings_padNotifCheckbox": "Disable comment notifications"
|
||||
}
|
||||
|
|
|
@ -84,9 +84,9 @@ body.cp-app-pad {
|
|||
}
|
||||
#cp-app-pad-comments {
|
||||
order: 3;
|
||||
width: 300px;
|
||||
width: 330px;
|
||||
//background-color: white;
|
||||
margin: 30px;
|
||||
margin: 0px 20px;
|
||||
.comments_main();
|
||||
}
|
||||
&.cke_body_width {
|
||||
|
|
|
@ -1,28 +1,31 @@
|
|||
(function () {
|
||||
(function() {
|
||||
var CKEDITOR = window.CKEDITOR;
|
||||
|
||||
function isUnstylable (el) {
|
||||
function isUnstylable(el) {
|
||||
if (el.hasClass('cke_widget_mathjax')) {
|
||||
return false;
|
||||
}
|
||||
if (el.hasClass('cke_widget_mediatag')) {
|
||||
return false;
|
||||
}
|
||||
var b = el.getAttribute( 'contentEditable' ) === 'false' ||
|
||||
el.getAttribute( 'data-nostyle' );
|
||||
var b = el.getAttribute('contentEditable') === 'false' ||
|
||||
el.getAttribute('data-nostyle');
|
||||
return b;
|
||||
}
|
||||
|
||||
var color1 = 'rgba(252, 165, 3, 0.8)';
|
||||
var color2 = 'rgba(252, 231, 3, 0.8)';
|
||||
var color1 = 'rgba(249, 230, 65, 1.0)';
|
||||
var color2 = 'rgba(252, 181, 0, 1.0)';
|
||||
|
||||
CKEDITOR.plugins.add('comments', {
|
||||
onLoad: function () {
|
||||
CKEDITOR.addCss('comment { background-color: '+color1+'; }' +
|
||||
'@keyframes color { 0% { background-color: '+color2+'; } 50% { background-color: '+color1+'; } 100% { background-color: '+color2+'; } }' +
|
||||
'comment.active { animation-name: color; animation-duration: 1s; animation-iteration-count: 2; background-color: '+color2+'; outline: none;}' +
|
||||
'comment media-tag { border: 2px solid '+color1+' !important; }' +
|
||||
'comment.active media-tag { border: 2px solid '+color2+' !important; }' +
|
||||
onLoad: function() {
|
||||
CKEDITOR.addCss('comment { background-color: ' + color1 + '; }' +
|
||||
'@keyframes color { 0% { background-color: ' + color2 + '; } 50% { background-color: ' + color1 + '; } 100% { background-color: ' + color2 + '; } }' +
|
||||
'comment.active { animation-name: color; animation-duration: 1s; animation-iteration-count: 2; background-color: ' + color2 + '; outline: none;}' +
|
||||
'comment media-tag { border: 2px solid ' + color1 + ' !important; }' +
|
||||
'comment.active media-tag { border: 2px solid ' + color2 + ' !important; }' +
|
||||
'comment * { background-color: transparent !important; }');
|
||||
},
|
||||
init: function (editor) {
|
||||
init: function(editor) {
|
||||
var Messages = CKEDITOR._commentsTranslations;
|
||||
|
||||
var styleDef = {
|
||||
|
@ -30,14 +33,14 @@
|
|||
attributes: {
|
||||
'data-uid': '#(uid)',
|
||||
},
|
||||
overrides: [ {
|
||||
overrides: [{
|
||||
element: 'comment'
|
||||
} ],
|
||||
}],
|
||||
childRule: isUnstylable
|
||||
};
|
||||
var removeStyle = new CKEDITOR.style(styleDef, { 'uid': '' });
|
||||
|
||||
var isApplicable = editor.plugins.comments.isApplicable = function (path, sel) {
|
||||
var isApplicable = editor.plugins.comments.isApplicable = function(path, sel) {
|
||||
path = path || editor.elementPath();
|
||||
sel = sel || editor.getSelection();
|
||||
var applicable = removeStyle.checkApplicable(path, editor);
|
||||
|
@ -49,12 +52,12 @@
|
|||
|
||||
// Register the command.
|
||||
editor.addCommand('comment', {
|
||||
exec: function (editor) {
|
||||
exec: function(editor) {
|
||||
if (editor.readOnly) { return; }
|
||||
editor.focus();
|
||||
|
||||
var uid = CKEDITOR.tools.getUniqueId();
|
||||
editor.plugins.comments.addComment(uid, function () {
|
||||
editor.plugins.comments.addComment(uid, function() {
|
||||
// Make an undo spnashot
|
||||
editor.fire('saveSnapshot');
|
||||
// Make sure comments won't overlap
|
||||
|
@ -65,16 +68,16 @@
|
|||
editor.applyStyle(s);
|
||||
|
||||
// Save the undo snapshot after all changes are affected.
|
||||
setTimeout( function() {
|
||||
setTimeout(function() {
|
||||
editor.fire('saveSnapshot');
|
||||
}, 0 );
|
||||
}, 0);
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Uncomment provided element
|
||||
editor.plugins.comments.uncomment = function (id, els) {
|
||||
editor.plugins.comments.uncomment = function(id, els) {
|
||||
if (editor.readOnly) { return; }
|
||||
editor.fire('saveSnapshot');
|
||||
|
||||
|
@ -86,7 +89,7 @@
|
|||
},
|
||||
});
|
||||
style.alwaysRemoveElement = true;
|
||||
els.forEach(function (el) {
|
||||
els.forEach(function(el) {
|
||||
// Create range for this element
|
||||
el.removeAttribute('class');
|
||||
var node = new CKEDITOR.dom.node(el);
|
||||
|
@ -101,22 +104,22 @@
|
|||
}
|
||||
});
|
||||
|
||||
setTimeout( function() {
|
||||
setTimeout(function() {
|
||||
editor.fire('saveSnapshot');
|
||||
}, 0 );
|
||||
}, 0);
|
||||
};
|
||||
|
||||
// Uncomment from context menu, disabled for now...
|
||||
editor.addCommand('uncomment', {
|
||||
exec: function (editor, data) {
|
||||
exec: function(editor, data) {
|
||||
if (editor.readOnly) { return; }
|
||||
editor.fire('saveSnapshot');
|
||||
if (!data || !data.id) {
|
||||
if (!data || !data.id) {
|
||||
editor.focus();
|
||||
editor.removeStyle(removeStyle);
|
||||
setTimeout( function() {
|
||||
setTimeout(function() {
|
||||
editor.fire('saveSnapshot');
|
||||
}, 0 );
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +130,7 @@
|
|||
editor.ui.addButton('Comment', {
|
||||
label: Messages.comment,
|
||||
command: 'comment',
|
||||
icon : '/pad/icons/comment.png',
|
||||
icon: '/pad/icons/comment.png',
|
||||
toolbar: 'insert,10'
|
||||
});
|
||||
}
|
||||
|
@ -136,7 +139,7 @@
|
|||
editor.addMenuGroup('comments');
|
||||
editor.addMenuItem('comment', {
|
||||
label: Messages.comment,
|
||||
icon : '/pad/icons/comment.png',
|
||||
icon: '/pad/icons/comment.png',
|
||||
command: 'comment',
|
||||
group: 'comments'
|
||||
});
|
||||
|
@ -159,7 +162,7 @@
|
|||
};
|
||||
});
|
||||
*/
|
||||
editor.contextMenu.addListener(function (element, sel, path) {
|
||||
editor.contextMenu.addListener(function(element, sel, path) {
|
||||
var applicable = isApplicable(path, sel);
|
||||
if (!applicable) { return; }
|
||||
return {
|
||||
|
|
|
@ -25,9 +25,11 @@ define([
|
|||
u: id,
|
||||
m: "str", // comment
|
||||
t: +new Date,
|
||||
v: "str" // value of the commented content
|
||||
v: "str", // value of the commented content
|
||||
e: undefined/1, // edited
|
||||
d: undefined/1, // deleted
|
||||
}],
|
||||
(deleted: undefined/true,)
|
||||
d: undefined/1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,14 +68,24 @@ define([
|
|||
return uid || authorUid(existing);
|
||||
};
|
||||
|
||||
var updateAuthorData = function (Env) {
|
||||
// Return the author ID and add/update the data for registered users
|
||||
// Return the username for unregistered users
|
||||
var updateAuthorData = function (Env, onChange) {
|
||||
var userData = Env.metadataMgr.getUserData();
|
||||
if (!Env.common.isLoggedIn()) {
|
||||
return userData.name;
|
||||
}
|
||||
var myAuthorId = getAuthorId(Env, userData.curvePublic);
|
||||
var data = Env.comments.authors[myAuthorId] = Env.comments.authors[myAuthorId] || {};
|
||||
var old = Sortify(data);
|
||||
data.name = userData.name;
|
||||
data.avatar = userData.avatar;
|
||||
data.profile = userData.profile;
|
||||
data.curvePublic = userData.curvePublic;
|
||||
data.notifications = userData.notifications;
|
||||
if (typeof(onChange) === "function" && Sortify(data) !== old) {
|
||||
onChange();
|
||||
}
|
||||
return myAuthorId;
|
||||
};
|
||||
|
||||
|
@ -83,17 +95,82 @@ define([
|
|||
Env.metadataMgr.updateMetadata(md);
|
||||
};
|
||||
|
||||
var sendReplyNotification = function (Env, uid) {
|
||||
if (!Env.comments || !Env.comments.data || !Env.comments.authors) { return; }
|
||||
if (!Env.common.isLoggedIn()) { return; }
|
||||
var thread = Env.comments.data[uid];
|
||||
if (!thread || !Array.isArray(thread.m)) { return; }
|
||||
var userData = Env.metadataMgr.getUserData();
|
||||
var privateData = Env.metadataMgr.getPrivateData();
|
||||
var others = {};
|
||||
// Get all the other registered users with a mailbox
|
||||
thread.m.forEach(function (obj) {
|
||||
var u = obj.u;
|
||||
if (typeof(u) !== "number") { return; }
|
||||
var author = Env.comments.authors[u];
|
||||
if (!author || others[u] || !author.notifications || !author.curvePublic) { return; }
|
||||
if (author.curvePublic === userData.curvePublic) { return; } // don't send to yourself
|
||||
others[u] = {
|
||||
curvePublic: author.curvePublic,
|
||||
comment: obj.m,
|
||||
content: obj.v,
|
||||
notifications: author.notifications
|
||||
};
|
||||
});
|
||||
// Send the notification
|
||||
Object.keys(others).forEach(function (id) {
|
||||
var data = others[id];
|
||||
Env.common.mailbox.sendTo("COMMENT_REPLY", {
|
||||
channel: privateData.channel,
|
||||
comment: data.comment,
|
||||
content: data.content
|
||||
}, {
|
||||
channel: data.notifications,
|
||||
curvePublic: data.curvePublic
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
var cleanMentions = function ($el) {
|
||||
$el.html('');
|
||||
var el = $el[0];
|
||||
var allowed = ['data-profile', 'data-name', 'data-avatar', 'class'];
|
||||
// Remove unnecessary/unsafe attributes
|
||||
for (var i=el.attributes.length-1; i>0; i--) {
|
||||
var name = el.attributes[i] && el.attributes[i].name;
|
||||
if (allowed.indexOf(name) === -1) {
|
||||
$el.removeAttr(name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Seletc all text of a contenteditable element
|
||||
var selectAll = function (element) {
|
||||
var selection = window.getSelection();
|
||||
var range = document.createRange();
|
||||
range.selectNodeContents(element);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
};
|
||||
|
||||
Messages.comments_deleted = "Comment deleted by its author"; // XXX
|
||||
Messages.comments_edited = "Edited"; // XXX
|
||||
Messages.comments_submit = "Submit"; // XXX
|
||||
Messages.comments_reply = "Reply"; // XXX
|
||||
Messages.comments_resolve = "Resolve"; // XXX
|
||||
|
||||
var getCommentForm = function (Env, reply, _cb) {
|
||||
var getCommentForm = function (Env, reply, _cb, editContent) {
|
||||
var cb = Util.once(_cb);
|
||||
var userData = Env.metadataMgr.getUserData();
|
||||
var name = Util.fixHTML(userData.name || Messages.anonymous);
|
||||
var avatar = h('span.cp-avatar');
|
||||
var textarea = h('textarea', {
|
||||
tabindex: 1
|
||||
var textarea = h('div.cp-textarea', {
|
||||
tabindex: 1,
|
||||
role: 'textbox',
|
||||
'aria-multiline': true,
|
||||
'aria-labelledby': 'cp-comments-label',
|
||||
'aria-required': true,
|
||||
contenteditable: true,
|
||||
});
|
||||
Env.common.displayAvatar($(avatar), userData.avatar, name);
|
||||
|
||||
|
@ -110,31 +187,106 @@ define([
|
|||
Messages.comments_submit
|
||||
]);
|
||||
|
||||
// List of allowed attributes in mentions
|
||||
$(submit).click(function (e) {
|
||||
e.stopPropagation();
|
||||
cb(textarea.value);
|
||||
var clone = textarea.cloneNode(true);
|
||||
var notify = {};
|
||||
var $clone = $(clone);
|
||||
$clone.find('span.cp-mentions').each(function (i, el) {
|
||||
var $el = $(el);
|
||||
var curve = $el.attr('data-curve');
|
||||
var notif = $el.attr('data-notifications');
|
||||
cleanMentions($el, true);
|
||||
if (!curve || !notif) { return; }
|
||||
notify[curve] = notif;
|
||||
});
|
||||
$clone.find('br').replaceWith("\n");
|
||||
$clone.find('> *:not(.cp-mentions)').remove();
|
||||
var content = clone.innerHTML.trim();
|
||||
if (!content) { return; }
|
||||
|
||||
// Send notification
|
||||
var privateData = Env.metadataMgr.getPrivateData();
|
||||
var userData = Env.metadataMgr.getUserData();
|
||||
Object.keys(notify).forEach(function (curve) {
|
||||
if (curve === userData.curvePublic) { return; }
|
||||
Env.common.mailbox.sendTo("MENTION", {
|
||||
channel: privateData.channel,
|
||||
}, {
|
||||
channel: notify[curve],
|
||||
curvePublic: curve
|
||||
});
|
||||
});
|
||||
|
||||
// Push the content
|
||||
cb(content);
|
||||
});
|
||||
$(cancel).click(function (e) {
|
||||
e.stopPropagation();
|
||||
cb();
|
||||
});
|
||||
|
||||
$(textarea).keydown(function (e) {
|
||||
var $text = $(textarea).keydown(function (e) {
|
||||
e.stopPropagation();
|
||||
if (e.which === 27) {
|
||||
$(cancel).click();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
if (e.which === 13 && !e.shiftKey) {
|
||||
// Submit form on Enter is the autocompelte menu is not visible
|
||||
try {
|
||||
var visible = $text.autocomplete("instance").menu.activeMenu.is(':visible');
|
||||
if (visible) { return; }
|
||||
} catch (err) {}
|
||||
$(submit).click();
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
}).click(function (e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
|
||||
if (Env.common.isLoggedIn()) {
|
||||
var authors = {};
|
||||
Object.keys((Env.comments && Env.comments.authors) || {}).forEach(function (id) {
|
||||
var obj = Util.clone(Env.comments.authors[id]);
|
||||
authors[obj.curvePublic] = obj;
|
||||
});
|
||||
Env.common.addMentions({
|
||||
$input: $text,
|
||||
contenteditable: true,
|
||||
type: 'contacts',
|
||||
sources: authors
|
||||
});
|
||||
}
|
||||
|
||||
var deleteButton;
|
||||
// Edit? start with the old content
|
||||
// Add a space to make sure we won't end with a mention and a bad cursor
|
||||
if (editContent) {
|
||||
textarea.innerHTML = editContent + " ";
|
||||
deleteButton = h('button.btn.btn-danger', {
|
||||
tabindex: 1
|
||||
}, [
|
||||
h('i.fa.fa-times'),
|
||||
Messages.kanban_delete
|
||||
]);
|
||||
$(deleteButton).click(function (e) {
|
||||
e.stopPropagation();
|
||||
cb(false);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
setTimeout(function () {
|
||||
$(textarea).focus();
|
||||
selectAll(textarea);
|
||||
});
|
||||
|
||||
return h('div.cp-comment-form' + (reply ? '.cp-comment-reply' : ''), {
|
||||
'data-uid': reply || undefined
|
||||
'data-uid': reply || ''
|
||||
}, [
|
||||
h('div.cp-comment-form-input', [
|
||||
avatar,
|
||||
|
@ -142,49 +294,90 @@ define([
|
|||
]),
|
||||
h('div.cp-comment-form-actions', [
|
||||
cancel,
|
||||
deleteButton,
|
||||
submit
|
||||
])
|
||||
]);
|
||||
};
|
||||
|
||||
var isVisible = function (el, $container) {
|
||||
var size = $container.outerHeight();
|
||||
var pos = el.getBoundingClientRect();
|
||||
return (pos.bottom < size) && (pos.y > 0);
|
||||
};
|
||||
|
||||
var redrawComments = function (Env) {
|
||||
// Don't redraw if there were no change
|
||||
var str = Sortify((Env.comments || {}).data || {});
|
||||
|
||||
var str = Sortify(Env.comments || {});
|
||||
if (str === Env.oldComments) { return; }
|
||||
Env.oldComments = str;
|
||||
|
||||
// Store the cursor position if it's located in this form
|
||||
var oldSelection = window.getSelection();
|
||||
var oldRangeObj;
|
||||
if ($(oldSelection.anchorNode).closest('.cp-comment-form').length) {
|
||||
var oldRange = oldSelection.getRangeAt && oldSelection.getRangeAt(0);
|
||||
oldRangeObj = {
|
||||
start: oldRange.startContainer,
|
||||
startO: oldRange.startOffset,
|
||||
end: oldRange.endContainer,
|
||||
endO: oldRange.endOffset
|
||||
};
|
||||
}
|
||||
// Store existing input form in memory
|
||||
var $oldInput = Env.$container.find('.cp-comment-form').detach();
|
||||
if ($oldInput.length !== 1) { $oldInput = undefined; }
|
||||
|
||||
// Remove everything
|
||||
Env.$container.html('');
|
||||
|
||||
// "show" tells us if we need to display the "comments" column or not
|
||||
var show = false;
|
||||
|
||||
// Add invisible label for accessibility tools
|
||||
var label = h('label#cp-comments-label', Messages.comments_comment);
|
||||
Env.$container.append(label);
|
||||
|
||||
// If we were adding a new comment, redraw our form
|
||||
if ($oldInput && !$oldInput.attr('data-uid')) {
|
||||
show = true;
|
||||
Env.$container.append($oldInput);
|
||||
}
|
||||
|
||||
var order = Env.$inner.find('comment').map(function (i, el) {
|
||||
var userData = Env.metadataMgr.getUserData();
|
||||
|
||||
// Get all the comment threads in their order in the pad
|
||||
var threads = Env.$inner.find('comment').map(function (i, el) {
|
||||
return el.getAttribute('data-uid');
|
||||
}).toArray();
|
||||
var done = [];
|
||||
|
||||
order.forEach(function (key) {
|
||||
// Avoir duplicates
|
||||
if (done.indexOf(key) !== -1) { return; }
|
||||
done.push(key);
|
||||
|
||||
// Draw all comment threads
|
||||
Util.deduplicateString(threads).forEach(function (key) {
|
||||
// Get thread data
|
||||
var obj = Env.comments.data[key];
|
||||
if (!obj || obj.deleted || !Array.isArray(obj.m) || !obj.m.length) {
|
||||
if (!obj || obj.d || !Array.isArray(obj.m) || !obj.m.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If at least one thread is visible, display the "comments" column
|
||||
show = true;
|
||||
|
||||
var content = [];
|
||||
obj.m.forEach(function (msg, i) {
|
||||
var author = (Env.comments.authors || {})[msg.u] || {};
|
||||
var $div;
|
||||
var $actions;
|
||||
|
||||
// Draw all messages for this thread
|
||||
(obj.m || []).forEach(function (msg, i) {
|
||||
var replyCls = i === 0 ? '' : '.cp-comment-reply';
|
||||
if (msg.d) {
|
||||
|
||||
content.push(h('div.cp-comment.cp-comment-deleted'+replyCls,
|
||||
Messages.comments_deleted));
|
||||
return;
|
||||
}
|
||||
var author = typeof(msg.u) === "number" ?
|
||||
((Env.comments.authors || {})[msg.u] || {}) :
|
||||
{ name: msg.u };
|
||||
var name = Util.fixHTML(author.name || Messages.anonymous);
|
||||
var date = new Date(msg.t);
|
||||
var avatar = h('span.cp-avatar');
|
||||
|
@ -196,17 +389,109 @@ define([
|
|||
});
|
||||
}
|
||||
|
||||
content.push(h('div.cp-comment'+(i === 0 ? '' : '.cp-comment-reply'), [
|
||||
// Build sanitized html with mentions
|
||||
var m = h('div.cp-comment-content');
|
||||
m.innerHTML = msg.m;
|
||||
var $m = $(m);
|
||||
$m.find('> *:not(span.cp-mentions)').remove();
|
||||
$m.find('span.cp-mentions').each(function (i, el) {
|
||||
var $el = $(el);
|
||||
var name = $el.attr('data-name');
|
||||
var avatarUrl = $el.attr('data-avatar');
|
||||
var profile = $el.attr('data-profile');
|
||||
if (!name && !avatarUrl && !profile) {
|
||||
$el.remove();
|
||||
return;
|
||||
}
|
||||
cleanMentions($el);
|
||||
var avatar = h('span.cp-avatar');
|
||||
Env.common.displayAvatar($(avatar), avatarUrl, name);
|
||||
$el.append([
|
||||
avatar,
|
||||
h('span.cp-mentions-name', name)
|
||||
]);
|
||||
if (profile) {
|
||||
$el.attr('tabindex', 1);
|
||||
$el.addClass('cp-mentions-clickable').click(function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
Env.common.openURL(Hash.hashToHref(profile, 'profile'));
|
||||
}).focus(function (e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// edited state
|
||||
var edited;
|
||||
if (msg.e) {
|
||||
edited = h('div.cp-comment-edited', Messages.comments_edited);
|
||||
}
|
||||
|
||||
var container;
|
||||
|
||||
// Add edit button when applicable (last message of the thread, written by ourselves)
|
||||
var edit;
|
||||
if (i === (obj.m.length -1) && author.curvePublic === userData.curvePublic) {
|
||||
edit = h('span.cp-comment-edit', {
|
||||
tabindex:1,
|
||||
title: Messages.clickToEdit
|
||||
}, h('i.fa.fa-pencil'));
|
||||
$(edit).click(function (e) {
|
||||
Env.$container.find('.cp-comment-active').removeClass('cp-comment-active');
|
||||
$div.addClass('cp-comment-active');
|
||||
e.stopPropagation();
|
||||
Env.$container.find('.cp-comment-form').remove();
|
||||
if ($actions) { $actions.hide(); }
|
||||
var form = getCommentForm(Env, key, function (val) {
|
||||
// Show the "reply" and "resolve" buttons again
|
||||
$(form).closest('.cp-comment-container')
|
||||
.find('.cp-comment-actions').css('display', '');
|
||||
$(form).remove();
|
||||
|
||||
if (typeof(val) === "undefined") { return; }
|
||||
|
||||
var obj = Env.comments.data[key];
|
||||
if (!obj || !Array.isArray(obj.m)) { return; }
|
||||
var msg = obj.m[i];
|
||||
if (!msg) { return; }
|
||||
// i is our index
|
||||
if (val === false) {
|
||||
msg.d = 1;
|
||||
if (container) {
|
||||
$(container).addClass('cp-comment-deleted')
|
||||
.html(Messages.comments_deleted);
|
||||
}
|
||||
if (obj.m.length === 1) {
|
||||
delete Env.comments.data[key];
|
||||
}
|
||||
} else {
|
||||
msg.e = 1;
|
||||
msg.m = val;
|
||||
}
|
||||
|
||||
// Send to chainpad
|
||||
updateMetadata(Env);
|
||||
Env.framework.localChange();
|
||||
}, m.innerHTML);
|
||||
|
||||
if (!$div) { return; }
|
||||
$div.append(form);
|
||||
});
|
||||
}
|
||||
|
||||
// Add the comment
|
||||
content.push(container = h('div.cp-comment'+replyCls, [
|
||||
h('div.cp-comment-header', [
|
||||
avatar,
|
||||
h('span.cp-comment-metadata', [
|
||||
h('span.cp-comment-author', name),
|
||||
h('span.cp-comment-time', date.toLocaleString())
|
||||
])
|
||||
]),
|
||||
edit
|
||||
]),
|
||||
h('div.cp-comment-content', [
|
||||
msg.m
|
||||
])
|
||||
m,
|
||||
edited
|
||||
]));
|
||||
|
||||
});
|
||||
|
@ -229,22 +514,23 @@ define([
|
|||
reply,
|
||||
resolve
|
||||
]));
|
||||
var $actions = $(actions);
|
||||
$actions = $(actions);
|
||||
|
||||
var div;
|
||||
Env.$container.append(div = h('div.cp-comment-container', {
|
||||
'data-uid': key,
|
||||
tabindex: 1
|
||||
}, content));
|
||||
var $div = $(div);
|
||||
$div = $(div);
|
||||
|
||||
$(reply).click(function (e) {
|
||||
e.stopPropagation();
|
||||
$actions.hide();
|
||||
var form = getCommentForm(Env, key, function (val) {
|
||||
$(form).remove();
|
||||
// Show the "reply" and "resolve" buttons again
|
||||
$(form).closest('.cp-comment-container')
|
||||
.find('.cp-comment-actions').css('display', '');
|
||||
$(form).remove();
|
||||
|
||||
if (!val) { return; }
|
||||
var obj = Env.comments.data[key];
|
||||
|
@ -257,23 +543,36 @@ define([
|
|||
}).join('\n');
|
||||
|
||||
// Push the reply
|
||||
var myId = updateAuthorData(Env);
|
||||
var user = updateAuthorData(Env);
|
||||
obj.m.push({
|
||||
u: myId,
|
||||
u: user, // id (number) or name (string)
|
||||
t: +new Date(),
|
||||
m: val,
|
||||
v: value
|
||||
});
|
||||
|
||||
// Notify other users
|
||||
sendReplyNotification(Env, key);
|
||||
|
||||
// Send to chainpad
|
||||
updateMetadata(Env);
|
||||
Env.framework.localChange();
|
||||
});
|
||||
|
||||
$div.append(form);
|
||||
|
||||
// Make sure the submit button is visible: scroll by the height of the form
|
||||
setTimeout(function () {
|
||||
var yContainer = Env.$container[0].getBoundingClientRect().bottom;
|
||||
var yActions = form.getBoundingClientRect().bottom;
|
||||
if (yActions > yContainer) {
|
||||
Env.$container.scrollTop(Env.$container.scrollTop() + 55);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
UI.confirmButton(resolve, {
|
||||
classes: 'btn-danger-alt'
|
||||
classes: 'btn-danger'
|
||||
}, function () {
|
||||
// Delete the comment
|
||||
delete Env.comments.data[key];
|
||||
|
@ -291,21 +590,24 @@ define([
|
|||
|
||||
// Scroll into view
|
||||
if (!$last.length) { return; }
|
||||
var size = Env.$inner.outerHeight();
|
||||
var pos = $last[0].getBoundingClientRect();
|
||||
var visible = (pos.y + pos.height) < size;
|
||||
var visible = isVisible($last[0], Env.$inner);
|
||||
if (!visible) { $last[0].scrollIntoView(); }
|
||||
};
|
||||
|
||||
$div.on('click focus', function () {
|
||||
$div.on('click focus', function (e) {
|
||||
// Prevent the click event to propagate if we're already selected
|
||||
// The propagation to #cp-app-pad-inner would trigger the "unselect" handler
|
||||
e.stopPropagation();
|
||||
if ($div.hasClass('cp-comment-active')) { return; }
|
||||
Env.$container.find('.cp-comment-active').removeClass('cp-comment-active');
|
||||
$div.addClass('cp-comment-active');
|
||||
div.scrollIntoView();
|
||||
$actions.css('display', '');
|
||||
Env.$container.find('.cp-comment-form').remove();
|
||||
|
||||
focusContent();
|
||||
|
||||
var visible = isVisible(div, Env.$container);
|
||||
if (!visible) { div.scrollIntoView(); }
|
||||
});
|
||||
|
||||
if ($oldInput && $oldInput.attr('data-uid') === key) {
|
||||
|
@ -317,6 +619,19 @@ define([
|
|||
}
|
||||
});
|
||||
|
||||
// Restore selection
|
||||
if (oldRangeObj) {
|
||||
setTimeout(function () {
|
||||
if (!oldRangeObj) { return; }
|
||||
var range = document.createRange();
|
||||
range.setStart(oldRangeObj.start, oldRangeObj.startO);
|
||||
range.setEnd(oldRangeObj.end, oldRangeObj.endO);
|
||||
var sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
});
|
||||
}
|
||||
|
||||
if (show) {
|
||||
Env.$container.show();
|
||||
} else {
|
||||
|
@ -334,10 +649,22 @@ define([
|
|||
}
|
||||
if (Env.ready === 0) {
|
||||
Env.ready = true;
|
||||
updateAuthorData(Env, function () {
|
||||
changed = true;
|
||||
});
|
||||
// On ready, if our user data have changed or if we've added the initial structure
|
||||
// of the comments, push the changes
|
||||
if (changed) {
|
||||
updateMetadata(Env);
|
||||
Env.framework.localChange();
|
||||
}
|
||||
} else if (Env.ready) {
|
||||
// Everytime there is a metadata change, check if our user data have changed
|
||||
// and push the updates if necessary
|
||||
updateAuthorData(Env, function () {
|
||||
updateMetadata(Env);
|
||||
Env.framework.localChange();
|
||||
});
|
||||
}
|
||||
redrawComments(Env);
|
||||
};
|
||||
|
@ -353,7 +680,7 @@ define([
|
|||
|
||||
// If there is no comment stored in the metadata, abort
|
||||
var comments = Object.keys(Env.comments.data || {}).filter(function (id) {
|
||||
return !Env.comments.data[id].deleted;
|
||||
return !Env.comments.data[id].d;
|
||||
});
|
||||
|
||||
var changed = false;
|
||||
|
@ -378,8 +705,8 @@ define([
|
|||
}
|
||||
// If this comment was deleted, we're probably using "undo" to restore it:
|
||||
// remove the "deleted" state and continue
|
||||
if (obj.deleted) {
|
||||
delete obj.deleted;
|
||||
if (obj.d) {
|
||||
delete obj.d;
|
||||
changed = true;
|
||||
}
|
||||
return id;
|
||||
|
@ -397,7 +724,7 @@ define([
|
|||
// comment has been deleted
|
||||
var data = Env.comments.data[uid];
|
||||
if (!data) { return; }
|
||||
data.deleted = true;
|
||||
data.d = 1;
|
||||
//delete Env.comments.data[uid];
|
||||
changed = true;
|
||||
});
|
||||
|
@ -440,7 +767,8 @@ define([
|
|||
node: node,
|
||||
button: button
|
||||
};
|
||||
$(button).click(function () {
|
||||
$(button).click(function (e) {
|
||||
e.stopPropagation();
|
||||
Env.editor.execCommand('comment');
|
||||
Env.bubble = undefined;
|
||||
});
|
||||
|
@ -462,6 +790,8 @@ define([
|
|||
return;
|
||||
}
|
||||
|
||||
// Remove active class on other comments
|
||||
Env.$container.find('.cp-comment-active').removeClass('cp-comment-active');
|
||||
Env.$container.find('.cp-comment-form').remove();
|
||||
var form = getCommentForm(Env, false, function (val) {
|
||||
$(form).remove();
|
||||
|
@ -477,10 +807,10 @@ define([
|
|||
// Don't override existing data
|
||||
if (Env.comments.data[uid]) { return; }
|
||||
|
||||
var myId = updateAuthorData(Env);
|
||||
var user = updateAuthorData(Env);
|
||||
Env.comments.data[uid] = {
|
||||
m: [{
|
||||
u: myId,
|
||||
u: user, // Id or name
|
||||
t: +new Date(),
|
||||
m: val,
|
||||
v: canonicalize(Env.editor.getSelection().getSelectedText())
|
||||
|
@ -533,7 +863,7 @@ define([
|
|||
// Clear data
|
||||
var data = (Env.comments && Env.comments.data) || {};
|
||||
Object.keys(data).forEach(function (uid) {
|
||||
if (data[uid].deleted) { delete data[uid]; }
|
||||
if (data[uid].d) { delete data[uid]; }
|
||||
});
|
||||
|
||||
// Commit
|
||||
|
@ -545,21 +875,39 @@ define([
|
|||
var Env = cfg;
|
||||
Env.comments = Util.clone(COMMENTS);
|
||||
|
||||
var ro = cfg.framework.isReadOnly();
|
||||
var onEditableChange = function (unlocked) {
|
||||
Env.$container.removeClass('cp-comments-readonly');
|
||||
if (ro || !unlocked) {
|
||||
Env.$container.addClass('cp-comments-readonly');
|
||||
}
|
||||
};
|
||||
cfg.framework.onEditableChange(onEditableChange);
|
||||
onEditableChange();
|
||||
|
||||
addAddCommentHandler(Env);
|
||||
|
||||
// Unselect comment when clicking outside
|
||||
$(window).click(function (e) {
|
||||
if ($(e.target).closest('.cp-comment-container').length) {
|
||||
return;
|
||||
}
|
||||
var $target = $(e.target);
|
||||
if (!$target.length) { return; }
|
||||
if ($target.is('.cp-comment-container')) { return; }
|
||||
if ($target.closest('.cp-comment-container').length) { return; }
|
||||
if ($target.closest('.ui-autocomplete').length) { return; }
|
||||
// Add comment button? don't remove anything because this handler is called after
|
||||
// the button action
|
||||
if ($target.is('.cke_button__comment')) { return; }
|
||||
if ($target.closest('.cke_button__comment').length) { return; }
|
||||
Env.$container.find('.cp-comment-active').removeClass('cp-comment-active');
|
||||
Env.$inner.find('comment.active').removeClass('active');
|
||||
Env.$container.find('.cp-comment-form').remove();
|
||||
});
|
||||
// Unselect comment when clicking on another part of the doc
|
||||
Env.$inner.on('click', function (e) {
|
||||
if ($(e.target).closest('comment').length) { return; }
|
||||
Env.$container.find('.cp-comment-active').removeClass('cp-comment-active');
|
||||
Env.$inner.find('comment.active').removeClass('active');
|
||||
Env.$container.find('.cp-comment-form').remove();
|
||||
});
|
||||
Env.$inner.on('click', 'comment', function (e) {
|
||||
var $comment = $(e.target);
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
|
@ -397,7 +397,7 @@ define([
|
|||
var addToolbarHideBtn = function (framework, $bar) {
|
||||
// Expand / collapse the toolbar
|
||||
var cfg = {
|
||||
element: $bar.find('.cke_toolbox_main')
|
||||
element: $bar
|
||||
};
|
||||
var onClick = function (visible) {
|
||||
framework._.sfCommon.setAttribute(['pad', 'showToolbar'], visible);
|
||||
|
@ -530,6 +530,15 @@ define([
|
|||
|
||||
mkHelpMenu(framework);
|
||||
|
||||
framework._.sfCommon.getAttribute(['pad', 'width'], function (err, data) {
|
||||
var active = data || typeof(data) === "undefined";
|
||||
if (active) {
|
||||
$contentContainer.addClass('cke_body_width');
|
||||
} else {
|
||||
editor.execCommand('pagemode');
|
||||
}
|
||||
});
|
||||
|
||||
framework.onEditableChange(function (unlocked) {
|
||||
if (!framework.isReadOnly()) {
|
||||
$inner.attr('contenteditable', '' + Boolean(unlocked));
|
||||
|
@ -663,7 +672,7 @@ define([
|
|||
});
|
||||
|
||||
if (!framework.isReadOnly()) {
|
||||
addToolbarHideBtn(framework, $contentContainer);
|
||||
addToolbarHideBtn(framework, $('.cke_toolbox_main'));
|
||||
} else {
|
||||
$('.cke_toolbox_main').hide();
|
||||
}
|
||||
|
@ -717,29 +726,6 @@ define([
|
|||
}
|
||||
});
|
||||
|
||||
framework._.sfCommon.getAttribute(['pad', 'width'], function (err, data) {
|
||||
var active = data || typeof(data) === "undefined";
|
||||
if (active) {
|
||||
$contentContainer.addClass('cke_body_width');
|
||||
}
|
||||
var $width = framework._.sfCommon.createButton('', true, {
|
||||
icon: 'fa-arrows-h',
|
||||
text: active ? Messages.pad_useFullWidth : Messages.pad_usePageWidth,
|
||||
name: "pad-width",
|
||||
},function () {
|
||||
if (active) {
|
||||
$contentContainer.removeClass('cke_body_width');
|
||||
} else {
|
||||
$contentContainer.addClass('cke_body_width');
|
||||
}
|
||||
active = !active;
|
||||
var key = active ? Messages.pad_useFullWidth : Messages.pad_usePageWidth;
|
||||
$width.find('.cp-toolbar-drawer-element').text(key);
|
||||
framework._.sfCommon.setAttribute(['pad', 'width'], active);
|
||||
});
|
||||
framework._.toolbar.$drawer.append($width);
|
||||
});
|
||||
|
||||
framework._.sfCommon.isPadStored(function (err, val) {
|
||||
if (!val) { return; }
|
||||
var b64images = $inner.find('img[src^="data:image"]:not(.cke_reset)');
|
||||
|
@ -972,6 +958,30 @@ define([
|
|||
module.ckeditor = editor = Ckeditor.replace('editor1', {
|
||||
customConfig: '/customize/ckeditor-config.js',
|
||||
});
|
||||
|
||||
editor.addCommand('pagemode', {
|
||||
exec: function () {
|
||||
if (!framework) { return; }
|
||||
var $contentContainer = $('#cke_1_contents');
|
||||
var $button = $('.cke_button__pagemode');
|
||||
var isLarge = $button.hasClass('cke_button_on');
|
||||
if (isLarge) {
|
||||
$button.addClass('cke_button_off').removeClass('cke_button_on');
|
||||
$contentContainer.addClass('cke_body_width');
|
||||
} else {
|
||||
$button.addClass('cke_button_on').removeClass('cke_button_off');
|
||||
$contentContainer.removeClass('cke_body_width');
|
||||
}
|
||||
framework._.sfCommon.setAttribute(['pad', 'width'], isLarge);
|
||||
}
|
||||
});
|
||||
editor.ui.addButton('PageMode', {
|
||||
label: Messages.pad_useFullWidth,
|
||||
command: 'pagemode',
|
||||
icon: '/pad/icons/arrows-h.png',
|
||||
toolbar: 'document,60'
|
||||
});
|
||||
|
||||
editor.on('instanceReady', waitFor());
|
||||
}).nThen(function () {
|
||||
editor.plugins.mediatag.import = function ($mt) {
|
||||
|
@ -985,6 +995,7 @@ define([
|
|||
var $ckeToolbar = $('#cke_1_top').find('.cke_toolbox_main');
|
||||
$mainContainer.prepend($ckeToolbar.addClass('cke_reset_all'));
|
||||
$contentContainer.append(h('div#cp-app-pad-comments'));
|
||||
$ckeToolbar.find('.cke_button__image_icon').parent().hide();
|
||||
}).nThen(waitFor());
|
||||
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
|
|
|
@ -78,6 +78,7 @@ define([
|
|||
'pad': [
|
||||
'cp-settings-pad-width',
|
||||
'cp-settings-pad-spellcheck',
|
||||
'cp-settings-pad-notif',
|
||||
],
|
||||
'code': [
|
||||
'cp-settings-code-indent-unit',
|
||||
|
@ -1275,6 +1276,38 @@ define([
|
|||
return $div;
|
||||
};
|
||||
|
||||
// XXX Messages.settings_padNotifTitle
|
||||
// XXX Messages.settings_padNotifHint
|
||||
// XXX Messages.settings_padNotifCheckbox ("disable comments notifications")
|
||||
Messages.settings_padNotifTitle = "Comments notifications"; // XXX
|
||||
Messages.settings_padNotifHint = "Block notifications when someone replies to one of your comments"; // XXX
|
||||
Messages.settings_padNotifCheckbox = "Disable comment notifications";
|
||||
makeBlock('pad-notif', function (cb) {
|
||||
var $cbox = $(UI.createCheckbox('cp-settings-pad-notif',
|
||||
Messages.settings_padNotifCheckbox,
|
||||
false, { label: {class: 'noTitle'} }));
|
||||
|
||||
var spinner = UI.makeSpinner($cbox);
|
||||
|
||||
// Checkbox: "Enable safe links"
|
||||
var $checkbox = $cbox.find('input').on('change', function () {
|
||||
spinner.spin();
|
||||
var val = $checkbox.is(':checked');
|
||||
common.setAttribute(['pad', 'disableNotif'], val, function () {
|
||||
spinner.done();
|
||||
});
|
||||
});
|
||||
|
||||
common.getAttribute(['pad', 'disableNotif'], function (e, val) {
|
||||
if (e) { return void console.error(e); }
|
||||
if (val === true) {
|
||||
$checkbox.attr('checked', 'checked');
|
||||
}
|
||||
});
|
||||
|
||||
cb($cbox);
|
||||
}, true);
|
||||
|
||||
// Code settings
|
||||
|
||||
create['code-indent-unit'] = function () {
|
||||
|
|
Loading…
Reference in New Issue