Add Quote Previewing.

Hell yeah, multi-inlined-quote highlighting!
This commit is contained in:
Nicolas Stepien 2012-09-04 02:30:59 +02:00
parent 8084573457
commit 264f03697b
3 changed files with 283 additions and 45 deletions

View File

@ -73,7 +73,7 @@
*/
(function() {
var $, $$, Board, Clone, Conf, Config, Get, Main, Post, QuoteBacklink, QuoteInline, Quotify, Thread, Time, UI, d, g,
var $, $$, Board, Clone, Conf, Config, Get, Main, Post, QuoteBacklink, QuoteInline, QuotePreview, Quotify, Thread, Time, UI, d, g,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
@ -933,6 +933,13 @@
$.log(err, 'Quote Inline');
}
}
if (Conf['Quote Preview']) {
try {
QuotePreview.init();
} catch (err) {
$.log(err, 'Quote Preview');
}
}
if (Conf['Quote Backlinks']) {
try {
QuoteBacklink.init();
@ -1006,16 +1013,43 @@
return $.on(d, 'DOMNodeInserted', Main.addStyle);
}
},
css: "/* general */\n.move {\n cursor: move;\n}\nlabel {\n cursor: pointer;\n}\n.warning {\n color: red;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\n display: block !important;\n}\n.post {\n overflow: visible !important;\n}\n\n/* header */\nbody.fourchan_x {\n margin-top: 2.5em;\n}\n#boardNavDesktop.reply {\n border-width: 0 0 1px;\n padding: 4px;\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n transition: opacity .1s ease-in-out;\n -o-transition: opacity .1s ease-in-out;\n -moz-transition: opacity .1s ease-in-out;\n -webkit-transition: opacity .1s ease-in-out;\n z-index: 1;\n}\n#boardNavDesktop.reply:not(:hover) {\n opacity: .4;\n transition: opacity 1.5s .5s ease-in-out;\n -o-transition: opacity 1.5s .5s ease-in-out;\n -moz-transition: opacity 1.5s .5s ease-in-out;\n -webkit-transition: opacity 1.5s .5s ease-in-out;\n}\n#boardNavDesktop.reply a {\n margin: -1px;\n}\n#settings {\n float: right;\n}\n\n/* quotes */\n.inlined {\n opacity: .5;\n}\n.forwarded {\n display: none;\n}\n.inline {\n border: 1px solid rgba(128, 128, 128, 0.5);\n display: table;\n margin: 2px 0;\n}\n.inline .post {\n display: table !important;\n margin: 0 !important;\n padding: 1px 2px !important;\n border: 0 !important;\n}"
css: "/* general */\n.dialog.reply {\n display: block;\n border: 1px solid rgba(0, 0, 0, .25);\n padding: 0;\n}\n.move {\n cursor: move;\n}\nlabel {\n cursor: pointer;\n}\na[href=\"javascript:;\"] {\n text-decoration: none;\n}\n.warning {\n color: red;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\n display: block !important;\n}\n.post {\n overflow: visible !important;\n}\n\n/* header */\nbody.fourchan_x {\n margin-top: 2.5em;\n}\n#boardNavDesktop.reply {\n border-width: 0 0 1px;\n padding: 4px;\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n transition: opacity .1s ease-in-out;\n -o-transition: opacity .1s ease-in-out;\n -moz-transition: opacity .1s ease-in-out;\n -webkit-transition: opacity .1s ease-in-out;\n z-index: 1;\n}\n#boardNavDesktop.reply:not(:hover) {\n opacity: .4;\n transition: opacity 1.5s .5s ease-in-out;\n -o-transition: opacity 1.5s .5s ease-in-out;\n -moz-transition: opacity 1.5s .5s ease-in-out;\n -webkit-transition: opacity 1.5s .5s ease-in-out;\n}\n#boardNavDesktop.reply a {\n margin: -1px;\n}\n#settings {\n float: right;\n}\n\n/* quote related */\n.inlined {\n opacity: .5;\n}\n#qp input, .forwarded {\n display: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\n text-decoration: none;\n border-bottom: 1px dashed;\n}\n.inline {\n border: 1px solid rgba(128, 128, 128, .5);\n display: table;\n margin: 2px 0;\n}\n.inline .post {\n border: 0 !important;\n display: table !important;\n margin: 0 !important;\n padding: 1px 2px !important;\n}\n#qp {\n position: fixed;\n padding: 2px 2px 5px;\n}\n#qp .post {\n border: none;\n margin: 0;\n padding: 0;\n}\n#qp img {\n max-height: 300px;\n max-width: 500px;\n}\n.qphl {\n outline: 2px solid rgba(216, 94, 49, .7);\n}"
};
Get = {
post: function(board, threadID, postID, root) {
postFromRoot: function(root) {
var board, index, link, post, postID;
link = $('a[title="Highlight this post"]', root);
board = link.pathname.split('/')[1];
postID = link.hash.slice(2);
index = root.dataset.clone;
post = g.posts["" + board + "." + postID];
if (index) {
return post.clones[index];
} else {
return post;
}
},
postDataFromLink: function(link) {
var board, path, postID, threadID;
if (link.host === 'boards.4chan.org') {
path = link.pathname.split('/');
board = path[1];
threadID = path[3];
postID = link.hash.slice(2);
}
return {
board: board,
threadID: threadID,
postID: postID
};
},
postClone: function(board, threadID, postID, root) {
var clone, origin;
if (origin = g.posts["" + board + "." + postID]) {
clone = origin.addClone();
$.add(root, Get.cleanRoot(clone));
Main.callbackNodes(Post, [clone]);
$.add(root, Get.cleanRoot(clone));
return;
}
root.textContent = "Loading post No." + postID + "...";
@ -1072,8 +1106,8 @@
return;
}
clone = post.addClone();
$.replace(root.firstChild, Get.cleanRoot(clone));
return Main.callbackNodes(Post, [clone]);
Main.callbackNodes(Post, [clone]);
return $.replace(root.firstChild, Get.cleanRoot(clone));
}
};
@ -1146,15 +1180,12 @@
}
},
toggle: function(e) {
var board, path, postID, threadID;
var board, postID, threadID, _ref;
if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) {
return;
}
e.preventDefault();
path = this.pathname.split('/');
board = path[1];
threadID = path[3];
postID = this.hash.slice(2);
_ref = Get.postDataFromLink(this), board = _ref.board, threadID = _ref.threadID, postID = _ref.postID;
if ($.hasClass(this, 'inlined')) {
QuoteInline.rm(this, board, threadID, postID);
} else {
@ -1173,7 +1204,7 @@
});
root = (isBacklink = $.hasClass(quotelink, 'backlink')) ? quotelink.parentNode.parentNode : $.x('ancestor-or-self::*[parent::blockquote][1]', quotelink);
$.after(root, inline);
Get.post(board, threadID, postID, inline);
Get.postClone(board, threadID, postID, inline);
if (!(board === g.BOARD.ID && $.x("ancestor::div[@id='t" + threadID + "']", quotelink))) {
return;
}
@ -1184,7 +1215,7 @@
}
},
rm: function(quotelink, board, threadID, postID) {
var el, inThreadID, index, inline, inlines, path, post, root, _i, _len;
var el, inThreadID, inline, inlines, post, root, _i, _len, _ref;
root = $.x("following::div[@id='i" + postID + "'][1]", quotelink);
$.rm(root);
if (!(el = root.firstElementChild)) {
@ -1202,13 +1233,12 @@
inlines = $$('.inlined', el);
for (_i = 0, _len = inlines.length; _i < _len; _i++) {
inline = inlines[_i];
path = inline.pathname.split('/');
board = path[1];
threadID = path[3];
postID = inline.hash.slice(2);
index = $.x("following::div[@id='i" + postID + "'][1]/child::div", inline).dataset.clone;
_ref = Get.postDataFromLink(inline), board = _ref.board, threadID = _ref.threadID, postID = _ref.postID;
if (!(root = $.x("following::div[@id='i" + postID + "'][1]", inline).firstElementChild)) {
continue;
}
post = g.posts["" + board + "." + postID];
post.rmClone(index);
post.rmClone(root.dataset.clone);
if (Conf['Forward Hiding'] && board === g.BOARD.ID && threadID === inThreadID && $.hasClass(inline, 'backlink')) {
if (!--post.forwarded) {
delete post.forwarded;
@ -1219,6 +1249,95 @@
}
};
QuotePreview = {
init: function() {
return Post.prototype.callbacks.push({
name: 'Quote Preview',
cb: this.node
});
},
node: function() {
var link, _i, _j, _len, _len1, _ref, _ref1;
_ref = this.nodes.quotelinks;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
$.on(link, 'mouseover', QuotePreview.mouseover);
}
_ref1 = this.nodes.backlinks;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
link = _ref1[_j];
$.on(link, 'mouseover', QuotePreview.mouseover);
}
},
mouseover: function(e) {
var board, clone, origin, post, postID, posts, qp, quote, quoterID, threadID, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;
if ($.hasClass(this, 'inlined')) {
return;
}
if (UI.el) {
return;
}
_ref = Get.postDataFromLink(this), board = _ref.board, threadID = _ref.threadID, postID = _ref.postID;
qp = UI.el = $.el('div', {
id: 'qp',
className: 'reply dialog'
});
UI.hover(e);
Get.postClone(board, threadID, postID, qp);
$.add(d.body, qp);
$.on(this, 'mousemove', UI.hover);
$.on(this, 'mouseout click', QuotePreview.mouseout);
if (!(origin = g.posts["" + board + "." + postID])) {
return;
}
if (Conf['Quote Highlighting']) {
posts = [origin].concat(origin.clones);
posts.pop();
for (_i = 0, _len = posts.length; _i < _len; _i++) {
post = posts[_i];
$.addClass(post.nodes.post, 'qphl');
}
}
quoterID = $.x('ancestor::*[@id][1]', this).id.match(/\d+$/)[0];
clone = Get.postFromRoot(qp.firstChild);
_ref1 = clone.nodes.quotelinks;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
quote = _ref1[_j];
if (quote.hash.slice(2) === quoterID) {
$.addClass(quote, 'forwardlink');
}
}
_ref2 = clone.nodes.backlinks;
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
quote = _ref2[_k];
if (quote.hash.slice(2) === quoterID) {
$.addClass(quote, 'forwardlink');
}
}
},
mouseout: function(e) {
var clone, post, root, _i, _len, _ref;
root = UI.el.firstElementChild;
UI.hoverend();
$.off(this, 'mousemove', UI.hover);
$.off(this, 'mouseout click', QuotePreview.mouseout);
if (!root) {
return;
}
clone = Get.postFromRoot(root);
post = clone.origin;
post.rmClone(root.dataset.clone);
if (!Conf['Quote Highlighting']) {
return;
}
_ref = [post].concat(post.clones);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
post = _ref[_i];
$.rmClass(post.nodes.post, 'qphl');
}
}
};
QuoteBacklink = {
init: function() {
var format;
@ -1258,6 +1377,9 @@
for (_k = 0, _len2 = containers.length; _k < _len2; _k++) {
container = containers[_k];
link = a.cloneNode(true);
if (Conf['Quote Preview']) {
$.on(link, 'mouseover', QuotePreview.mouseover);
}
if (Conf['Quote Inline']) {
$.on(link, 'click', QuoteInline.toggle);
}

View File

@ -1,6 +1,8 @@
alpha
- Mayhem
Fix Chrome's install warning that 4chan X would execute on all domains.
Fix Quote Backlinks not affecting inlined quotes.
Fix Quote Highlighting not affecting inlined quotes.
2.34.9
- Mayhem

View File

@ -746,6 +746,13 @@ Main =
# XXX handle error
$.log err, 'Quote Inline'
if Conf['Quote Preview']
try
QuotePreview.init()
catch err
# XXX handle error
$.log err, 'Quote Preview'
if Conf['Quote Backlinks']
try
QuoteBacklink.init()
@ -807,12 +814,20 @@ Main =
$.on d, 'DOMNodeInserted', Main.addStyle
css: """
/* general */
.dialog.reply {
display: block;
border: 1px solid rgba(0, 0, 0, .25);
padding: 0;
}
.move {
cursor: move;
}
label {
cursor: pointer;
}
a[href="javascript:;"] {
text-decoration: none;
}
.warning {
color: red;
}
@ -856,34 +871,78 @@ body.fourchan_x {
float: right;
}
/* quotes */
/* quote related */
.inlined {
opacity: .5;
}
.forwarded {
#qp input, .forwarded {
display: none;
}
.quotelink.forwardlink,
.backlink.forwardlink {
text-decoration: none;
border-bottom: 1px dashed;
}
.inline {
border: 1px solid rgba(128, 128, 128, 0.5);
border: 1px solid rgba(128, 128, 128, .5);
display: table;
margin: 2px 0;
}
.inline .post {
border: 0 !important;
display: table !important;
margin: 0 !important;
padding: 1px 2px !important;
border: 0 !important;
}
#qp {
position: fixed;
padding: 2px 2px 5px;
}
#qp .post {
border: none;
margin: 0;
padding: 0;
}
#qp img {
max-height: 300px;
max-width: 500px;
}
.qphl {
outline: 2px solid rgba(216, 94, 49, .7);
}
"""
Get =
post: (board, threadID, postID, root) ->
postFromRoot: (root) ->
link = $ 'a[title="Highlight this post"]', root
board = link.pathname.split('/')[1]
postID = link.hash[2..]
index = root.dataset.clone
post = g.posts["#{board}.#{postID}"]
if index then post.clones[index] else post
postDataFromLink: (link) ->
if link.host is 'boards.4chan.org'
path = link.pathname.split '/'
board = path[1]
threadID = path[3]
postID = link.hash[2..]
# XXX
# else # quote resurrection
# board = ???
# threadID = ???
# postID = ???
return {
board: board
threadID: threadID
postID: postID
}
postClone: (board, threadID, postID, root) ->
if origin = g.posts["#{board}.#{postID}"]
clone = origin.addClone()
$.add root, Get.cleanRoot clone
Main.callbackNodes Post, [clone]
$.add root, Get.cleanRoot clone
return
root.textContent = "Loading post No.#{postID}..."
@ -947,8 +1006,8 @@ Get =
# Stop here if the container has been removed while loading.
return unless root.parentNode
clone = post.addClone()
$.replace root.firstChild, Get.cleanRoot clone
Main.callbackNodes Post, [clone]
$.replace root.firstChild, Get.cleanRoot clone
Quotify =
init: ->
@ -1032,12 +1091,7 @@ QuoteInline =
toggle: (e) ->
return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0
e.preventDefault()
# XXX quote resurrection
# id = @dataset.id or @hash[2..]
path = @pathname.split '/'
board = path[1]
threadID = path[3]
postID = @hash[2..]
{board, threadID, postID} = Get.postDataFromLink @
if $.hasClass @, 'inlined'
QuoteInline.rm @, board, threadID, postID
else
@ -1056,7 +1110,7 @@ QuoteInline =
else
$.x 'ancestor-or-self::*[parent::blockquote][1]', quotelink
$.after root, inline
Get.post board, threadID, postID, inline
Get.postClone board, threadID, postID, inline
return unless board is g.BOARD.ID and $.x "ancestor::div[@id='t#{threadID}']", quotelink
post = g.posts["#{board}.#{postID}"]
@ -1078,7 +1132,7 @@ QuoteInline =
root = $.x "following::div[@id='i#{postID}'][1]", quotelink
$.rm root
# Stop if it's still loading.
# Stop if it only contains text.
return unless el = root.firstElementChild
# Dereference clone.
@ -1099,14 +1153,10 @@ QuoteInline =
# Repeat.
inlines = $$ '.inlined', el
for inline in inlines
# XXX resurrected quotes
path = inline.pathname.split '/'
board = path[1]
threadID = path[3]
postID = inline.hash[2..]
index = $.x("following::div[@id='i#{postID}'][1]/child::div", inline).dataset.clone
post = g.posts["#{board}.#{postID}"]
post.rmClone index
{board, threadID, postID} = Get.postDataFromLink inline
continue unless root = $.x("following::div[@id='i#{postID}'][1]", inline).firstElementChild
post = g.posts["#{board}.#{postID}"]
post.rmClone root.dataset.clone
if Conf['Forward Hiding'] and
board is g.BOARD.ID and
@ -1117,6 +1167,71 @@ QuoteInline =
$.rmClass post.nodes.root, 'forwarded'
return
QuotePreview =
init: ->
Post::callbacks.push
name: 'Quote Preview'
cb: @node
node: ->
for link in @nodes.quotelinks
$.on link, 'mouseover', QuotePreview.mouseover
for link in @nodes.backlinks
$.on link, 'mouseover', QuotePreview.mouseover
return
mouseover: (e) ->
return if $.hasClass @, 'inlined'
# Don't stop other elements from dragging
return if UI.el
{board, threadID, postID} = Get.postDataFromLink @
qp = UI.el = $.el 'div',
id: 'qp'
className: 'reply dialog'
UI.hover e
Get.postClone board, threadID, postID, qp
$.add d.body, qp
$.on @, 'mousemove', UI.hover
$.on @, 'mouseout click', QuotePreview.mouseout
return unless origin = g.posts["#{board}.#{postID}"]
if Conf['Quote Highlighting']
posts = [origin].concat origin.clones
# Remove the clone that's in the qp from the array.
posts.pop()
for post in posts
$.addClass post.nodes.post, 'qphl'
quoterID = $.x('ancestor::*[@id][1]', @).id.match(/\d+$/)[0]
clone = Get.postFromRoot qp.firstChild
for quote in clone.nodes.quotelinks
if quote.hash[2..] is quoterID
$.addClass quote, 'forwardlink'
for quote in clone.nodes.backlinks
if quote.hash[2..] is quoterID
$.addClass quote, 'forwardlink'
return
mouseout: (e) ->
root = UI.el.firstElementChild
UI.hoverend()
$.off @, 'mousemove', UI.hover
$.off @, 'mouseout click', QuotePreview.mouseout
# Stop if it only contains text.
return unless root
clone = Get.postFromRoot root
post = clone.origin
post.rmClone root.dataset.clone
return unless Conf['Quote Highlighting']
for post in [post].concat post.clones
$.rmClass post.nodes.post, 'qphl'
return
QuoteBacklink =
# Backlinks appending need to work for:
# - previous, same, and following posts.
@ -1153,9 +1268,8 @@ QuoteBacklink =
containers.push clone.nodes.backlinkContainer
for container in containers
link = a.cloneNode true
# XXX
# if Conf['Quote Preview']
# $.on link, 'mouseover', QuotePreview.mouseover
if Conf['Quote Preview']
$.on link, 'mouseover', QuotePreview.mouseover
if Conf['Quote Inline']
$.on link, 'click', QuoteInline.toggle
$.add container, [$.tn(' '), link]