Add Quote Inlining. Close #501.

This commit is contained in:
Nicolas Stepien 2012-09-03 21:28:14 +02:00
parent 320cb1269c
commit 94b9ff9c36
2 changed files with 429 additions and 47 deletions

View File

@ -73,7 +73,7 @@
*/
(function() {
var $, $$, Board, Clone, Conf, Config, Main, Post, QuoteBacklink, Quotify, Thread, Time, UI, d, g,
var $, $$, Board, Clone, Conf, Config, Get, Main, Post, QuoteBacklink, QuoteInline, 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; };
@ -628,16 +628,10 @@
return this.ID;
};
function Thread(root, board) {
var postInfo;
this.root = root;
function Thread(ID, board) {
this.board = board;
this.ID = +root.id.slice(1);
this.hr = root.nextElementSibling;
this.ID = +ID;
this.posts = {};
postInfo = $('.postInfo', root.firstElementChild);
this.isClosed = !!$('img[title=Closed]', postInfo);
this.isSticky = !!$('img[title=Sticky]', postInfo);
g.threads["" + board + "." + this] = board.threads[this] = this;
}
@ -665,7 +659,8 @@
post: post,
info: info,
comment: $('.postMessage', post),
quotelinks: []
quotelinks: [],
backlinks: info.getElementsByClassName('backlink')
};
this.info = {};
if (subject = $('.subject', info)) {
@ -753,12 +748,11 @@
};
Post.prototype.rmClone = function(index) {
var clone, i, _i, _ref;
clone = this.clones.splice(index, 1);
var i, _i, _ref;
this.clones.splice(index, 1);
for (i = _i = index, _ref = this.clones.length; index <= _ref ? _i < _ref : _i > _ref; i = index <= _ref ? ++_i : --_i) {
this.clones[i].nodes.root.setAttribute('data-clone', i);
}
return $.rm(clone.nodes.root);
};
return Post;
@ -770,7 +764,7 @@
__extends(Clone, _super);
function Clone(origin) {
var file, index, info, key, nodes, post, quotelink, root, val, _i, _j, _len, _len1, _ref, _ref1, _ref2;
var file, index, info, inline, inlined, key, nodes, post, quotelink, root, val, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3, _ref4;
this.origin = origin;
_ref = ['ID', 'board', 'thread', 'info', 'quotes', 'isReply'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@ -786,8 +780,20 @@
post: post,
info: info,
comment: $('.postMessage', post),
quotelinks: []
quotelinks: [],
backlinks: info.getElementsByClassName('backlink')
};
_ref1 = $$('.inline', post);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
inline = _ref1[_j];
$.rm(inline);
}
_ref2 = $$('.inlined', post);
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
inlined = _ref2[_k];
$.rmClass(inlined, 'inlined');
}
$.rmClass(root, 'forwarded');
if (nodes.subject) {
this.nodes.subject = $('.subject', info);
}
@ -812,18 +818,18 @@
if (nodes.date) {
this.nodes.date = $('.dateTime', info);
}
_ref1 = $$('.quotelink', this.nodes.comment);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
quotelink = _ref1[_j];
_ref3 = $$('.quotelink', this.nodes.comment);
for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
quotelink = _ref3[_l];
if (quotelink.hash || $.hasClass(quotelink, 'deadlink')) {
this.nodes.quotelinks.push(quotelink);
}
}
if (origin.file) {
this.file = {};
_ref2 = origin.file;
for (key in _ref2) {
val = _ref2[key];
_ref4 = origin.file;
for (key in _ref4) {
val = _ref4[key];
this.file[key] = val;
}
file = $('.file', post);
@ -832,7 +838,7 @@
this.file.thumb = $('img[data-md5]', file);
}
this.isClone = true;
index = origin.clones.push(this);
index = origin.clones.push(this) - 1;
root.setAttribute('data-clone', index);
}
@ -920,6 +926,13 @@
$.log(err, 'Resurrect Quotes');
}
}
if (Conf['Quote Inline']) {
try {
QuoteInline.init();
} catch (err) {
$.log(err, 'Quote Inline');
}
}
if (Conf['Quote Backlinks']) {
try {
QuoteBacklink.init();
@ -949,7 +962,7 @@
if (!$.hasClass(boardChild, 'thread')) {
continue;
}
thread = new Thread(boardChild, g.BOARD);
thread = new Thread(boardChild.id.slice(1), g.BOARD);
threads.push(thread);
_ref1 = boardChild.children;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
@ -993,7 +1006,75 @@
return $.on(d, 'DOMNodeInserted', Main.addStyle);
}
},
css: "/* general */\n.move {\n cursor: move;\n}\nlabel {\n cursor: pointer;\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}"
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}"
};
Get = {
post: 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]);
return;
}
root.textContent = "Loading post No." + postID + "...";
if (threadID) {
return $.cache("/" + board + "/res/" + threadID, function() {
return Get.parsePost(this, board, threadID, postID, root);
});
}
},
cleanRoot: function(clone) {
var child, post, root, _i, _len, _ref, _ref1;
_ref = clone.nodes, root = _ref.root, post = _ref.post;
_ref1 = Array.prototype.slice.call(root.childNodes);
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
child = _ref1[_i];
if (child !== post) {
$.rm(child);
}
}
return root;
},
parsePost: function(req, board, threadID, postID, root) {
var clone, doc, href, inBoard, inThread, link, pc, post, quote, status, _i, _len, _ref;
status = req.status;
if (status !== 200) {
$.addClass(root, 'warning');
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 = 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 = $('a[title="Highlight this post"]', pc);
link.href = "/" + board + "/res/" + threadID + "#p" + postID;
link.nextSibling.href = "/" + board + "/res/" + threadID + "#q" + postID;
inBoard = g.boards[board] || new Board(board);
inThread = g.threads["" + board + "." + threadID] || new Thread(threadID, inBoard);
post = new Post(pc, inThread, inBoard);
Main.callbackNodes(Post, [post]);
if (!root.parentNode) {
return;
}
clone = post.addClone();
$.replace(root.firstChild, Get.cleanRoot(clone));
return Main.callbackNodes(Post, [clone]);
}
};
Quotify = {
@ -1044,6 +1125,100 @@
}
};
QuoteInline = {
init: function() {
return Post.prototype.callbacks.push({
name: 'Quote Inline',
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, 'click', QuoteInline.toggle);
}
_ref1 = this.nodes.backlinks;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
link = _ref1[_j];
$.on(link, 'click', QuoteInline.toggle);
}
},
toggle: function(e) {
var board, path, postID, threadID;
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);
if ($.hasClass(this, 'inlined')) {
QuoteInline.rm(this, board, threadID, postID);
} else {
if ($.x("ancestor::div[@id='p" + postID + "']", this)) {
return;
}
QuoteInline.add(this, board, threadID, postID);
}
return this.classList.toggle('inlined');
},
add: function(quotelink, board, threadID, postID) {
var inline, isBacklink, post, root;
inline = $.el('div', {
id: "i" + postID,
className: 'inline'
});
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);
if (!(board === g.BOARD.ID && $.x("ancestor::div[@id='t" + threadID + "']", quotelink))) {
return;
}
post = g.posts["" + board + "." + postID];
if (isBacklink && Conf['Forward Hiding']) {
$.addClass(post.nodes.root, 'forwarded');
return post.forwarded++ || (post.forwarded = 1);
}
},
rm: function(quotelink, board, threadID, postID) {
var el, inThreadID, index, inline, inlines, path, post, root, _i, _len;
root = $.x("following::div[@id='i" + postID + "'][1]", quotelink);
$.rm(root);
if (!(el = root.firstElementChild)) {
return;
}
post = g.posts["" + board + "." + postID];
post.rmClone(el.dataset.clone);
inThreadID = $.x('ancestor::div[@class="thread"]', quotelink).id.slice(1);
if (Conf['Forward Hiding'] && board === g.BOARD.ID && threadID === inThreadID && $.hasClass(quotelink, 'backlink')) {
if (!--post.forwarded) {
delete post.forwarded;
$.rmClass(post.nodes.root, 'forwarded');
}
}
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;
post = g.posts["" + board + "." + postID];
post.rmClone(index);
if (Conf['Forward Hiding'] && board === g.BOARD.ID && threadID === inThreadID && $.hasClass(inline, 'backlink')) {
if (!--post.forwarded) {
delete post.forwarded;
$.rmClass(post.nodes.root, 'forwarded');
}
}
}
}
};
QuoteBacklink = {
init: function() {
var format;
@ -1083,6 +1258,9 @@
for (_k = 0, _len2 = containers.length; _k < _len2; _k++) {
container = containers[_k];
link = a.cloneNode(true);
if (Conf['Quote Inline']) {
$.on(link, 'click', QuoteInline.toggle);
}
$.add(container, [$.tn(' '), link]);
}
}
@ -1090,9 +1268,7 @@
secondNode: function() {
var container;
if (this.isClone && this.origin.nodes.backlinkContainer) {
container = $('.container', this.nodes.info);
this.nodes.backlinkContainer = container;
this.nodes.backlinks = container.getElementsByClassName('backlinks');
this.nodes.backlinkContainer = $('.container', this.nodes.info);
return;
}
if (!(Conf['OP Backlinks'] || this.isReply)) {
@ -1100,7 +1276,6 @@
}
container = QuoteBacklink.getContainer("" + this.board + "." + this);
this.nodes.backlinkContainer = container;
this.nodes.backlinks = container.getElementsByClassName('backlinks');
return $.add(this.nodes.info, container);
},
getContainer: function(id) {

View File

@ -486,14 +486,15 @@ class Thread
callbacks: []
toString: -> @ID
constructor: (@root, @board) ->
@ID = +root.id[1..]
@hr = root.nextElementSibling
constructor: (ID, @board) ->
@ID = +ID
@posts = {}
postInfo = $ '.postInfo', root.firstElementChild
@isClosed = !!$ 'img[title=Closed]', postInfo
@isSticky = !!$ 'img[title=Sticky]', postInfo
# XXX Can't check when parsing single posts
# move to Post constructor? unless @isReply
# postInfo = $ '.postInfo', root.firstElementChild
# @isClosed = !!$ 'img[title=Closed]', postInfo
# @isSticky = !!$ 'img[title=Sticky]', postInfo
g.threads["#{board}.#{@}"] = board.threads[@] = @
@ -512,6 +513,7 @@ class Post
info: info
comment: $ '.postMessage', post
quotelinks: []
backlinks: info.getElementsByClassName 'backlink'
@info = {}
if subject = $ '.subject', info
@ -595,10 +597,10 @@ class Post
addClone: ->
new Clone @
rmClone: (index) ->
clone = @clones.splice index, 1
@clones.splice index, 1
for i in [index...@clones.length]
@clones[i].nodes.root.setAttribute 'data-clone', i
$.rm clone.nodes.root
return
class Clone extends Post
constructor: (@origin) ->
@ -616,6 +618,17 @@ class Clone extends Post
info: info
comment: $ '.postMessage', post
quotelinks: []
backlinks: info.getElementsByClassName 'backlink'
# Remove inlined posts inside of this post.
for inline in $$ '.inline', post
$.rm inline
for inlined in $$ '.inlined', post
$.rmClass inlined, 'inlined'
# root.hidden = false # post hiding
$.rmClass root, 'forwarded' # quote inlining
# $.rmClass post, 'highlight' # keybind navigation
if nodes.subject
@nodes.subject = $ '.subject', info
@ -651,7 +664,7 @@ class Clone extends Post
@file.thumb = $ 'img[data-md5]', file
@isClone = true
index = origin.clones.push @
index = origin.clones.push(@) - 1
root.setAttribute 'data-clone', index
@ -726,6 +739,13 @@ Main =
# XXX handle error
$.log err, 'Resurrect Quotes'
if Conf['Quote Inline']
try
QuoteInline.init()
catch err
# XXX handle error
$.log err, 'Quote Inline'
if Conf['Quote Backlinks']
try
QuoteBacklink.init()
@ -749,7 +769,7 @@ Main =
for boardChild in $('.board').children
continue unless $.hasClass boardChild, 'thread'
thread = new Thread boardChild, g.BOARD
thread = new Thread boardChild.id[1..], g.BOARD
threads.push thread
for threadChild in boardChild.children
continue unless $.hasClass threadChild, 'postContainer'
@ -793,6 +813,9 @@ Main =
label {
cursor: pointer;
}
.warning {
color: red;
}
/* 4chan style fixes */
.opContainer, .op {
@ -832,10 +855,101 @@ body.fourchan_x {
#settings {
float: right;
}
/* quotes */
.inlined {
opacity: .5;
}
.forwarded {
display: none;
}
.inline {
border: 1px solid rgba(128, 128, 128, 0.5);
display: table;
margin: 2px 0;
}
.inline .post {
display: table !important;
margin: 0 !important;
padding: 1px 2px !important;
border: 0 !important;
}
"""
Get =
post: (board, threadID, postID, root) ->
if origin = g.posts["#{board}.#{postID}"]
clone = origin.addClone()
$.add root, Get.cleanRoot clone
Main.callbackNodes Post, [clone]
return
root.textContent = "Loading post No.#{postID}..."
if threadID
$.cache "/#{board}/res/#{threadID}", ->
Get.parsePost @, board, threadID, postID, root
# else if url = Redirect.post board, postID
# $.cache url, ->
# Get.parseArchivedPost @, board, postID, root
cleanRoot: (clone) ->
{root, post} = clone.nodes
for child in Array::slice.call root.childNodes
$.rm child unless child is post
root
parsePost: (req, board, threadID, postID, root) ->
{status} = req
if status isnt 200
# The thread can die by the time we check a quote.
# XXX
# if url = Redirect.post board, postID
# $.cache url, ->
# Get.parseArchivedPost @, board, postID, root
# else
$.addClass root, 'warning'
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}"
# The post can be deleted by the time we check a quote.
# XXX
# if url = Redirect.post board, postID
# $.cache url, ->
# Get.parseArchivedPost @, board, postID, root
# else
root.textContent = "Post No.#{postID} has not been found."
return
pc = 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 = $ 'a[title="Highlight this post"]', pc
link.href = "/#{board}/res/#{threadID}#p#{postID}"
link.nextSibling.href = "/#{board}/res/#{threadID}#q#{postID}"
inBoard = g.boards[board] or
new Board board
inThread = g.threads["#{board}.#{threadID}"] or
new Thread threadID, inBoard
post = new Post pc, inThread, inBoard
Main.callbackNodes Post, [post]
# 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]
Quotify =
init: ->
Post::callbacks.push
@ -884,7 +998,6 @@ Quotify =
# if board is g.BOARD and $.id "p#{ID}"
# a.href = "#p#{ID}"
# a.className = 'quotelink'
# a.setAttribute 'onclick', "replyhl('#{ID}');"
# else
# a.href = Redirect.thread board, 0, ID
# a.className = 'deadlink'
@ -905,6 +1018,105 @@ Quotify =
$.replace node, nodes
return
QuoteInline =
init: ->
Post::callbacks.push
name: 'Quote Inline'
cb: @node
node: ->
for link in @nodes.quotelinks
$.on link, 'click', QuoteInline.toggle
for link in @nodes.backlinks
$.on link, 'click', QuoteInline.toggle
return
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..]
if $.hasClass @, 'inlined'
QuoteInline.rm @, board, threadID, postID
else
return if $.x "ancestor::div[@id='p#{postID}']", @
QuoteInline.add @, board, threadID, postID
@classList.toggle 'inlined'
add: (quotelink, board, threadID, postID) ->
inline = $.el 'div',
id: "i#{postID}"
className: 'inline'
root =
if isBacklink = $.hasClass quotelink, 'backlink'
quotelink.parentNode.parentNode
else
$.x 'ancestor-or-self::*[parent::blockquote][1]', quotelink
$.after root, inline
Get.post board, threadID, postID, inline
return unless board is g.BOARD.ID and $.x "ancestor::div[@id='t#{threadID}']", quotelink
post = g.posts["#{board}.#{postID}"]
# Hide forward post if it's a backlink of a post in this thread.
# Will only unhide if there's no inlined backlinks of it anymore.
if isBacklink and Conf['Forward Hiding']
$.addClass post.nodes.root, 'forwarded'
post.forwarded++ or post.forwarded = 1
# Decrease the unread count if this post is in the array of unread reply.
# XXX
# if (i = Unread.replies.indexOf el) isnt -1
# Unread.replies.splice i, 1
# Unread.update true
rm: (quotelink, board, threadID, postID) ->
# Select the corresponding inlined quote, and remove it.
root = $.x "following::div[@id='i#{postID}'][1]", quotelink
$.rm root
# Stop if it's still loading.
return unless el = root.firstElementChild
# Dereference clone.
post = g.posts["#{board}.#{postID}"]
post.rmClone el.dataset.clone
inThreadID = $.x('ancestor::div[@class="thread"]', quotelink).id[1..]
# Decrease forward count and unhide.
if Conf['Forward Hiding'] and
board is g.BOARD.ID and
threadID is inThreadID and
$.hasClass quotelink, 'backlink'
unless --post.forwarded
delete post.forwarded
$.rmClass post.nodes.root, 'forwarded'
# 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
if Conf['Forward Hiding'] and
board is g.BOARD.ID and
threadID is inThreadID and
$.hasClass inline, 'backlink'
unless --post.forwarded
delete post.forwarded
$.rmClass post.nodes.root, 'forwarded'
return
QuoteBacklink =
# Backlinks appending need to work for:
# - previous, same, and following posts.
@ -944,23 +1156,18 @@ QuoteBacklink =
# XXX
# if Conf['Quote Preview']
# $.on link, 'mouseover', QuotePreview.mouseover
# if Conf['Quote Inline']
# $.on link, 'click', QuoteInline.toggle
# else
# link.setAttribute 'onclick', "replyhl('#{post.ID}');"
if Conf['Quote Inline']
$.on link, 'click', QuoteInline.toggle
$.add container, [$.tn(' '), link]
return
secondNode: ->
if @isClone and @origin.nodes.backlinkContainer
container = $ '.container', @nodes.info
@nodes.backlinkContainer = container
@nodes.backlinks = container.getElementsByClassName 'backlinks'
@nodes.backlinkContainer = $ '.container', @nodes.info
return
# Don't backlink the OP.
return unless Conf['OP Backlinks'] or @isReply
container = QuoteBacklink.getContainer "#{@board}.#{@}"
@nodes.backlinkContainer = container
@nodes.backlinks = container.getElementsByClassName 'backlinks'
$.add @nodes.info, container
getContainer: (id) ->
@containers[id] or=