diff --git a/4chan_x.user.js b/4chan_x.user.js
index 6a3487b9c..a5f5f3455 100644
--- a/4chan_x.user.js
+++ b/4chan_x.user.js
@@ -77,7 +77,7 @@
*/
(function() {
- var $, $$, Anonymize, ArchiveLink, AutoGif, Conf, Config, DeleteLink, DownloadLink, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Menu, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportLink, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base;
+ var $, $$, Anonymize, ArchiveLink, AutoGif, Build, Conf, Config, DeleteLink, DownloadLink, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Menu, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportLink, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base;
Config = {
main: {
@@ -487,15 +487,6 @@
$.add(d.head, script);
return $.rm(script);
},
- shortenFilename: function(filename, isOP) {
- var threshold;
- threshold = 30 + 10 * isOP;
- if (filename.replace(/\.\w+$/, '').length > threshold) {
- return "" + filename.slice(0, threshold - 5) + "(...)" + (filename.match(/\.\w+$/));
- } else {
- return filename;
- }
- },
bytesToString: function(size) {
var unit;
unit = 0;
@@ -3398,7 +3389,7 @@
unit: alt.match(/\w+$/)[0],
resolution: node.textContent.match(/\d+x\d+|PDF/)[0],
fullname: filename,
- shortname: $.shortenFilename(filename, post.ID === post.threadID)
+ shortname: Build.shortFilename(filename, post.ID === post.threadID)
};
node.setAttribute('data-filename', filename);
return node.innerHTML = FileInfo.funk(FileInfo);
@@ -3490,7 +3481,7 @@
}
root.textContent = "Loading post No." + postID + "...";
if (threadID) {
- return $.cache("/" + board + "/res/" + threadID, function() {
+ return $.cache("//api.4chan.org/" + board + "/res/" + threadID + ".json", function() {
return Get.parsePost(this, board, threadID, postID, root, cb);
});
} else if (url = Redirect.post(board, postID)) {
@@ -3500,7 +3491,7 @@
}
},
parsePost: function(req, board, threadID, postID, root, cb) {
- var doc, href, link, pc, quote, status, url, _i, _len, _ref;
+ var post, posts, status, url, _i, _len;
status = req.status;
if (status !== 200) {
if (url = Redirect.post(board, postID)) {
@@ -3508,166 +3499,44 @@
return Get.parseArchivedPost(this, board, postID, root, cb);
});
} 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))) {
- if (url = Redirect.post(board, postID)) {
- $.cache(url, function() {
- return Get.parseArchivedPost(this, board, postID, root, cb);
- });
- } else {
- root.textContent = "Post No." + postID + " has not been found.";
+ posts = JSON.parse(req.response).posts;
+ postID = +postID;
+ for (_i = 0, _len = posts.length; _i < _len; _i++) {
+ post = posts[_i];
+ if (post.no === postID) {
+ break;
}
- 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;
+ if (post.no > postID) {
+ if (url = Redirect.post(board, postID)) {
+ $.cache(url, function() {
+ return Get.parseArchivedPost(this, board, postID, root, cb);
+ });
+ } else {
+ $.addClass(root, 'warning');
+ root.textContent = "Post No." + postID + " has not been found.";
+ }
+ return;
}
- 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;
- $.replace(root.firstChild, pc);
+ $.replace(root.firstChild, Get.cleanPost(Build.postFromObject(post, board)));
if (cb) {
return cb();
}
},
parseArchivedPost: function(req, board, postID, root, cb) {
- var bq, br, capcode, data, email, file, filename, filesize, isOP, name, nameBlock, pc, pi, piM, span, spoiler, subject, threadID, thumb_src, timestamp, trip, userID;
+ var bq, comment, data, o;
data = JSON.parse(req.response);
- $.addClass(root, 'archivedPost');
if (data.error) {
+ $.addClass(root, 'warning');
root.textContent = data.error;
return;
}
- threadID = data.thread_num;
- isOP = postID === threadID;
- name = data.name, trip = data.trip, timestamp = data.timestamp;
- subject = data.title;
- userID = data.poster_hash;
- piM = $.el('div', {
- id: "pim" + postID,
- className: 'postInfoM mobile',
- innerHTML: "
" + data.fourchan_date + "
No." + postID + ""
- });
- $('.name', piM).textContent = name;
- $('.subject', piM).textContent = subject;
- br = $('br', piM);
- if (trip) {
- $.before(br, [
- $.tn(' '), $.el('span', {
- className: 'postertrip',
- textContent: trip
- })
- ]);
- }
- capcode = data.capcode;
- if (capcode !== 'N') {
- $.addClass(br.parentNode, capcode === 'A' ? 'capcodeAdmin' : 'capcodeMod');
- $.before(br, [
- $.tn(' '), $.el('strong', {
- className: 'capcode',
- textContent: capcode === 'A' ? '## Admin' : '## Mod'
- }), $.tn(' '), $.el('img', {
- src: capcode === 'A' ? '//static.4chan.org/image/adminicon.gif' : '//static.4chan.org/image/modicon.gif',
- alt: capcode === 'A' ? 'This user is the 4chan Administrator.' : 'This user is a 4chan Moderator.',
- title: capcode === 'A' ? 'This user is the 4chan Administrator.' : 'This user is a 4chan Moderator.',
- className: 'identityIcon'
- })
- ]);
- }
- pi = $.el('div', {
- id: "pi" + postID,
- className: 'postInfo desktop',
- innerHTML: " data.fourchan_date No." + postID + ""
- });
- $('.subject', pi).textContent = subject;
- nameBlock = $('.nameBlock', pi);
- if (data.email) {
- email = $.el('a', {
- className: 'useremail',
- href: "mailto:" + data.email
- });
- $.add(nameBlock, email);
- nameBlock = email;
- }
- $.add(nameBlock, $.el('span', {
- className: 'name',
- textContent: data.name
- }));
- if (userID) {
- $.add(nameBlock, [
- $.tn(' '), $.el('span', {
- className: "posteruid id_" + userID,
- innerHTML: "(ID: " + userID + ")"
- })
- ]);
- }
- if (trip) {
- $.add(nameBlock, [
- $.tn(' '), $.el('span', {
- className: 'postertrip',
- textContent: trip
- })
- ]);
- }
- nameBlock = $('.nameBlock', pi);
- switch (capcode) {
- case 'A':
- $.addClass(nameBlock, 'capcodeAdmin');
- $.add(nameBlock, [
- $.tn(' '), $.el('strong', {
- className: 'capcode',
- textContent: '## Admin'
- }), $.tn(' '), $.el('img', {
- src: '//static.4chan.org/image/adminicon.gif',
- alt: 'This user is the 4chan Administrator.',
- title: 'This user is the 4chan Administrator.',
- className: 'identityIcon'
- })
- ]);
- break;
- case 'M':
- $.addClass(nameBlock, 'capcodeMod');
- $.add(nameBlock, [
- $.tn(' '), $.el('strong', {
- className: 'capcode',
- textContent: '## Mod'
- }), $.tn(' '), $.el('img', {
- src: '//static.4chan.org/image/modicon.gif',
- alt: 'This user is a 4chan Moderator.',
- title: 'This user is a 4chan Moderator.',
- className: 'identityIcon'
- })
- ]);
- break;
- case 'D':
- $.addClass(nameBlock, 'capcodeDeveloper');
- $.add(nameBlock, [
- $.tn(' '), $.el('strong', {
- className: 'capcode',
- textContent: '## Developer'
- }), $.tn(' '), $.el('img', {
- src: '//static.4chan.org/image/developericon.gif',
- alt: 'This user is a 4chan Developer.',
- title: 'title="This user is a 4chan Developer.',
- className: 'identityIcon'
- })
- ]);
- }
bq = $.el('blockquote', {
- id: "m" + postID,
- className: 'postMessage',
textContent: data.comment
});
bq.innerHTML = bq.innerHTML.replace(/\n|\[\/?b\]|\[\/?spoiler\]|\[\/?code\]|\[\/?moot\]|\[\/?banned\]/g, function(text) {
@@ -3696,37 +3565,47 @@
return '';
}
});
- bq.innerHTML = bq.innerHTML.replace(/(^|>)(>[^<$]+)(<|$)/g, '$1$2$3');
- pc = $.el('div', {
- id: "pc" + postID,
- className: "postContainer " + (isOP ? 'op' : 'reply') + "Container",
- innerHTML: "
"
- });
- $.add(pc.firstChild, [piM, pi, bq]);
- if (filename = data.media_filename) {
- file = $.el('div', {
- id: "f" + postID,
- className: 'file'
- });
- spoiler = data.spoiler === '1';
- filesize = $.bytesToString(data.media_size);
- $.add(file, $.el('div', {
- className: 'fileInfo',
- innerHTML: "File: " + data.media_orig + "-(" + (spoiler ? 'Spoiler Image, ' : '') + filesize + ", " + data.media_w + "x" + data.media_h + ", )"
- }));
- span = $('span[title]', file);
- span.title = filename;
- span.textContent = $.shortenFilename(filename, isOP);
- thumb_src = data.media_status === 'available' ? "src=" + data.thumb_link : '';
- $.add(file, $.el('a', {
- className: spoiler ? 'fileThumb imgspoiler' : 'fileThumb',
- href: data.media_link || data.remote_media_link,
- target: '_blank',
- innerHTML: "
"
- }));
- $.after((isOP ? piM : pi), file);
+ comment = bq.innerHTML.replace(/(^|>)(>[^<$]+)(<|$)/g, '$1$2$3');
+ o = {
+ postID: postID,
+ threadID: data.thread_num,
+ board: board,
+ name: data.name_processed,
+ capcode: (function() {
+ switch (data.capcode) {
+ case 'M':
+ return 'mod';
+ case 'A':
+ return 'admin';
+ case 'D':
+ return 'developer';
+ }
+ })(),
+ tripcode: data.trip,
+ uniqueID: data.poster_hash,
+ email: encodeURIComponent(data.email),
+ subject: data.title_processed,
+ flagCode: data.poster_country,
+ date: data.fourchan_date,
+ dateUTC: data.timestamp,
+ comment: comment
+ };
+ if (data.media_filename) {
+ o.file = {
+ name: data.media_filename_processed,
+ timestamp: data.media_orig,
+ url: data.media_link || data.remote_media_link,
+ height: data.media_h,
+ width: data.media_w,
+ 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,
+ isSpoiler: data.spoiler === '1'
+ };
}
- $.replace(root.firstChild, Get.cleanPost(pc));
+ $.replace(root.firstChild, Get.cleanPost(Build.post(o, true)));
if (cb) {
return cb();
}
@@ -3782,6 +3661,164 @@
}
};
+ Build = {
+ shortFilename: function(filename, isOP) {
+ var threshold;
+ threshold = isOP ? 40 : 30;
+ if (filename.length - 4 > threshold) {
+ return "" + filename.slice(0, threshold - 5) + "(...)." + filename.slice(-3);
+ } else {
+ return filename;
+ }
+ },
+ postFromObject: function(data, board) {
+ var o;
+ o = {
+ postID: data.no,
+ threadID: data.resto || data.no,
+ board: board,
+ name: data.name,
+ capcode: data.capcode,
+ tripcode: data.trip,
+ uniqueID: data.id,
+ email: data.email.replace(/\s/g, '%20'),
+ subject: data.sub,
+ flagCode: data.country,
+ flagName: data.country_name,
+ date: data.now,
+ dateUTC: data.time,
+ comment: data.com
+ };
+ if (data.ext) {
+ o.file = {
+ name: data.filename + data.ext,
+ timestamp: "" + data.tim + data.ext,
+ url: "//images.4chan.org/" + board + "/src/" + data.tim + data.ext,
+ height: data.h,
+ width: data.w,
+ MD5: data.md5,
+ size: data.fsize,
+ turl: "//thumbs.4chan.org/" + board + "/thumb/" + data.tim + "s.jpg",
+ theight: data.tn_h,
+ twidth: data.tn_w,
+ isSpoiler: !!data.spoiler,
+ isDeleted: !!data.filedeleted
+ };
+ }
+ return Build.post(o);
+ },
+ post: function(o, isArchived) {
+ /*
+ This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS).
+ @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE
+ */
+
+ var board, capcode, capcodeClass, capcodeStart, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, flag, flagCode, flagName, href, imgSrc, isOP, name, postID, quote, staticPath, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref;
+ 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, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, comment = o.comment, file = o.file;
+ isOP = postID === threadID;
+ staticPath = '//static.4chan.org';
+ if (email) {
+ emailStart = '';
+ emailEnd = '';
+ } else {
+ emailStart = '';
+ emailEnd = '';
+ }
+ userID = !capcode && uniqueID ? (" (ID: ") + ("" + uniqueID + ") ") : '';
+ switch (capcode) {
+ case 'admin':
+ case 'admin_highlight':
+ capcodeClass = " capcodeAdmin";
+ capcodeStart = " ## Admin";
+ capcode = ("
";
+ break;
+ case 'mod':
+ capcodeClass = " capcodeMod";
+ capcodeStart = " ## Moderator";
+ capcode = ("
";
+ break;
+ case 'developer':
+ capcodeClass = " capcodeDeveloper";
+ capcodeStart = " ## Developer";
+ capcode = ("
";
+ break;
+ default:
+ capcodeClass = '';
+ capcodeStart = '';
+ capcode = '';
+ }
+ flag = flagCode ? ("
") : '';
+ if (file != null ? file.isDeleted : void 0) {
+ fileHTML = isOP ? ("") + ("
") + " " : ("") + ("
") + " ";
+ } else if (file) {
+ ext = file.name.slice(-3);
+ if (!file.twidth && !file.theight && ext === 'gif') {
+ file.twidth = file.width;
+ file.theight = file.height;
+ }
+ fileSize = $.bytesToString(file.size);
+ fileThumb = file.turl;
+ if (file.isSpoiler) {
+ fileSize = "Spoiler Image, " + fileSize;
+ if (!isArchived) {
+ fileThumb = '//thumbs.4chan.org/image/spoiler';
+ fileThumb += (function() {
+ switch (board) {
+ case 'a':
+ return '-a';
+ case 'co':
+ return '-co';
+ case 'jp':
+ return '-jp';
+ case 'lit':
+ return 'lit';
+ case 'm':
+ return '-m2';
+ case 'mlp':
+ return '-mlp';
+ case 'tg':
+ return '-tg2';
+ case 'tv':
+ return '-tv5';
+ case 'v':
+ case 'vg':
+ return '-v';
+ case 'vp':
+ return '-vp';
+ default:
+ return '';
+ }
+ })();
+ fileThumb += '.png';
+ file.twidth = file.theight = 100;
+ }
+ }
+ imgSrc = ("") + ("
");
+ fileDims = ext === 'pdf' ? 'PDF' : "" + file.width + "x" + file.height;
+ fileInfo = ("File: " + file.timestamp + "") + ("-(" + fileSize + ", " + fileDims + (file.isSpoiler ? '' : ", " + (Build.shortFilename(file.name)) + "'")) + ")";
+ fileHTML = "" + fileInfo + "
" + imgSrc + "
";
+ } else {
+ fileHTML = '';
+ }
+ tripcode = tripcode ? " " + tripcode + "" : '';
+ container = $.el('div', {
+ className: "postContainer " + (isOP ? 'op' : 'reply') + "Container",
+ id: "pc" + postID,
+ innerHTML: (isOP ? '' : ">>
") + ("") + ("
") + ("
") + emailStart + ("" + name + "") + tripcode + emailEnd + capcodeStart + capcode + userID + flag + ("
" + subject + "") + ("" + date) + '
' + ("No.") + ("" + postID + "") + '' + '
' + (isOP ? fileHTML : '') + ("
") + ("
") + ("
" + (subject || '') + " ") + ("
") + emailStart + ("" + name + "") + tripcode + emailEnd + capcodeStart + capcode + userID + flag + ' ' + ("
" + date + " ") + "
" + ("No.") + ("" + postID + "") + '' + '
' + (isOP ? '' : fileHTML) + ("
" + comment + "
") + '
'
+ });
+ _ref = $$('.quotelink', container);
+ 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;
+ }
+ return container;
+ }
+ };
+
TitlePost = {
init: function() {
return d.title = Get.title();
diff --git a/script.coffee b/script.coffee
index 8f8a34812..2be1f4181 100644
--- a/script.coffee
+++ b/script.coffee
@@ -368,15 +368,6 @@ $.extend $,
script = $.el 'script', textContent: code
$.add d.head, script
$.rm script
- shortenFilename: (filename, isOP) ->
- # FILENAME SHORTENING SCIENCE:
- # OPs have a +10 characters threshold.
- # The file extension is not taken into account.
- threshold = 30 + 10 * isOP
- if filename.replace(/\.\w+$/, '').length > threshold
- "#{filename[...threshold - 5]}(...)#{filename.match(/\.\w+$/)}"
- else
- filename
bytesToString: (size) ->
unit = 0 # Bytes
while size >= 1024
@@ -1977,7 +1968,7 @@ QR =
# Enable auto-posting if we have stuff to post, disable it otherwise.
QR.cooldown.auto = QR.replies.length > 1
QR.cooldown.set if g.BOARD is 'q' or /sage/i.test reply.email then 60 else 30
- if Conf['Open Reply in New Tab'] && !g.REPLY && !QR.cooldown.auto
+ if Conf['Open Reply in New Tab'] and !g.REPLY and !QR.cooldown.auto
$.open "//boards.4chan.org/#{g.BOARD}/res/#{threadID}#p#{postID}"
if Conf['Persistent QR'] or QR.cooldown.auto
@@ -2410,7 +2401,7 @@ Updater =
Updater.unsuccessfulFetchCount = 0
Updater.set 'timer', -Updater.getInterval()
- scroll = Conf['Scrolling'] && Updater.scrollBG() &&
+ scroll = Conf['Scrolling'] and Updater.scrollBG() and
lastPost.getBoundingClientRect().bottom - d.documentElement.clientHeight < 25
$.add Updater.thread, nodes.reverse()
if scroll
@@ -2689,7 +2680,7 @@ FileInfo =
unit: alt.match(/\w+$/)[0]
resolution: node.textContent.match(/\d+x\d+|PDF/)[0]
fullname: filename
- shortname: $.shortenFilename filename, post.ID is post.threadID
+ shortname: Build.shortFilename filename, post.ID is post.threadID
# XXX GM/Scriptish
node.setAttribute 'data-filename', filename
node.innerHTML = FileInfo.funk FileInfo
@@ -2738,7 +2729,7 @@ Get =
root.textContent = "Loading post No.#{postID}..."
if threadID
- $.cache "/#{board}/res/#{threadID}", ->
+ $.cache "//api.4chan.org/#{board}/res/#{threadID}.json", ->
Get.parsePost @, board, threadID, postID, root, cb
else if url = Redirect.post board, postID
$.cache url, ->
@@ -2751,6 +2742,7 @@ Get =
$.cache url, ->
Get.parseArchivedPost @, board, postID, root, cb
else
+ $.addClass root, 'warning'
root.textContent =
if status is 404
"Thread No.#{threadID} has not been found."
@@ -2758,153 +2750,31 @@ Get =
"Error #{req.status}: #{req.statusText}."
return
- doc = d.implementation.createHTMLDocument ''
- doc.documentElement.innerHTML = req.response
+ posts = JSON.parse(req.response).posts
+ postID = +postID
+ for post in posts
+ break if post.no is postID # we found it!
+ if post.no > postID
+ # The post can be deleted by the time we check a quote.
+ if url = Redirect.post board, postID
+ $.cache url, ->
+ Get.parseArchivedPost @, board, postID, root, cb
+ else
+ $.addClass root, 'warning'
+ root.textContent = "Post No.#{postID} has not been found."
+ return
- unless pc = doc.getElementById "pc#{postID}"
- # The post can be deleted by the time we check a quote.
- if url = Redirect.post board, postID
- $.cache url, ->
- Get.parseArchivedPost @, board, postID, root, cb
- 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 = $ '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
+ $.replace root.firstChild, Get.cleanPost Build.postFromObject post, board
cb() if cb
parseArchivedPost: (req, board, postID, root, cb) ->
data = JSON.parse req.response
- $.addClass root, 'archivedPost'
if data.error
+ $.addClass root, 'warning'
root.textContent = data.error
return
- threadID = data.thread_num
- isOP = postID is threadID
- {name, trip, timestamp} = data
- subject = data.title
- userID = data.poster_hash
-
- # post info (mobile)
- piM = $.el 'div',
- id: "pim#{postID}"
- className: 'postInfoM mobile'
- innerHTML: "
#{data.fourchan_date}
No.#{postID}"
- $('.name', piM).textContent = name
- $('.subject', piM).textContent = subject
- br = $ 'br', piM
- if trip
- $.before br, [$.tn(' '), $.el 'span',
- className: 'postertrip'
- textContent: trip
- ]
- {capcode} = data
- if capcode isnt 'N' # 'A'dmin or 'M'od
- $.addClass br.parentNode, if capcode is 'A' then 'capcodeAdmin' else 'capcodeMod'
- $.before br, [
- $.tn(' '),
- $.el('strong',
- className: 'capcode',
- textContent: if capcode is 'A' then '## Admin' else '## Mod'
- ),
- $.tn(' '),
- $.el('img',
- src: if capcode is 'A' then '//static.4chan.org/image/adminicon.gif' else '//static.4chan.org/image/modicon.gif',
- alt: if capcode is 'A' then 'This user is the 4chan Administrator.' else 'This user is a 4chan Moderator.',
- title: if capcode is 'A' then 'This user is the 4chan Administrator.' else 'This user is a 4chan Moderator.',
- className: 'identityIcon'
- )
- ]
-
- # post info
- pi = $.el 'div',
- id: "pi#{postID}"
- className: 'postInfo desktop'
- innerHTML: " data.fourchan_date No.#{postID}"
- # subject
- $('.subject', pi).textContent = subject
- nameBlock = $ '.nameBlock', pi
- if data.email
- email = $.el 'a',
- className: 'useremail'
- href: "mailto:#{data.email}"
- $.add nameBlock, email
- nameBlock = email
- $.add nameBlock, $.el 'span',
- className: 'name'
- textContent: data.name
- if userID
- $.add nameBlock, [$.tn(' '), $.el('span',
- className: "posteruid id_#{userID}"
- innerHTML: "(ID: #{userID})"
- )]
- if trip
- $.add nameBlock, [$.tn(' '), $.el('span', className: 'postertrip', textContent: trip)]
- nameBlock = $ '.nameBlock', pi
- switch capcode # 'A'dmin or 'M'od or 'D'eveloper
- when 'A'
- $.addClass nameBlock, 'capcodeAdmin'
- $.add nameBlock, [
- $.tn(' '),
- $.el('strong',
- className: 'capcode'
- textContent: '## Admin'
- ),
- $.tn(' '),
- $.el('img',
- src: '//static.4chan.org/image/adminicon.gif'
- alt: 'This user is the 4chan Administrator.'
- title: 'This user is the 4chan Administrator.'
- className: 'identityIcon'
- )
- ]
- when 'M'
- $.addClass nameBlock, 'capcodeMod'
- $.add nameBlock, [
- $.tn(' '),
- $.el('strong',
- className: 'capcode'
- textContent: '## Mod'
- ),
- $.tn(' '),
- $.el('img',
- src: '//static.4chan.org/image/modicon.gif'
- alt: 'This user is a 4chan Moderator.'
- title: 'This user is a 4chan Moderator.'
- className: 'identityIcon'
- )
- ]
- when 'D'
- $.addClass nameBlock, 'capcodeDeveloper'
- $.add nameBlock, [
- $.tn(' '),
- $.el('strong',
- className: 'capcode'
- textContent: '## Developer'
- ),
- $.tn(' '),
- $.el('img',
- src: '//static.4chan.org/image/developericon.gif'
- alt: 'This user is a 4chan Developer.'
- title: 'title="This user is a 4chan Developer.'
- className: 'identityIcon'
- )
- ]
-
- # comment
- bq = $.el 'blockquote',
- id: "m#{postID}"
- className: 'postMessage'
- textContent: data.comment # set this first to convert text to HTML entities
+ # 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 ///
@@ -2939,37 +2809,44 @@ Get =
when '[/banned]'
''
# greentext
- bq.innerHTML = bq.innerHTML.replace /(^|>)(>[^<$]+)(<|$)/g, '$1$2$3'
+ comment = bq.innerHTML.replace /(^|>)(>[^<$]+)(<|$)/g, '$1$2$3'
- # post container
- pc = $.el 'div',
- id: "pc#{postID}"
- className: "postContainer #{if isOP then 'op' else 'reply'}Container"
- innerHTML: ""
- $.add pc.firstChild, [piM, pi, bq]
+ o =
+ # id
+ postID: postID
+ threadID: data.thread_num
+ board: board
+ # info
+ name: data.name_processed
+ capcode: switch data.capcode
+ when 'M' then 'mod'
+ when 'A' then 'admin'
+ when 'D' then 'developer'
+ tripcode: data.trip
+ uniqueID: data.poster_hash
+ email: encodeURIComponent data.email
+ subject: data.title_processed
+ flagCode: data.poster_country
+ # XXX flagName: data.???_processed
+ date: data.fourchan_date
+ dateUTC: data.timestamp
+ comment: comment
+ # file
+ if data.media_filename
+ o.file =
+ name: data.media_filename_processed
+ timestamp: data.media_orig
+ url: data.media_link or data.remote_media_link
+ height: data.media_h
+ width: data.media_w
+ 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
+ isSpoiler: data.spoiler is '1'
- # file
- if filename = data.media_filename
- file = $.el 'div',
- id: "f#{postID}"
- className: 'file'
- spoiler = data.spoiler is '1'
- filesize = $.bytesToString data.media_size
- $.add file, $.el 'div',
- className: 'fileInfo'
- innerHTML: "File: #{data.media_orig}-(#{if spoiler then 'Spoiler Image, ' else ''}#{filesize}, #{data.media_w}x#{data.media_h}, )"
- span = $ 'span[title]', file
- span.title = filename
- span.textContent = $.shortenFilename filename, isOP
- thumb_src = if data.media_status is 'available' then "src=#{data.thumb_link}" else ''
- $.add file, $.el 'a',
- className: if spoiler then 'fileThumb imgspoiler' else 'fileThumb'
- href: data.media_link or data.remote_media_link
- target: '_blank'
- innerHTML: "
"
- $.after (if isOP then piM else pi), file
-
- $.replace root.firstChild, Get.cleanPost pc
+ $.replace root.firstChild, Get.cleanPost Build.post o, true
cb() if cb
cleanPost: (root) ->
post = $ '.post', root
@@ -3006,6 +2883,234 @@ Get =
span = $.el 'span', innerHTML: el.innerHTML.replace /
/g, ' '
"/#{g.BOARD}/ - #{span.textContent.trim()}"
+Build =
+ shortFilename: (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[-3..]}"
+ else
+ filename
+ postFromObject: (data, board) ->
+ o =
+ # id
+ postID: data.no
+ threadID: data.resto or data.no
+ board: board
+ # info
+ name: data.name
+ capcode: data.capcode
+ tripcode: data.trip
+ uniqueID: data.id
+ email: data.email.replace /\s/g, '%20' # UGH
+ subject: data.sub
+ flagCode: data.country
+ flagName: data.country_name
+ date: data.now
+ dateUTC: data.time
+ comment: data.com
+ # file
+ if data.ext
+ o.file =
+ name: data.filename + data.ext
+ timestamp: "#{data.tim}#{data.ext}"
+ url: "//images.4chan.org/#{board}/src/#{data.tim}#{data.ext}"
+ height: data.h
+ width: data.w
+ MD5: data.md5
+ size: data.fsize
+ turl: "//thumbs.4chan.org/#{board}/thumb/#{data.tim}s.jpg"
+ theight: data.tn_h
+ twidth: data.tn_w
+ isSpoiler: !!data.spoiler
+ isDeleted: !!data.filedeleted
+ Build.post o
+ post: (o, isArchived) ->
+ ###
+ This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS).
+ @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE
+ ###
+ {
+ postID, threadID, board
+ name, capcode, tripcode, uniqueID, email, subject, flagCode, flagName, date, dateUTC
+ comment
+ file
+ } = o
+ isOP = postID is threadID
+
+ staticPath = '//static.4chan.org'
+
+ if email
+ emailStart = ''
+ emailEnd = ''
+ else
+ emailStart = ''
+ emailEnd = ''
+
+ userID =
+ if !capcode and uniqueID
+ " (ID: " +
+ "#{uniqueID}) "
+ else
+ ''
+
+ switch capcode
+ when 'admin', 'admin_highlight'
+ capcodeClass = " capcodeAdmin"
+ capcodeStart = " ## Admin"
+ capcode = "
"
+ when 'mod'
+ capcodeClass = " capcodeMod"
+ capcodeStart = " ## Moderator"
+ capcode = "
"
+ when 'developer'
+ capcodeClass = " capcodeDeveloper"
+ capcodeStart = " ## Developer"
+ capcode = "
"
+ else
+ capcodeClass = ''
+ capcodeStart = ''
+ capcode = ''
+
+ flag =
+ if flagCode
+ "
"
+ else
+ ''
+
+ if file?.isDeleted
+ fileHTML =
+ if isOP
+ "" +
+ "
" +
+ " "
+ else
+ "" +
+ "
" +
+ " "
+ else if file
+ ext = file.name[-3..]
+ if !file.twidth and !file.theight and ext is 'gif' # wtf ?
+ file.twidth = file.width
+ file.theight = file.height
+
+ fileSize = $.bytesToString file.size
+
+ fileThumb = file.turl
+ if file.isSpoiler
+ fileSize = "Spoiler Image, #{fileSize}"
+ unless isArchived
+ fileThumb = '//thumbs.4chan.org/image/spoiler'
+ fileThumb += switch board
+ # UGGH, I can't wait to maintain this crap.
+ # Sup desuwa?
+ when 'a' then '-a'
+ when 'co' then '-co'
+ when 'jp' then '-jp'
+ when 'lit' then 'lit'
+ when 'm' then '-m2'
+ when 'mlp' then '-mlp'
+ when 'tg' then '-tg2'
+ when 'tv' then '-tv5'
+ when 'v', 'vg' then '-v'
+ when 'vp' then '-vp'
+ else ''
+ fileThumb += '.png'
+ file.twidth = file.theight = 100
+
+ imgSrc = "" +
+ "
"
+
+ fileDims = if ext is 'pdf' then 'PDF' else "#{file.width}x#{file.height}"
+ fileInfo = "File: #{file.timestamp}" +
+ "-(#{fileSize}, #{fileDims}#{
+ if file.isSpoiler
+ ''
+ else
+ ", #{Build.shortFilename file.name}'"
+ }" + ")"
+
+ fileHTML = ""
+ else
+ fileHTML = ''
+
+ tripcode =
+ if tripcode
+ " #{tripcode}"
+ else
+ ''
+
+ container = $.el 'div',
+ className: "postContainer #{if isOP then 'op' else 'reply'}Container"
+ id: "pc#{postID}"
+ innerHTML: \
+ (if isOP then '' else ">>
") +
+ "" +
+
+ "
" +
+ "
" +
+ emailStart +
+ "#{name}" + tripcode +
+ emailEnd + capcodeStart + capcode + userID + flag +
+ "
#{subject}" +
+ "#{date}" +
+ '
' +
+ "No." +
+ "#{postID}" +
+ '' +
+ '
' +
+
+ (if isOP then fileHTML else '') +
+
+ "
" +
+ "
" +
+ "
#{subject or ''} " +
+ "
" +
+ emailStart +
+ "#{name}" + tripcode +
+ emailEnd + capcodeStart + capcode + userID + flag +
+ ' ' +
+ "
#{date} " +
+ "
" +
+ "No." +
+ "#{postID}" +
+ '' +
+ '
' +
+
+ (if isOP then '' else fileHTML) +
+
+ "
#{comment}
" +
+
+ '
'
+
+ for quote in $$ '.quotelink', container
+ href = quote.getAttribute 'href'
+ continue if href[0] is '/' # Cross-board quote, or board link
+ quote.href = "/#{board}/res/#{href}" # Fix pathnames
+
+ container
TitlePost =
init: ->
d.title = Get.title()