From dccd4bf9ba34e49d7843f93e7bff45a5300e6637 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 5 Sep 2012 03:23:17 +0200 Subject: [PATCH] Finish Quote Resurrecting. Add fetching posts from archives. Add post building. --- 4chan_x.user.js | 315 ++++++++++++++++++++++++++++++++++++++++---- Cakefile | 1 + script.coffee | 341 +++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 567 insertions(+), 90 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 09984f8c3..ae20c6d3c 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -8,6 +8,7 @@ // @match *://boards.4chan.org/* // @match *://images.4chan.org/* // @match *://sys.4chan.org/* +// @match *://*.foolz.us/api/* // @run-at document-start // @updateURL https://github.com/MayhemYDG/4chan-x/raw/stable/4chan_x.user.js // @downloadURL https://github.com/MayhemYDG/4chan-x/raw/stable/4chan_x.user.js @@ -73,7 +74,7 @@ */ (function() { - var $, $$, Board, Clone, Conf, Config, Get, Main, Post, QuoteBacklink, QuoteInline, QuotePreview, Quotify, Redirect, Thread, Time, UI, d, g, + var $, $$, Board, Build, Clone, Conf, Config, Get, Main, Post, QuoteBacklink, QuoteInline, QuotePreview, Quotify, Redirect, 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; }; @@ -529,15 +530,6 @@ p.setAttribute('onclick', 'return window'); return p.onclick(); })(), - shortenFilename: function(filename, isOP) { - var threshold; - threshold = isOP ? 40 : 30; - if (filename.length - 4 > threshold) { - return "" + filename.slice(0, threshold - 5) + "(...)." + (filename.match(/\w+$/)); - } else { - return filename; - } - }, bytesToString: function(size) { var unit; unit = 0; @@ -647,10 +639,13 @@ return this.ID; }; - function Post(root, thread, board) { - var alt, anchor, bq, capcode, data, date, email, file, flag, i, info, name, node, nodes, post, quotelink, quotes, subject, text, thumb, tripcode, uniqueID, _i, _j, _k, _len, _len1, _ref, _ref1, _ref2; + function Post(root, thread, board, that) { + var alt, anchor, bq, capcode, data, date, email, file, flag, i, info, name, node, nodes, post, quotelink, quotes, size, subject, text, thumb, tripcode, uniqueID, unit, _i, _j, _k, _len, _len1, _ref, _ref1, _ref2; this.thread = thread; this.board = board; + if (that == null) { + that = {}; + } this.ID = +root.id.slice(2); post = $('.post', root); info = $('.postInfo', post); @@ -729,16 +724,24 @@ thumb: thumb, URL: anchor.href, MD5: thumb.dataset.md5, - size: alt.match(/\d+(\.\d+)?\s\w+$/)[0], isSpoiler: $.hasClass(anchor, 'imgspoiler') }; - this.file.thumbURL = "" + location.protocol + "//thumbs.4chan.org/" + board + "/thumb/" + (this.file.URL.match(/(\d+)\./)[1]) + "s.jpg"; + size = +alt.match(/\d+(\.\d+)?/)[0]; + unit = ['B', 'KB', 'MB', 'GB'].indexOf(alt.match(/\w+$/)[0]); + while (unit--) { + size *= 1024; + } + this.file.size = size; + this.file.thumbURL = that.isArchived ? thumb.src : "" + location.protocol + "//thumbs.4chan.org/" + board + "/thumb/" + (this.file.URL.match(/(\d+)\./)[1]) + "s.jpg"; this.file.name = $('span[title]', this.file.info).title; - if (this.file.isImage = /(jpg|png|gif|svg)$/i.test(this.file.name)) { + if (this.file.isImage = /(jpg|png|gif)$/i.test(this.file.name)) { this.file.dimensions = this.file.text.textContent.match(/\d+x\d+/)[0]; } } this.isReply = $.hasClass(post, 'reply'); + if (that.isArchived) { + this.isDead = true; + } this.clones = []; g.posts["" + board + "." + this] = thread.posts[this] = board.posts[this] = this; } @@ -837,6 +840,9 @@ this.file.text = $('.fileText', file); this.file.thumb = $('img[data-md5]', file); } + if (origin.isDead) { + this.isDead = true; + } this.isClone = true; index = origin.clones.push(this) - 1; root.setAttribute('data-clone', index); @@ -1087,7 +1093,7 @@ postID = postID.match(/\d+/)[0]; } path = threadID ? "" + board + "/thread/" + threadID : "" + board + "/post/" + postID; - switch (board) { + switch ("" + board) { case 'a': case 'co': case 'jp': @@ -1164,6 +1170,134 @@ } }; + Build = { + shortFilename: function(filename, isReply) { + var threshold; + threshold = isReply ? 30 : 40; + if (filename.length - 4 > threshold) { + return "" + filename.slice(0, threshold - 5) + "(...)." + filename.slice(-3); + } else { + return filename; + } + }, + post: function(o) { + var board, bq, capcode, comment, container, date, dateUTC, email, file, fl, flag, flagTitle, html, isOP, name, pi, post, postID, subject, threadID, tripcode, uniqueID; + postID = o.postID, threadID = o.threadID, board = o.board, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flag = o.flag, flagTitle = o.flagTitle, date = o.date, dateUTC = o.dateUTC, comment = o.comment, file = o.file; + isOP = postID === threadID; + html = []; + html.push(" "); + html.push("" + subject + " "); + html.push(""); + if (email) { + html.push(""); + } + html.push("" + name + ""); + if (tripcode) { + html.push(" " + tripcode + ""); + } + if (uniqueID) { + html.push(" (ID: " + uniqueID + ")"); + } + switch (capcode) { + case 'M': + html.push(' ## Mod'); + html.push(' This user is a 4chan Moderator.'); + break; + case 'A': + html.push(' ## Admin'); + html.push(' This user is the 4chan Administrator.'); + break; + case 'D': + html.push(' ## Mod'); + html.push(' This user is a 4chan Developer.'); + } + if (email) { + html.push(''); + } + if (flag) { + html.push("  + flag + "); + } + html.push(' '); + html.push("" + date + " "); + html.push(''); + html.push("No."); + html.push("" + postID + ""); + html.push(''); + pi = $.el('div', { + id: "pi" + postID, + className: 'postInfo desktop', + innerHTML: html.join('') + }); + bq = $.el('blockquote', { + id: "m" + postID, + className: 'postMessage', + innerHTML: comment + }); + if (file.name) { + html = []; + html.push('
'); + html.push("File: "); + html.push("" + file.origin + ""); + html.push('-('); + if (file.isSpoiler) { + html.push('Spoiler Image, '); + } + html.push("" + ($.bytesToString(file.size)) + ", "); + html.push(/\.pdf$/i.test(file.name) ? "PDF" : "" + file.width + "x" + file.height); + if (!file.isSpoiler) { + html.push(", " + (Build.shortFilename(file.name)) + ""); + } + html.push(")
"); + html.push(""); + html.push("" + (file.isSpoiler ? "); + html.push(''); + fl = $.el('div', { + id: "f" + postID, + className: 'file', + innerHTML: html.join('') + }); + } + post = $.el('div', { + id: "p" + postID, + className: "post " + (isOP ? 'op' : 'reply') + }); + if (fl && isOP) { + $.add(post, fl); + } + $.add(post, pi); + if (fl && !isOP) { + $.add(post, fl); + } + $.add(post, bq); + container = $.el('div', { + id: "pc" + postID, + className: "postContainer " + (isOP ? 'op' : 'reply') + "Container" + }); + if (!isOP) { + $.add(container, $.el('div', { + id: "sa" + postID, + className: 'sideArrows', + textContent: '>>' + })); + } + $.add(container, post); + return container; + } + }; + Get = { postFromRoot: function(root) { var board, index, link, post, postID; @@ -1185,6 +1319,10 @@ board = path[1]; threadID = path[3]; postID = link.hash.slice(2); + } else { + board = link.dataset.board; + threadID = ''; + postID = link.dataset.postid; } return { board: board, @@ -1193,7 +1331,7 @@ }; }, postClone: function(board, threadID, postID, root) { - var clone, origin; + var clone, origin, url; if (origin = g.posts["" + board + "." + postID]) { clone = origin.addClone(); Main.callbackNodes(Post, [clone]); @@ -1203,7 +1341,11 @@ root.textContent = "Loading post No." + postID + "..."; if (threadID) { return $.cache("/" + board + "/res/" + threadID, function() { - return Get.parsePost(this, board, threadID, postID, root); + return Get.fetchedPost(this, board, threadID, postID, root); + }); + } else if (url = Redirect.post(board, postID)) { + return $.cache(url, function() { + return Get.archivedPost(this, board, postID, root); }); } }, @@ -1219,18 +1361,31 @@ } return root; }, - parsePost: function(req, board, threadID, postID, root) { - var clone, doc, href, inBoard, inThread, link, pc, post, quote, status, _i, _len, _ref; + fetchedPost: function(req, board, threadID, postID, root) { + var clone, doc, href, inBoard, inThread, link, pc, post, quote, status, url, _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 + "."; + if (url = Redirect.post(board, postID)) { + $.cache(url, function() { + return Get.archivedPost(this, board, postID, root); + }); + } else { + $.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."; + if (url = Redirect.post(board, postID)) { + $.cache(url, function() { + return Get.archivedPost(this, board, postID, root); + }); + } else { + $.addClass(root, 'warning'); + root.textContent = "Post No." + postID + " has not been found."; + } return; } pc = d.importNode(pc, true); @@ -1256,6 +1411,86 @@ clone = post.addClone(); Main.callbackNodes(Post, [clone]); return $.replace(root.firstChild, Get.cleanRoot(clone)); + }, + archivedPost: function(req, board, postID, root) { + var bq, clone, comment, data, post, postContainer, thread, threadID; + data = JSON.parse(req.response); + if (data.error) { + $.addClass(root, 'warning'); + root.textContent = data.error; + return; + } + bq = $.el('blockquote', { + textContent: data.comment + }); + bq.innerHTML = bq.innerHTML.replace(/\n|\[\/?b\]|\[\/?spoiler\]|\[\/?code\]|\[\/?moot\]|\[\/?banned\]/g, function(text) { + switch (text) { + case '\n': + return '
'; + case '[b]': + return ''; + case '[/b]': + return ''; + case '[spoiler]': + return ''; + case '[/spoiler]': + return ''; + case '[code]': + return '
';
+          case '[/code]':
+            return '
'; + case '[moot]': + return '
'; + case '[/moot]': + return '
'; + case '[banned]': + return ''; + case '[/banned]': + return ''; + } + }); + comment = bq.innerHTML.replace(/(^|>)(>[^<$]+)(<|$)/g, '$1$2$3'); + threadID = data.thread_num; + postContainer = Build.post({ + postID: postID, + threadID: threadID, + board: board, + name: data.name, + capcode: data.capcode, + tripcode: data.trip, + uniqueID: data.poster_hash, + email: data.email, + subject: data.title, + flag: data.poster_country, + date: data.fourchan_date, + dateUTC: data.timestamp, + comment: comment, + file: { + name: data.media_filename, + origin: data.media_orig, + url: data.media_link || data.remote_media_link, + height: data.media_h, + width: data.media_w, + isSpoiler: data.spoiler === '1', + MD5: data.media_hash, + size: data.media_size, + turl: data.thumb_link || ("//thumbs.4chan.org/" + board + "/thumb/" + data.preview_orig), + theight: data.preview_h, + twidth: data.preview_w + } + }); + board = g.boards[board] || new Board(board); + thread = g.threads["" + board + "." + threadID] || new Thread(threadID, board); + post = new Post(postContainer, thread, board, { + isArchived: true + }); + Main.callbackNodes(Post, [post]); + if (!root.parentNode) { + return; + } + clone = post.addClone(); + Main.callbackNodes(Post, [clone]); + return $.replace(root.firstChild, Get.cleanRoot(clone)); } }; @@ -1267,7 +1502,7 @@ }); }, node: function() { - var ID, a, board, data, i, index, m, node, nodes, quote, quoteID, quotes, snapshot, text, _i, _j, _len, _ref; + var ID, a, board, data, i, index, m, node, nodes, post, quote, quoteID, quotes, snapshot, text, _i, _j, _len, _ref; if (this.isClone) { return; } @@ -1288,13 +1523,37 @@ ID = quote.match(/\d+$/)[0]; board = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : this.board.ID; quoteID = "" + board + "." + ID; + if (post = g.posts[quoteID]) { + if (post.isDead) { + a = $.el('a', { + href: Redirect.thread(board, 0, ID), + className: 'quotelink deadlink', + textContent: "" + quote + "\u00A0(Dead)", + target: '_blank' + }); + } else { + a = $.el('a', { + href: "/" + board + "/" + post.thread + "/res/#p" + ID, + className: 'quotelink', + textContent: quote + }); + } + } else { + a = $.el('a', { + href: Redirect.thread(board, 0, ID), + className: 'deadlink', + target: '_blank', + textContent: this.isDead ? quote : "" + quote + "\u00A0(Dead)" + }); + if (Redirect.post(board, ID)) { + $.addClass(a, 'quotelink'); + a.setAttribute('data-board', board); + a.setAttribute('data-postid', ID); + } + } if (this.quotes.indexOf(quoteID) === -1) { this.quotes.push(quoteID); } - a = $.el('a', { - textContent: quote, - className: 'quotelink deadlink' - }); this.nodes.quotelinks.push(a); nodes.push(a); data = data.slice(index + quote.length); diff --git a/Cakefile b/Cakefile index 7619ac4cb..68a89ad75 100644 --- a/Cakefile +++ b/Cakefile @@ -19,6 +19,7 @@ HEADER = """ // @match *://boards.4chan.org/* // @match *://images.4chan.org/* // @match *://sys.4chan.org/* +// @match *://*.foolz.us/api/* // @run-at document-start // @updateURL https://github.com/MayhemYDG/4chan-x/raw/stable/4chan_x.user.js // @downloadURL https://github.com/MayhemYDG/4chan-x/raw/stable/4chan_x.user.js diff --git a/script.coffee b/script.coffee index caee1c281..89eddf2de 100644 --- a/script.coffee +++ b/script.coffee @@ -407,15 +407,6 @@ $.extend $, p.setAttribute 'onclick', 'return window' p.onclick() )() - shortenFilename: (filename, isOP) -> - # FILENAME SHORTENING SCIENCE: - # OPs have a +10 characters threshold. - # The file extension is not taken into account. - threshold = if isOP then 40 else 30 - if filename.length - 4 > threshold - "#{filename[...threshold - 5]}(...).#{filename.match(/\w+$/)}" - else - filename bytesToString: (size) -> unit = 0 # Bytes while size >= 1024 @@ -502,7 +493,7 @@ class Post callbacks: [] toString: -> @ID - constructor: (root, @thread, @board) -> + constructor: (root, @thread, @board, that={}) -> @ID = +root.id[2..] post = $ '.post', root @@ -583,14 +574,23 @@ class Post thumb: thumb URL: anchor.href MD5: thumb.dataset.md5 - size: alt.match(/\d+(\.\d+)?\s\w+$/)[0] isSpoiler: $.hasClass anchor, 'imgspoiler' - @file.thumbURL = "#{location.protocol}//thumbs.4chan.org/#{board}/thumb/#{@file.URL.match(/(\d+)\./)[1]}s.jpg" + size = +alt.match(/\d+(\.\d+)?/)[0] + unit = ['B', 'KB', 'MB', 'GB'].indexOf alt.match(/\w+$/)[0] + while unit-- + size *= 1024 + @file.size = size + @file.thumbURL = + if that.isArchived + thumb.src + else + "#{location.protocol}//thumbs.4chan.org/#{board}/thumb/#{@file.URL.match(/(\d+)\./)[1]}s.jpg" @file.name = $('span[title]', @file.info).title - if @file.isImage = /(jpg|png|gif|svg)$/i.test @file.name # I want to believe. + if @file.isImage = /(jpg|png|gif)$/i.test @file.name @file.dimensions = @file.text.textContent.match(/\d+x\d+/)[0] @isReply = $.hasClass post, 'reply' + @isDead = true if that.isArchived @clones = [] g.posts["#{board}.#{@}"] = thread.posts[@] = board.posts[@] = @ @@ -663,6 +663,7 @@ class Clone extends Post @file.text = $ '.fileText', file @file.thumb = $ 'img[data-md5]', file + @isDead = true if origin.isDead @isClone = true index = origin.clones.push(@) - 1 root.setAttribute 'data-clone', index @@ -956,7 +957,7 @@ Redirect = "#{board}/thread/#{threadID}" else "#{board}/post/#{postID}" - switch board + switch "#{board}" when 'a', 'co', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'wsg', 'dev', 'foolz' url = "//archive.foolz.us/#{path}/" if threadID and postID @@ -990,6 +991,125 @@ Redirect = url = "//boards.4chan.org/#{board}/" url or '' +Build = + shortFilename: (filename, isReply) -> + # FILENAME SHORTENING SCIENCE: + # OPs have a +10 characters threshold. + # The file extension is not taken into account. + threshold = if isReply then 30 else 40 + if filename.length - 4 > threshold + "#{filename[...threshold - 5]}(...).#{filename[-3..]}" + else + filename + post: (o) -> + { + postID, threadID, board + name, capcode, tripcode, uniqueID, email, subject, flag, flagTitle, date, dateUTC, comment + file + } = o + isOP = postID is threadID + + # post info + html = [] + # input + html.push " " + # subject + html.push "#{subject} " + # name block + html.push "" + # mail start + html.push "" if email + # name + html.push "#{name}" + # tripcode + html.push " #{tripcode}" if tripcode + # user id + html.push " (ID: #{uniqueID})" if uniqueID + # capcode + switch capcode + when 'M' + html.push ' ## Mod' + html.push ' This user is a 4chan Moderator.' + when 'A' + html.push ' ## Admin' + html.push ' This user is the 4chan Administrator.' + when 'D' + html.push ' ## Mod' + html.push ' This user is a 4chan Developer.' + # mail end + html.push '' if email + # flag + # XXX what about troll flags in /pol/? + html.push " #{flag}" if flag + # end name block + html.push ' ' + # date + html.push "#{date} " + # post num + html.push '' + html.push "No." + html.push "#{postID}" + # XXX closed/sticky? + html.push '' + pi = $.el 'div', + id: "pi#{postID}" + className: 'postInfo desktop' + innerHTML: html.join '' + + bq = $.el 'blockquote', + id: "m#{postID}" + className: 'postMessage' + innerHTML: comment + + # file + if file.name # XXX need to fix support of Flash + html = [] + html.push '
' + html.push "File: " + html.push "#{file.origin}" + html.push '-(' + html.push 'Spoiler Image, ' if file.isSpoiler + html.push "#{$.bytesToString file.size}, " + html.push if /\.pdf$/i.test file.name + "PDF" + else + "#{file.width}x#{file.height}" + html.push ", #{Build.shortFilename file.name}" unless file.isSpoiler + html.push ")
" + html.push "" + html.push "#{if file.isSpoiler then " + html.push '' + fl = $.el 'div', + id: "f#{postID}" + className: 'file' + innerHTML: html.join '' + + post = $.el 'div', + id: "p#{postID}" + className: "post #{if isOP then 'op' else 'reply'}" + $.add post, fl if fl and isOP + $.add post, pi + $.add post, fl if fl and !isOP + $.add post, bq + + container = $.el 'div', + id: "pc#{postID}" + className: "postContainer #{if isOP then 'op' else 'reply'}Container" + unless isOP + $.add container, $.el 'div', + id: "sa#{postID}" + className: 'sideArrows' + textContent: '>>' + $.add container, post + + container + Get = postFromRoot: (root) -> link = $ 'a[title="Highlight this post"]', root @@ -1004,11 +1124,10 @@ Get = board = path[1] threadID = path[3] postID = link.hash[2..] - # XXX - # else # quote resurrection - # board = ??? - # threadID = ??? - # postID = ??? + else # resurrected quote + board = link.dataset.board + threadID = '' + postID = link.dataset.postid return { board: board threadID: threadID @@ -1024,30 +1143,29 @@ Get = 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 + Get.fetchedPost @, board, threadID, postID, root + else if url = Redirect.post board, postID + $.cache url, -> + Get.archivedPost @, 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) -> + fetchedPost: (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}." + if url = Redirect.post board, postID + $.cache url, -> + Get.archivedPost @, 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 '' @@ -1055,12 +1173,12 @@ Get = 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." + if url = Redirect.post board, postID + $.cache url, -> + Get.archivedPost @, board, postID, root + else + $.addClass root, 'warning' + root.textContent = "Post No.#{postID} has not been found." return pc = d.importNode pc, true @@ -1079,6 +1197,96 @@ Get = 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() + Main.callbackNodes Post, [clone] + $.replace root.firstChild, Get.cleanRoot clone + archivedPost: (req, board, postID, root) -> + data = JSON.parse req.response + if data.error + $.addClass root, 'warning' + root.textContent = data.error + return + + # convert comment to html + bq = $.el 'blockquote', textContent: data.comment # set this first to convert text to HTML entities + # https://github.com/eksopl/fuuka/blob/master/Board/Yotsuba.pm#L413-452 + # https://github.com/eksopl/asagi/blob/master/src/main/java/net/easymodo/asagi/Yotsuba.java#L109-138 + bq.innerHTML = bq.innerHTML.replace /// + \n + | \[/?b\] + | \[/?spoiler\] + | \[/?code\] + | \[/?moot\] + | \[/?banned\] + ///g, (text) -> + switch text + when '\n' + '
' + when '[b]' + '' + when '[/b]' + '' + when '[spoiler]' + '' + when '[/spoiler]' + '' + when '[code]' + '
'
+          when '[/code]'
+            '
' + when '[moot]' + '
' + when '[/moot]' + '
' + when '[banned]' + '' + when '[/banned]' + '' + # greentext + comment = bq.innerHTML.replace /(^|>)(>[^<$]+)(<|$)/g, '$1$2$3' + + threadID = data.thread_num + postContainer = Build.post + # id + postID: postID + threadID: threadID + board: board + # info + name: data.name + capcode: data.capcode + tripcode: data.trip + uniqueID: data.poster_hash + email: data.email + subject: data.title + flag: data.poster_country + # XXX flagTitle: data.??? + date: data.fourchan_date + dateUTC: data.timestamp + comment: comment + # file + file: + name: data.media_filename + origin: data.media_orig + url: data.media_link or data.remote_media_link + height: data.media_h + width: data.media_w + isSpoiler: data.spoiler is '1' + MD5: data.media_hash + size: data.media_size + turl: data.thumb_link or "//thumbs.4chan.org/#{board}/thumb/#{data.preview_orig}" + theight: data.preview_h + twidth: data.preview_w + + board = g.boards[board] or + new Board board + thread = g.threads["#{board}.#{threadID}"] or + new Thread threadID, board + post = new Post postContainer, thread, board, + isArchived: true + Main.callbackNodes Post, [post] + # Stop here if the container has been removed while loading. return unless root.parentNode clone = post.addClone() @@ -1120,28 +1328,37 @@ Quotify = @board.ID quoteID = "#{board}.#{ID}" + + # \u00A0 is nbsp + if post = g.posts[quoteID] + if post.isDead + a = $.el 'a', + href: Redirect.thread board, 0, ID + className: 'quotelink deadlink' + textContent: "#{quote}\u00A0(Dead)" + target: '_blank' + else + # Don't (Dead) when quotifying in an archived post, + # and we know the post still exists. + a = $.el 'a', + href: "/#{board}/#{post.thread}/res/#p#{ID}" + className: 'quotelink' + textContent: quote + else + a = $.el 'a', + href: Redirect.thread board, 0, ID + className: 'deadlink' + target: '_blank' + # Don't (Dead) when quotifying in an archived post, + # and we don't know anything about the post. + textContent: if @isDead then quote else "#{quote}\u00A0(Dead)" + if Redirect.post board, ID + $.addClass a, 'quotelink' + a.setAttribute 'data-board', board + a.setAttribute 'data-postid', ID + if @quotes.indexOf(quoteID) is -1 @quotes.push quoteID - - a = $.el 'a', - # \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}" - # a.className = 'quotelink' - # else - # a.href = Redirect.thread board, 0, ID - # a.className = 'deadlink' - # a.target = '_blank' - # if Redirect.post board, ID - # $.addClass a, 'quotelink' - # a.setAttribute 'data-board', board - # a.setAttribute 'data-id', ID - @nodes.quotelinks.push a nodes.push a data = data[index + quote.length..] @@ -1242,7 +1459,7 @@ QuoteInline = $.x 'ancestor-or-self::*[parent::blockquote][1]', inline root = $.x "following-sibling::div[@id='i#{postID}'][1]", root continue unless el = root.firstElementChild - post = g.posts["#{board}.#{postID}"] + post = g.posts["#{board}.#{postID}"] post.rmClone el.dataset.clone if Conf['Forward Hiding'] and