Refactor method to get post for Quote Previewing and Quote Inlining. Getting ready for post fetching from archives. Also fix forward quotes on Firefox in inlined quotes, and other tiny fixes.

This commit is contained in:
Nicolas Stepien 2012-06-05 12:29:41 +02:00
parent 286b704525
commit 641aa39a7b
2 changed files with 305 additions and 317 deletions

View File

@ -72,7 +72,7 @@
*/ */
(function() { (function() {
var $, $$, Anonymize, AutoGif, Conf, Config, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, GetTitle, ImageExpand, ImageHover, Keybinds, Main, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base; var $, $$, Anonymize, AutoGif, Conf, Config, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base;
Config = { Config = {
main: { main: {
@ -874,7 +874,7 @@
quote.href = "res/" + href; quote.href = "res/" + href;
} }
id = reply.id.slice(2); id = reply.id.slice(2);
link = $('.postInfo > .postNum > a:first-child', reply); link = $('.postInfo > .postNum > a[title="Highlight this post"]', reply);
link.href = "res/" + threadID + "#p" + id; link.href = "res/" + threadID + "#p" + id;
link.nextSibling.href = "res/" + threadID + "#q" + id; link.nextSibling.href = "res/" + threadID + "#q" + id;
nodes.push(reply); nodes.push(reply);
@ -2686,7 +2686,7 @@
watched[_name = g.BOARD] || (watched[_name] = {}); watched[_name = g.BOARD] || (watched[_name] = {});
watched[g.BOARD][id] = { watched[g.BOARD][id] = {
href: "/" + g.BOARD + "/res/" + id, href: "/" + g.BOARD + "/res/" + id,
textContent: GetTitle(thread) textContent: Get.title(thread)
}; };
$.set('watched', watched); $.set('watched', watched);
Watcher.refresh(); Watcher.refresh();
@ -2708,7 +2708,7 @@
if ((trip = name.nextElementSibling) && trip.className === 'postertrip') { if ((trip = name.nextElementSibling) && trip.className === 'postertrip') {
$.rm(trip); $.rm(trip);
} }
if ((parent = name.parentNode).className === 'useremail' && !/^sage$/i.test(parent.pathname)) { if ((parent = name.parentNode).className === 'useremail' && !/^mailto:sage$/i.test(parent.href)) {
return $.replace(parent, name); return $.replace(parent, name);
} }
} }
@ -2992,25 +2992,95 @@
} }
}; };
GetTitle = function(thread) { Get = {
var el, op, span; post: function(board, threadID, postID, root, cb) {
op = $('.op', thread); var post;
el = $('.subject', op); if (board === g.BOARD && (post = $.id("pc" + postID))) {
if (!el.textContent) { $.add(root, Get.cleanPost(post.cloneNode(true)));
el = $('blockquote', op); return;
if (!el.textContent) {
el = $('.nameBlock', op);
} }
root.textContent = "Loading post No." + postID + "...";
if (threadID) {
return $.cache("/" + board + "/res/" + threadID, function() {
return Get.parsePost(this, board, threadID, postID, root, cb);
});
}
},
parsePost: function(req, board, threadID, postID, root, cb) {
var doc, href, link, pc, quote, status, _i, _len, _ref;
status = req.status;
if (status !== 200) {
root.textContent = status === 404 ? "Thread No." + threadID + " has not been found." : "Error " + req.status + ": " + req.statusText + ".";
return;
}
doc = d.implementation.createHTMLDocument('');
doc.documentElement.innerHTML = req.response;
if (!(pc = doc.getElementById("pc" + postID))) {
root.textContent = "Post No." + postID + " has not been found.";
return;
}
pc = Get.cleanPost(d.importNode(pc, true));
_ref = $$('.quotelink', pc);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quote = _ref[_i];
href = quote.getAttribute('href');
if (href[0] === '/') {
continue;
}
quote.href = "/" + board + "/res/" + href;
}
link = $('.postInfo > .postNum > a[title="Highlight this post"]', pc);
link.href = "/" + board + "/res/" + threadID + "#p" + postID;
link.nextSibling.href = "/" + board + "/res/" + threadID + "#q" + postID;
$.replace(root.firstChild, pc);
if (cb) {
return cb();
}
},
parseArchivedPost: function(req, board, postID, root, cb) {},
cleanPost: function(root) {
var child, el, now, post, _i, _j, _len, _len1, _ref, _ref1;
post = $('.post', root);
_ref = Array.prototype.slice.call(root.childNodes);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
if (child !== post) {
$.rm(child);
}
}
now = Date.now();
_ref1 = $$('[id]', root);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
el = _ref1[_j];
el.id = "" + now + "_" + el.id;
}
$.rmClass(root, 'forwarded');
$.rmClass(root, 'qphl');
$.rmClass(post, 'highlight');
$.rmClass(post, 'qphl');
root.hidden = post.hidden = false;
return root;
},
title: function(thread) {
var el, op, span;
op = $('.op', thread);
el = $('.subject', op);
if (!el.textContent) {
el = $('blockquote', op);
if (!el.textContent) {
el = $('.nameBlock', op);
}
}
span = $.el('span', {
innerHTML: el.innerHTML.replace(/<br>/g, ' ')
});
return "/" + g.BOARD + "/ - " + (span.textContent.trim());
} }
span = $.el('span', {
innerHTML: el.innerHTML.replace(/<br>/g, ' ')
});
return "/" + g.BOARD + "/ - " + (span.textContent.trim());
}; };
TitlePost = { TitlePost = {
init: function() { init: function() {
return d.title = GetTitle(); return d.title = Get.title();
} }
}; };
@ -3103,49 +3173,36 @@
return this.classList.toggle('inlined'); return this.classList.toggle('inlined');
}, },
add: function(q, id) { add: function(q, id) {
var clonePost, el, i, inline, isBacklink, pathname, root; var el, i, inline, isBacklink, path, root;
if (!(isBacklink = /\bbacklink\b/.test(q.className))) { if (!(isBacklink = /\bbacklink\b/.test(q.className))) {
root = q; root = q;
while (root.parentNode.nodeName !== 'BLOCKQUOTE') { while (root.parentNode.nodeName !== 'BLOCKQUOTE') {
root = root.parentNode; root = root.parentNode;
} }
} }
if (el = $.id("p" + id)) { path = q.pathname.split('/');
if (/\bop\b/.test(el.className)) { el = path[1] === g.BOARD ? $.id("p" + id) : false;
$.rmClass(el.parentNode, 'qphl'); inline = $.el('div', {
} else { id: "i" + id,
$.rmClass(el, 'qphl'); className: el ? 'inline' : 'inline crosspost'
} });
clonePost = QuoteInline.clone(id, el); $.after((isBacklink ? q.parentNode : root), inline);
if (isBacklink) { Get.post(path[1], path[3], id, inline);
$.after(q.parentNode, clonePost); if (!el) {
if (Conf['Forward Hiding']) {
$.addClass(el.parentNode, 'forwarded');
++el.dataset.forwarded || (el.dataset.forwarded = 1);
}
} else {
$.after(root, clonePost);
}
if ((i = Unread.replies.indexOf(el)) !== -1) {
Unread.replies.splice(i, 1);
Unread.update(true);
}
return; return;
} }
inline = $.el('div', { if (isBacklink && Conf['Forward Hiding']) {
className: 'inline', $.addClass(el.parentNode, 'forwarded');
id: "i" + id, ++el.dataset.forwarded || (el.dataset.forwarded = 1);
textContent: "Loading " + id + "..." }
}); if ((i = Unread.replies.indexOf(el)) !== -1) {
$.after(root, inline); Unread.replies.splice(i, 1);
pathname = q.pathname; return Unread.update(true);
return $.cache(pathname, function() { }
return QuoteInline.parse(this, pathname, id, inline);
});
}, },
rm: function(q, id) { rm: function(q, id) {
var div, inlined, _i, _len, _ref; var div, inlined, _i, _len, _ref;
div = $.x("following::div[@id='i_pc" + id + "']", q); div = $.x("following::div[@id='i" + id + "']", q);
$.rm(div); $.rm(div);
if (!Conf['Forward Hiding']) { if (!Conf['Forward Hiding']) {
return; return;
@ -3164,50 +3221,6 @@
return $.rmClass(div.parentNode, 'forwarded'); return $.rmClass(div.parentNode, 'forwarded');
} }
} }
},
parse: function(req, pathname, id, inline) {
var doc, href, link, newInline, node, quote, _i, _len, _ref;
if (!inline.parentNode) {
return;
}
if (req.status !== 200) {
inline.textContent = "" + req.status + " " + req.statusText;
return;
}
doc = d.implementation.createHTMLDocument('');
doc.documentElement.innerHTML = req.response;
node = doc.getElementById("p" + id);
newInline = QuoteInline.clone(id, node);
_ref = $$('.quotelink', newInline);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quote = _ref[_i];
href = quote.getAttribute('href');
if (href[0] === '/') {
continue;
}
quote.href = "res/" + href;
}
link = $('.postInfo > .postNum > a:first-child', newInline);
link.href = "" + pathname + "#p" + id;
link.nextSibling.href = "" + pathname + "#q" + id;
$.addClass(newInline, 'crosspost');
return $.replace(inline, newInline);
},
clone: function(id, el) {
var clone, node, post, _i, _len, _ref;
clone = $.el('div', {
className: 'postContainer inline',
id: "i_pc" + id
});
post = el.cloneNode(true);
post.hidden = false;
$.add(clone, post);
_ref = $$('[id]', clone);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i];
node.id = "i_" + node.id;
}
return clone;
} }
}; };
@ -3231,7 +3244,7 @@
} }
}, },
mouseover: function(e) { mouseover: function(e) {
var el, id, klass, qp, quote, replyID, _i, _j, _len, _len1, _ref, _ref1; var el, id, parent, path, qp, quote, quoterID, _i, _len, _ref;
if (/\binlined\b/.test(this.className)) { if (/\binlined\b/.test(this.className)) {
return; return;
} }
@ -3244,21 +3257,39 @@
if (UI.el) { if (UI.el) {
return; return;
} }
path = this.pathname.split('/');
id = this.hash.slice(2);
qp = UI.el = $.el('div', { qp = UI.el = $.el('div', {
id: 'qp', id: 'qp',
className: 'post reply dialog' className: 'reply dialog'
}); });
UI.hover(e);
$.add(d.body, qp); $.add(d.body, qp);
id = this.hash.slice(2); Get.post(path[1], path[3], id, qp, function() {
if (el = $.id("p" + id)) { var bq, fileInfo, img, post;
_ref = el.parentNode.className.split(' '); bq = $('blockquote', qp);
for (_i = 0, _len = _ref.length; _i < _len; _i++) { Main.prettify(bq);
klass = _ref[_i]; post = {
if (!/^((op|reply|post)Container|forwarded)$/.test(klass)) { el: qp
$.addClass(qp, klass); };
if (fileInfo = $('.fileInfo', qp)) {
img = fileInfo.nextElementSibling.firstElementChild;
if (img.alt !== 'File deleted.') {
post.fileInfo = fileInfo;
post.img = img;
} }
} }
qp.innerHTML = el.innerHTML; if (Conf['Image Auto-Gif']) {
AutoGif.node(post);
}
if (Conf['Time Formatting']) {
Time.node(post);
}
if (Conf['File Info Formatting']) {
return FileInfo.node(post);
}
});
if (path[1] === g.BOARD && (el = $.id("p" + id))) {
if (Conf['Quote Highlighting']) { if (Conf['Quote Highlighting']) {
if (/\bop\b/.test(el.className)) { if (/\bop\b/.test(el.className)) {
$.addClass(el.parentNode, 'qphl'); $.addClass(el.parentNode, 'qphl');
@ -3266,72 +3297,31 @@
$.addClass(el, 'qphl'); $.addClass(el, 'qphl');
} }
} }
replyID = $.x('ancestor::div[contains(@class,"postContainer")]', this).id.match(/\d+$/)[0]; parent = this.parentNode;
_ref1 = $$('.quotelink, .backlink', qp); while (!parent.id) {
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { parent = parent.parentNode;
quote = _ref1[_j]; }
if (quote.hash.slice(2) === replyID) { quoterID = parent.id.match(/\d+$/)[0];
_ref = $$('.quotelink, .backlink', qp);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quote = _ref[_i];
if (quote.hash.slice(2) === quoterID) {
$.addClass(quote, 'forwardlink'); $.addClass(quote, 'forwardlink');
} }
} }
} else {
qp.textContent = "Loading " + id + "...";
$.cache(this.pathname, function() {
return QuotePreview.parse(this, id);
});
} }
UI.hover(e);
$.on(this, 'mousemove', UI.hover); $.on(this, 'mousemove', UI.hover);
return $.on(this, 'mouseout click', QuotePreview.mouseout); return $.on(this, 'mouseout click', QuotePreview.mouseout);
}, },
mouseout: function() { mouseout: function(e) {
var el; var el;
UI.hoverend(); UI.hoverend();
if (el = $.id(this.hash.slice(1))) { if (el = $.id(this.hash.slice(1))) {
if (/\bop\b/.test(el.className)) { $.rmClass(el, 'qphl');
$.rmClass(el.parentNode, 'qphl'); $.rmClass(el.parentNode, 'qphl');
} else {
$.rmClass(el, 'qphl');
}
} }
$.off(this, 'mousemove', UI.hover); $.off(this, 'mousemove', UI.hover);
return $.off(this, 'mouseout click', QuotePreview.mouseout); return $.off(this, 'mouseout click', QuotePreview.mouseout);
},
parse: function(req, id) {
var bq, doc, fileInfo, img, node, post, qp;
if (!((qp = UI.el) && qp.textContent === ("Loading " + id + "..."))) {
return;
}
if (req.status !== 200) {
qp.textContent = "" + req.status + " " + req.statusText;
return;
}
doc = d.implementation.createHTMLDocument('');
doc.documentElement.innerHTML = req.response;
node = doc.getElementById("p" + id);
qp.innerHTML = node.innerHTML;
bq = $('blockquote', qp);
bq.id += '_qp';
Main.prettify(bq);
post = {
el: qp
};
if (fileInfo = $('.fileInfo', qp)) {
img = fileInfo.nextElementSibling.firstElementChild;
if (img.alt !== 'File deleted.') {
post.fileInfo = fileInfo;
post.img = img;
}
}
if (Conf['Image Auto-Gif']) {
AutoGif.node(post);
}
if (Conf['Time Formatting']) {
Time.node(post);
}
if (Conf['File Info Formatting']) {
return FileInfo.node(post);
}
} }
}; };
@ -3401,7 +3391,7 @@
nodes.push($.tn(text)); nodes.push($.tn(text));
} }
id = quote.match(/\d+$/)[0]; id = quote.match(/\d+$/)[0];
board = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : $('.postInfo > .postNum > a:first-child', post.el).pathname.split('/')[1]; board = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : $('.postInfo > .postNum > a[title="Highlight this post"]', post.el).pathname.split('/')[1];
nodes.push(a = $.el('a', { nodes.push(a = $.el('a', {
textContent: "" + quote + "\u00A0(Dead)" textContent: "" + quote + "\u00A0(Dead)"
})); }));
@ -4203,17 +4193,17 @@
} }
}, },
preParse: function(node) { preParse: function(node) {
var el, fileInfo, img, post, rootClass; var el, fileInfo, img, parentClass, post;
rootClass = node.className; parentClass = node.parentNode.className;
el = $('.post', node); el = $('.post', node);
post = { post = {
root: node, root: node,
el: el, el: el,
"class": el.className, "class": el.className,
id: el.id.slice(1), id: el.id.match(/\d+$/)[0],
threadId: g.THREAD_ID || $.x('ancestor::div[parent::div[@class="board"]]', node).id.slice(1), threadId: g.THREAD_ID || $.x('ancestor::div[parent::div[@class="board"]]', node).id.match(/\d+$/)[0],
isInlined: /\binline\b/.test(rootClass), isInlined: /\binline\b/.test(parentClass),
isCrosspost: /\bcrosspost\b/.test(rootClass), isCrosspost: /\bcrosspost\b/.test(parentClass),
blockquote: el.lastElementChild, blockquote: el.lastElementChild,
quotes: el.getElementsByClassName('quotelink'), quotes: el.getElementsByClassName('quotelink'),
backlinks: el.getElementsByClassName('backlink'), backlinks: el.getElementsByClassName('backlink'),
@ -4637,8 +4627,12 @@ textarea.field {\
}\ }\
\ \
#qp {\ #qp {\
padding: 2px 2px 5px;\
}\
#qp .post {\
border: none;\
margin: 0;\ margin: 0;\
padding: 1px 2px 5px;\ padding: 0;\
}\ }\
#qp img {\ #qp img {\
max-height: 300px;\ max-height: 300px;\
@ -4647,26 +4641,15 @@ textarea.field {\
.qphl {\ .qphl {\
outline: 2px solid rgba(216, 94, 49, .7);\ outline: 2px solid rgba(216, 94, 49, .7);\
}\ }\
.qphl.opContainer {\
outline-offset: -2px;\
}\
div.opContainer {\
display: block !important;\
}\
.inlined {\ .inlined {\
opacity: .5;\ opacity: .5;\
}\ }\
.inline {\ .inline .post {\
background-color: rgba(255, 255, 255, 0.15);\ background-color: rgba(255, 255, 255, 0.15);\
border: 1px solid rgba(128, 128, 128, 0.5);\ border: 1px solid rgba(128, 128, 128, 0.5);\
display: table;\ display: table;\
margin: 2px;\ margin: 2px;\
}\ padding: 2px;\
.inline .post {\
background: none;\
border: none;\
margin: 0;\
padding: 0;\
}\ }\
.opContainer.filter_highlight {\ .opContainer.filter_highlight {\
box-shadow: inset 5px 0 rgba(255,0,0,0.5);\ box-shadow: inset 5px 0 rgba(255,0,0,0.5);\

View File

@ -663,7 +663,7 @@ ExpandThread =
continue if href[0] is '/' # Cross-board quote continue if href[0] is '/' # Cross-board quote
quote.href = "res/#{href}" # Fix pathnames quote.href = "res/#{href}" # Fix pathnames
id = reply.id[2..] id = reply.id[2..]
link = $ '.postInfo > .postNum > a:first-child', reply link = $ '.postInfo > .postNum > a[title="Highlight this post"]', reply
link.href = "res/#{threadID}#p#{id}" link.href = "res/#{threadID}#p#{id}"
link.nextSibling.href = "res/#{threadID}#q#{id}" link.nextSibling.href = "res/#{threadID}#q#{id}"
nodes.push reply nodes.push reply
@ -2099,7 +2099,7 @@ Watcher =
watched[g.BOARD] or= {} watched[g.BOARD] or= {}
watched[g.BOARD][id] = watched[g.BOARD][id] =
href: "/#{g.BOARD}/res/#{id}" href: "/#{g.BOARD}/res/#{id}"
textContent: GetTitle thread textContent: Get.title thread
$.set 'watched', watched $.set 'watched', watched
Watcher.refresh() Watcher.refresh()
true true
@ -2113,7 +2113,7 @@ Anonymize =
name.textContent = 'Anonymous' name.textContent = 'Anonymous'
if (trip = name.nextElementSibling) and trip.className is 'postertrip' if (trip = name.nextElementSibling) and trip.className is 'postertrip'
$.rm trip $.rm trip
if (parent = name.parentNode).className is 'useremail' and not /^sage$/i.test parent.pathname if (parent = name.parentNode).className is 'useremail' and not /^mailto:sage$/i.test parent.href
$.replace parent, name $.replace parent, name
Sauce = Sauce =
@ -2282,19 +2282,87 @@ FileInfo =
M: -> FileInfo.convertUnit 'MB' M: -> FileInfo.convertUnit 'MB'
r: -> FileInfo.data.resolution r: -> FileInfo.data.resolution
GetTitle = (thread) -> Get =
op = $ '.op', thread post: (board, threadID, postID, root, cb) ->
el = $ '.subject', op if board is g.BOARD and post = $.id "pc#{postID}"
unless el.textContent $.add root, Get.cleanPost post.cloneNode true
el = $ 'blockquote', op return
root.textContent = "Loading post No.#{postID}..."
if threadID
$.cache "/#{board}/res/#{threadID}", ->
Get.parsePost @, board, threadID, postID, root, cb
# else if url = Redirect.???
# $.cache url, ->
# Get.parseArchivedPost @, board, postID, root, cb
parsePost: (req, board, threadID, postID, root, cb) ->
{status} = req
if status isnt 200
# thread can die by the time we check a post
# try archive if possible
# else
root.textContent =
if status is 404
"Thread No.#{threadID} has not been found."
else
"Error #{req.status}: #{req.statusText}."
return
doc = d.implementation.createHTMLDocument ''
doc.documentElement.innerHTML = req.response
unless pc = doc.getElementById "pc#{postID}"
# post can be deleted by the time we check for it
# try archive if possible
# else
root.textContent = "Post No.#{postID} has not been found."
return
pc = Get.cleanPost d.importNode pc, true
for quote in $$ '.quotelink', pc
href = quote.getAttribute 'href'
continue if href[0] is '/' # Cross-board quote, or board link
quote.href = "/#{board}/res/#{href}" # Fix pathnames
link = $ '.postInfo > .postNum > a[title="Highlight this post"]', pc
link.href = "/#{board}/res/#{threadID}#p#{postID}"
link.nextSibling.href = "/#{board}/res/#{threadID}#q#{postID}"
$.replace root.firstChild, pc
cb() if cb
parseArchivedPost: (req, board, postID, root, cb) ->
# $.replace root.firstChild,
cleanPost: (root) ->
post = $ '.post', root
for child in Array::slice.call root.childNodes
$.rm child unless child is post
# Don't mess with other features
now = Date.now()
for el in $$ '[id]', root
el.id = "#{now}_#{el.id}"
# $.rmClass post, 'opContainer'
# $.rmClass post, 'replyContainer'
$.rmClass root, 'forwarded'
$.rmClass root, 'qphl' # op
$.rmClass post, 'highlight'
$.rmClass post, 'qphl' # reply
root.hidden = post.hidden = false
root
title: (thread) ->
op = $ '.op', thread
el = $ '.subject', op
unless el.textContent unless el.textContent
el = $ '.nameBlock', op el = $ 'blockquote', op
span = $.el 'span', innerHTML: el.innerHTML.replace /<br>/g, ' ' unless el.textContent
"/#{g.BOARD}/ - #{span.textContent.trim()}" el = $ '.nameBlock', op
span = $.el 'span', innerHTML: el.innerHTML.replace /<br>/g, ' '
"/#{g.BOARD}/ - #{span.textContent.trim()}"
TitlePost = TitlePost =
init: -> init: ->
d.title = GetTitle() d.title = Get.title()
QuoteBacklink = QuoteBacklink =
init: -> init: ->
@ -2357,39 +2425,33 @@ QuoteInline =
# Can't use this because Firefox a shit: # Can't use this because Firefox a shit:
# root = $.x 'ancestor::*[parent::blockquote]', q # root = $.x 'ancestor::*[parent::blockquote]', q
unless isBacklink = /\bbacklink\b/.test q.className unless isBacklink = /\bbacklink\b/.test q.className
root = q root = q
while root.parentNode.nodeName isnt 'BLOCKQUOTE' until root.parentNode.nodeName is 'BLOCKQUOTE'
root = root.parentNode root = root.parentNode
if el = $.id "p#{id}"
if /\bop\b/.test el.className
$.rmClass el.parentNode, 'qphl'
else
$.rmClass el, 'qphl'
clonePost = QuoteInline.clone id, el
if isBacklink
$.after q.parentNode, clonePost
if Conf['Forward Hiding']
$.addClass el.parentNode, 'forwarded'
# Will only unhide if there's no inlined backlinks of it anymore.
++el.dataset.forwarded or el.dataset.forwarded = 1
else
$.after root, clonePost
if (i = Unread.replies.indexOf el) isnt -1
Unread.replies.splice i, 1
Unread.update true
return
path = q.pathname.split '/'
el = if path[1] is g.BOARD then $.id "p#{id}" else false
inline = $.el 'div', inline = $.el 'div',
className: 'inline'
id: "i#{id}" id: "i#{id}"
textContent: "Loading #{id}..." className: if el then 'inline' else 'inline crosspost'
$.after root, inline $.after (if isBacklink then q.parentNode else root), inline
{pathname} = q Get.post path[1], path[3], id, inline
$.cache pathname, -> QuoteInline.parse @, pathname, id, inline
return unless el
# Will only unhide if there's no inlined backlinks of it anymore.
if isBacklink and Conf['Forward Hiding']
$.addClass el.parentNode, 'forwarded'
++el.dataset.forwarded or el.dataset.forwarded = 1
# Decrease the unread count if this post is in the array of unread reply.
if (i = Unread.replies.indexOf el) isnt -1
Unread.replies.splice i, 1
Unread.update true
rm: (q, id) -> rm: (q, id) ->
# select the corresponding inlined quote or loading quote # select the corresponding inlined quote or loading quote
div = $.x "following::div[@id='i_pc#{id}']", q div = $.x "following::div[@id='i#{id}']", q
$.rm div $.rm div
return unless Conf['Forward Hiding'] return unless Conf['Forward Hiding']
for inlined in $$ '.backlink.inlined', div for inlined in $$ '.backlink.inlined', div
@ -2399,40 +2461,6 @@ QuoteInline =
div = $.id "p#{id}" div = $.id "p#{id}"
$.rmClass div.parentNode, 'forwarded' unless --div.dataset.forwarded $.rmClass div.parentNode, 'forwarded' unless --div.dataset.forwarded
parse: (req, pathname, id, inline) ->
return unless inline.parentNode
if req.status isnt 200
inline.textContent = "#{req.status} #{req.statusText}"
return
doc = d.implementation.createHTMLDocument ''
doc.documentElement.innerHTML = req.response
node = doc.getElementById "p#{id}"
newInline = QuoteInline.clone id, node
for quote in $$ '.quotelink', newInline
href = quote.getAttribute 'href'
continue if href[0] is '/' # Cross-board quote
quote.href = "res/#{href}" # Fix pathnames
link = $ '.postInfo > .postNum > a:first-child', newInline
link.href = "#{pathname}#p#{id}"
link.nextSibling.href = "#{pathname}#q#{id}"
$.addClass newInline, 'crosspost'
$.replace inline, newInline
clone: (id, el) ->
clone = $.el 'div',
className: 'postContainer inline'
id: "i_pc#{id}"
post = el.cloneNode true
post.hidden = false
$.add clone, post
for node in $$ '[id]', clone
# Don't mess with other features
node.id = "i_#{node.id}"
clone
QuotePreview = QuotePreview =
init: -> init: ->
Main.callbacks.push @node Main.callbacks.push @node
@ -2455,70 +2483,53 @@ QuotePreview =
# Don't stop other elements from dragging # Don't stop other elements from dragging
return if UI.el return if UI.el
qp = UI.el = $.el 'div', path = @pathname.split '/'
id = @hash[2..]
qp = UI.el = $.el 'div',
id: 'qp' id: 'qp'
className: 'post reply dialog' className: 'reply dialog'
UI.hover e
$.add d.body, qp $.add d.body, qp
Get.post path[1], path[3], id, qp, ->
bq = $ 'blockquote', qp
Main.prettify bq
post =
el: qp
if fileInfo = $ '.fileInfo', qp
img = fileInfo.nextElementSibling.firstElementChild
if img.alt isnt 'File deleted.'
post.fileInfo = fileInfo
post.img = img
if Conf['Image Auto-Gif']
AutoGif.node post
if Conf['Time Formatting']
Time.node post
if Conf['File Info Formatting']
FileInfo.node post
id = @hash[2..] if path[1] is g.BOARD and el = $.id "p#{id}"
if el = $.id "p#{id}"
for klass in el.parentNode.className.split ' '
# preserve highlight classes
unless /^((op|reply|post)Container|forwarded)$/.test klass
$.addClass qp, klass
qp.innerHTML = el.innerHTML
if Conf['Quote Highlighting'] if Conf['Quote Highlighting']
if /\bop\b/.test el.className if /\bop\b/.test el.className
$.addClass el.parentNode, 'qphl' $.addClass el.parentNode, 'qphl'
else else
$.addClass el, 'qphl' $.addClass el, 'qphl'
replyID = $.x('ancestor::div[contains(@class,"postContainer")]', @).id.match(/\d+$/)[0] # can't use xpath because >firefox
parent = @parentNode
until parent.id
parent = parent.parentNode
quoterID = parent.id.match(/\d+$/)[0]
for quote in $$ '.quotelink, .backlink', qp for quote in $$ '.quotelink, .backlink', qp
if quote.hash[2..] is replyID if quote.hash[2..] is quoterID
$.addClass quote, 'forwardlink' $.addClass quote, 'forwardlink'
else
qp.textContent = "Loading #{id}..."
$.cache @pathname, -> QuotePreview.parse @, id
UI.hover e
$.on @, 'mousemove', UI.hover $.on @, 'mousemove', UI.hover
$.on @, 'mouseout click', QuotePreview.mouseout $.on @, 'mouseout click', QuotePreview.mouseout
mouseout: -> mouseout: (e) ->
UI.hoverend() UI.hoverend()
if el = $.id @hash[1..] if el = $.id @hash[1..]
if /\bop\b/.test el.className $.rmClass el, 'qphl' # reply
$.rmClass el.parentNode, 'qphl' $.rmClass el.parentNode, 'qphl' # op
else
$.rmClass el, 'qphl'
$.off @, 'mousemove', UI.hover $.off @, 'mousemove', UI.hover
$.off @, 'mouseout click', QuotePreview.mouseout $.off @, 'mouseout click', QuotePreview.mouseout
parse: (req, id) ->
return unless (qp = UI.el) and qp.textContent is "Loading #{id}..."
if req.status isnt 200
qp.textContent = "#{req.status} #{req.statusText}"
return
doc = d.implementation.createHTMLDocument ''
doc.documentElement.innerHTML = req.response
node = doc.getElementById "p#{id}"
qp.innerHTML = node.innerHTML
bq = $ 'blockquote', qp
bq.id += '_qp'
Main.prettify bq
post =
el: qp
if fileInfo = $ '.fileInfo', qp
img = fileInfo.nextElementSibling.firstElementChild
if img.alt isnt 'File deleted.'
post.fileInfo = fileInfo
post.img = img
if Conf['Image Auto-Gif']
AutoGif.node post
if Conf['Time Formatting']
Time.node post
if Conf['File Info Formatting']
FileInfo.node post
QuoteOP = QuoteOP =
init: -> init: ->
@ -2579,7 +2590,7 @@ Quotify =
m[1] m[1]
else else
# Get the post's board, whether it's inlined or not. # Get the post's board, whether it's inlined or not.
$('.postInfo > .postNum > a:first-child', post.el).pathname.split('/')[1] $('.postInfo > .postNum > a[title="Highlight this post"]', post.el).pathname.split('/')[1]
nodes.push a = $.el 'a', nodes.push a = $.el 'a',
# \u00A0 is nbsp # \u00A0 is nbsp
@ -2592,6 +2603,7 @@ Quotify =
else else
a.href = Redirect.thread board, id, 'post' a.href = Redirect.thread board, id, 'post'
a.className = 'deadlink' a.className = 'deadlink'
# a.className = if JSONable then 'quotelink deadlink' else 'deadlink'
a.target = '_blank' a.target = '_blank'
data = data[index + quote.length..] data = data[index + quote.length..]
@ -3197,16 +3209,16 @@ Main =
window.location = "https://raw.github.com/mayhemydg/4chan-x/#{version}/4chan_x.user.js" window.location = "https://raw.github.com/mayhemydg/4chan-x/#{version}/4chan_x.user.js"
preParse: (node) -> preParse: (node) ->
rootClass = node.className parentClass = node.parentNode.className
el = $ '.post', node el = $ '.post', node
post = post =
root: node root: node
el: el el: el
class: el.className class: el.className
id: el.id[1..] id: el.id.match(/\d+$/)[0]
threadId: g.THREAD_ID or $.x('ancestor::div[parent::div[@class="board"]]', node).id[1..] threadId: g.THREAD_ID or $.x('ancestor::div[parent::div[@class="board"]]', node).id.match(/\d+$/)[0]
isInlined: /\binline\b/.test rootClass isInlined: /\binline\b/.test parentClass
isCrosspost: /\bcrosspost\b/.test rootClass isCrosspost: /\bcrosspost\b/.test parentClass
blockquote: el.lastElementChild blockquote: el.lastElementChild
quotes: el.getElementsByClassName 'quotelink' quotes: el.getElementsByClassName 'quotelink'
backlinks: el.getElementsByClassName 'backlink' backlinks: el.getElementsByClassName 'backlink'
@ -3597,8 +3609,12 @@ textarea.field {
} }
#qp { #qp {
padding: 2px 2px 5px;
}
#qp .post {
border: none;
margin: 0; margin: 0;
padding: 1px 2px 5px; padding: 0;
} }
#qp img { #qp img {
max-height: 300px; max-height: 300px;
@ -3607,26 +3623,15 @@ textarea.field {
.qphl { .qphl {
outline: 2px solid rgba(216, 94, 49, .7); outline: 2px solid rgba(216, 94, 49, .7);
} }
.qphl.opContainer {
outline-offset: -2px;
}
div.opContainer {
display: block !important;
}
.inlined { .inlined {
opacity: .5; opacity: .5;
} }
.inline { .inline .post {
background-color: rgba(255, 255, 255, 0.15); background-color: rgba(255, 255, 255, 0.15);
border: 1px solid rgba(128, 128, 128, 0.5); border: 1px solid rgba(128, 128, 128, 0.5);
display: table; display: table;
margin: 2px; margin: 2px;
} padding: 2px;
.inline .post {
background: none;
border: none;
margin: 0;
padding: 0;
} }
.opContainer.filter_highlight { .opContainer.filter_highlight {
box-shadow: inset 5px 0 rgba(255,0,0,0.5); box-shadow: inset 5px 0 rgba(255,0,0,0.5);