Add Post cloning. Close #315.

This commit is contained in:
Nicolas Stepien 2012-09-02 00:47:12 +02:00
parent d96c7c92ae
commit 012d3ea332
2 changed files with 246 additions and 58 deletions

View File

@ -73,7 +73,9 @@
*/
(function() {
var $, $$, Board, Conf, Config, Main, Post, QuoteBacklink, Quotify, Thread, Time, UI, d, g;
var $, $$, Board, Clone, Conf, Config, Main, Post, QuoteBacklink, 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; };
Config = {
main: {
@ -657,45 +659,47 @@
this.board = board;
this.ID = +root.id.slice(2);
post = $('.post', root);
info = $('.postInfo', post);
this.nodes = {
root: root,
post: post,
info: $('.postInfo', post),
info: info,
comment: $('.postMessage', post),
quotelinks: []
quotelinks: [],
backlinks: info.getElementsByClassName('backlink')
};
info = this.nodes.info;
this.info = {};
if (subject = $('.subject', info)) {
this.nodes.subject = subject;
this.subject = subject.textContent;
this.info.subject = subject.textContent;
}
if (name = $('.name', info)) {
this.nodes.name = name;
this.name = name.textContent;
this.info.name = name.textContent;
}
if (email = $('.useremail', info)) {
this.nodes.email = email;
this.email = decodeURIComponent(email.href.slice(7));
this.info.email = decodeURIComponent(email.href.slice(7));
}
if (tripcode = $('.postertrip', info)) {
this.nodes.tripcode = tripcode;
this.tripcode = tripcode.textContent;
this.info.tripcode = tripcode.textContent;
}
if (uniqueID = $('.posteruid', info)) {
this.nodes.uniqueID = uniqueID;
this.uniqueID = uniqueID.textContent;
this.info.uniqueID = uniqueID.textContent;
}
if (capcode = $('.capcode', info)) {
this.nodes.capcode = capcode;
this.capcode = capcode.textContent;
this.info.capcode = capcode.textContent;
}
if (flag = $('.countryFlag', info)) {
this.nodes.flag = flag;
this.flag = flag.title;
this.info.flag = flag.title;
}
if (date = $('.dateTime', info)) {
this.nodes.date = date;
this.date = new Date(date.dataset.utc * 1000);
this.info.date = new Date(date.dataset.utc * 1000);
}
bq = this.nodes.comment.cloneNode(true);
_ref = $$('.abbr, .capcodeReplies, .exif, b', bq);
@ -708,7 +712,7 @@
for (i = _j = 0, _ref1 = nodes.snapshotLength; 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
text.push((data = nodes.snapshotItem(i).data) ? data : '\n');
}
this.comment = text.join('').replace(/^\n+|\n+$| +(?=\n|$)/g, '');
this.info.comment = text.join('').replace(/^\n+|\n+$| +(?=\n|$)/g, '');
quotes = {};
_ref2 = $$('.quotelink', this.nodes.comment);
for (_k = 0, _len1 = _ref2.length; _k < _len1; _k++) {
@ -741,13 +745,105 @@
}
}
this.isReply = $.hasClass(post, 'reply');
this.clones = [];
g.posts["" + board + "." + this] = thread.posts[this] = board.posts[this] = this;
}
Post.prototype.addClone = function() {
return new Clone(this);
};
Post.prototype.rmClone = function(index) {
var clone, i, _i, _ref;
clone = 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;
})();
Clone = (function(_super) {
__extends(Clone, _super);
function Clone(origin) {
var file, index, info, key, nodes, post, quotelink, root, val, _i, _j, _len, _len1, _ref, _ref1, _ref2;
_ref = ['ID', 'board', 'thread', 'info', 'quotes', 'isReply'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
key = _ref[_i];
this[key] = origin[key];
}
nodes = origin.nodes;
root = nodes.root.cloneNode(true);
post = $('.post', root);
info = $('.postInfo', post);
this.nodes = {
root: root,
post: post,
info: info,
comment: $('.postMessage', post),
quotelinks: [],
backlinks: info.getElementsByClassName('backlinks')
};
if (nodes.subject) {
this.nodes.subject = $('.subject', info);
}
if (nodes.name) {
this.nodes.name = $('.name', info);
}
if (nodes.email) {
this.nodes.email = $('.useremail', info);
}
if (nodes.tripcode) {
this.nodes.tripcode = $('.postertrip', info);
}
if (nodes.uniqueID) {
this.nodes.uniqueID = $('.posteruid', info);
}
if (nodes.capcode) {
this.nodes.capcode = $('.capcode', info);
}
if (nodes.flag) {
this.nodes.flag = $('.countryFlag', info);
}
if (nodes.date) {
this.nodes.date = $('.dateTime', info);
}
if (nodes.backlinkContainer) {
this.nodes.backlinkContainer = $('.container', info);
}
_ref1 = $$('.quotelink', this.nodes.comment);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
quotelink = _ref1[_j];
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];
this.file[key] = val;
}
file = $('.file', post);
this.file.info = $('.fileInfo', file);
this.file.text = $('.fileText', file);
this.file.thumb = $('img[data-md5]', file);
}
this.isClone = true;
index = origin.clones.push(this);
root.setAttribute('data-clone', index);
}
return Clone;
})(Post);
Main = {
init: function() {
var flatten, key, pathname, val;
@ -913,6 +1009,9 @@
},
node: function() {
var ID, a, board, data, i, index, m, node, nodes, quote, quoteID, quotes, snapshot, text, _i, _j, _len, _ref;
if (this.isClone) {
return;
}
snapshot = d.evaluate('.//text()[not(parent::a)]', this.nodes.comment, null, 6, null);
for (i = _i = 0, _ref = snapshot.snapshotLength; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
node = snapshot.snapshotItem(i);
@ -934,7 +1033,8 @@
this.quotes.push(quoteID);
}
a = $.el('a', {
textContent: quote
textContent: quote,
className: 'quotelink deadlink'
});
this.nodes.quotelinks.push(a);
nodes.push(a);
@ -964,8 +1064,8 @@
});
},
firstNode: function() {
var a, link, quote, _i, _len, _ref;
if (!this.quotes.length) {
var a, clone, container, containers, link, post, quote, _i, _j, _k, _len, _len1, _len2, _ref, _ref1;
if (this.isClone || !this.quotes.length) {
return;
}
a = $.el('a', {
@ -976,15 +1076,29 @@
_ref = this.quotes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quote = _ref[_i];
link = a.cloneNode(true);
$.add(QuoteBacklink.getContainer(quote), [$.tn(' '), link]);
containers = [QuoteBacklink.getContainer(quote)];
if (post = g.posts[quote]) {
_ref1 = post.clones;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
clone = _ref1[_j];
containers.push(clone.nodes.backlinkContainer);
}
}
for (_k = 0, _len2 = containers.length; _k < _len2; _k++) {
container = containers[_k];
link = a.cloneNode(true);
$.add(container, [$.tn(' '), link]);
}
}
},
secondNode: function() {
if (!(Conf['OP Backlinks'] || this.isReply)) {
var container;
if (this.isClone || !(Conf['OP Backlinks'] || this.isReply)) {
return;
}
return $.add(this.nodes.info, QuoteBacklink.getContainer("" + this.board + "." + this));
container = QuoteBacklink.getContainer("" + this.board + "." + this);
this.nodes.backlinkContainer = container;
return $.add(this.nodes.info, container);
},
getContainer: function(id) {
var _base;
@ -1003,7 +1117,10 @@
});
},
node: function() {
return this.nodes.date.textContent = Time.funk(Time, this.date);
if (this.isClone) {
return;
}
return this.nodes.date.textContent = Time.funk(Time, this.info.date);
},
createFunc: function() {
var code;

View File

@ -324,7 +324,7 @@ $.extend $,
$.add d.head, style
style
x: (path, root=d.body) ->
# XPathResult.ANY_UNORDERED_NODE_TYPE is 8
# XPathResult.ANY_UNORDERED_NODE_TYPE === 8
d.evaluate(path, root, null, 8, null).singleNodeValue
addClass: (el, className) ->
el.classList.add className
@ -504,39 +504,41 @@ class Post
constructor: (root, @thread, @board) ->
@ID = +root.id[2..]
post = $ '.post', root
post = $ '.post', root
info = $ '.postInfo', post
@nodes =
root: root
post: post
info: $ '.postInfo', post
info: info
comment: $ '.postMessage', post
quotelinks: []
backlinks: info.getElementsByClassName 'backlink'
info = @nodes.info
@info = {}
if subject = $ '.subject', info
@nodes.subject = subject
@subject = subject.textContent
@info.subject = subject.textContent
if name = $ '.name', info
@nodes.name = name
@name = name.textContent
@info.name = name.textContent
if email = $ '.useremail', info
@nodes.email = email
@email = decodeURIComponent email.href[7..]
@info.email = decodeURIComponent email.href[7..]
if tripcode = $ '.postertrip', info
@nodes.tripcode = tripcode
@tripcode = tripcode.textContent
@info.tripcode = tripcode.textContent
if uniqueID = $ '.posteruid', info
@nodes.uniqueID = uniqueID
@uniqueID = uniqueID.textContent
@info.uniqueID = uniqueID.textContent
if capcode = $ '.capcode', info
@nodes.capcode = capcode
@capcode = capcode.textContent
@info.capcode = capcode.textContent
if flag = $ '.countryFlag', info
@nodes.flag = flag
@flag = flag.title
@info.flag = flag.title
if date = $ '.dateTime', info
@nodes.date = date
@date = new Date date.dataset.utc * 1000
@info.date = new Date date.dataset.utc * 1000
# Get the comment's text.
# <br> -> \n
@ -555,16 +557,16 @@ class Post
nodes = d.evaluate './/br|.//text()', bq, null, 7, null
for i in [0...nodes.snapshotLength]
text.push if data = nodes.snapshotItem(i).data then data else '\n'
@comment = text.join('').replace /^\n+|\n+$| +(?=\n|$)/g, ''
@info.comment = text.join('').replace /^\n+|\n+$| +(?=\n|$)/g, ''
quotes = {}
for quotelink in $$ '.quotelink', @nodes.comment
# Don't add board links. (>>>/b/)
# Don't add text-board quotelinks. (>>>/img/1234)
# Don't count capcode replies as quote. (Admin/Mod/Dev Replies: ...)
# Don't count capcode replies as quotes. (Admin/Mod/Dev Replies: ...)
# Only add quotes that link to posts on an imageboard.
if quotelink.hash
@.nodes.quotelinks.push quotelink
@nodes.quotelinks.push quotelink
continue if quotelink.parentNode.parentNode.className is 'capcodeReplies'
quotes["#{quotelink.pathname.split('/')[1]}.#{quotelink.hash[2..]}"] = true
@quotes = Object.keys quotes
@ -588,9 +590,74 @@ class Post
@file.dimensions = @file.text.textContent.match(/\d+x\d+/)[0]
@isReply = $.hasClass post, 'reply'
@clones = []
g.posts["#{board}.#{@}"] = thread.posts[@] = board.posts[@] = @
addClone: ->
new Clone @
rmClone: (index) ->
clone = @clones.splice index, 1
for i in [index...@clones.length]
@clones[i].nodes.root.setAttribute 'data-clone', i
$.rm clone.nodes.root
class Clone extends Post
constructor: (origin) ->
for key in ['ID', 'board', 'thread', 'info', 'quotes', 'isReply']
# Copy or point to the origin's key value.
@[key] = origin[key]
{nodes} = origin
root = nodes.root.cloneNode true
post = $ '.post', root
info = $ '.postInfo', post
@nodes =
root: root
post: post
info: info
comment: $ '.postMessage', post
quotelinks: []
backlinks: info.getElementsByClassName 'backlinks'
if nodes.subject
@nodes.subject = $ '.subject', info
if nodes.name
@nodes.name = $ '.name', info
if nodes.email
@nodes.email = $ '.useremail', info
if nodes.tripcode
@nodes.tripcode = $ '.postertrip', info
if nodes.uniqueID
@nodes.uniqueID = $ '.posteruid', info
if nodes.capcode
@nodes.capcode = $ '.capcode', info
if nodes.flag
@nodes.flag = $ '.countryFlag', info
if nodes.date
@nodes.date = $ '.dateTime', info
if nodes.backlinkContainer
@nodes.backlinkContainer = $ '.container', info
for quotelink in $$ '.quotelink', @nodes.comment
# See comments in Post's constructor.
if quotelink.hash or $.hasClass quotelink, 'deadlink'
@nodes.quotelinks.push quotelink
if origin.file
# Copy values, point to relevant elements.
# See comments in Post's constructor.
@file = {}
for key, val of origin.file
@file[key] = val
file = $ '.file', post
@file.info = $ '.fileInfo', file
@file.text = $ '.fileText', file
@file.thumb = $ 'img[data-md5]', file
@isClone = true
index = origin.clones.push @
root.setAttribute 'data-clone', index
Main =
init: ->
@ -779,9 +846,9 @@ Quotify =
name: 'Resurrect Quotes'
cb: @node
node: ->
# XXX return if post.isInlined and not post.isCrosspost
return if @isClone
# XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE is 6
# XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE === 6
# Get all the text nodes that are not inside an anchor.
snapshot = d.evaluate './/text()[not(parent::a)]', @nodes.comment, null, 6, null
@ -815,6 +882,8 @@ Quotify =
# \u00A0 is nbsp
# textContent: "#{quote}\u00A0(Dead)"
textContent: quote
# XXX
className: 'quotelink deadlink'
# if board is g.BOARD and $.id "p#{ID}"
# a.href = "#p#{ID}"
@ -826,10 +895,6 @@ Quotify =
# a.target = '_blank'
# if Redirect.post board, ID
# $.addClass a, 'quotelink'
# # XXX https://github.com/greasemonkey/greasemonkey/issues/1571
# # GM can't into dataset
# # a.dataset.board = board
# # a.dataset.id = ID
# a.setAttribute 'data-board', board
# a.setAttribute 'data-id', ID
@ -868,28 +933,34 @@ QuoteBacklink =
name: 'Quote Backlinking Part 2'
cb: @secondNode
firstNode: ->
# XXX return if post.isInlined
return unless @quotes.length
return if @isClone or !@quotes.length
a = $.el 'a',
href: "/#{@board}/res/#{@thread}#p#{@}"
# XXX className: if post.el.hidden then 'filtered backlink' else 'backlink'
className: 'backlink'
textContent: QuoteBacklink.funk @ID
for quote in @quotes
link = a.cloneNode true
# 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}');"
$.add QuoteBacklink.getContainer(quote), [$.tn(' '), link]
containers = [QuoteBacklink.getContainer quote]
if post = g.posts[quote]
for clone in post.clones
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 Inline']
# $.on link, 'click', QuoteInline.toggle
# else
# link.setAttribute 'onclick', "replyhl('#{post.ID}');"
$.add container, [$.tn(' '), link]
return
secondNode: ->
# Don't backlink the OP.
return unless Conf['OP Backlinks'] or @isReply
$.add @nodes.info, QuoteBacklink.getContainer "#{@board}.#{@}"
return if @isClone or !(Conf['OP Backlinks'] or @isReply)
container = QuoteBacklink.getContainer "#{@board}.#{@}"
@nodes.backlinkContainer = container
$.add @nodes.info, container
getContainer: (id) ->
@containers[id] or=
$.el 'span', className: 'container'
@ -901,8 +972,8 @@ Time =
name: 'Time Formatting'
cb: @node
node: ->
# XXX return if @isInlined and not @isCrosspost
@nodes.date.textContent = Time.funk Time, @date
return if @isClone
@nodes.date.textContent = Time.funk Time, @info.date
createFunc: ->
code = Conf['time'].replace /%([A-Za-z])/g, (s, c) ->
if c of Time.formatters