Merge branch 'api' into v3

This commit is contained in:
Nicolas Stepien 2012-09-11 16:29:34 +02:00
commit ff1411d6ed
5 changed files with 484 additions and 288 deletions

View File

@ -8,11 +8,16 @@
// @match *://boards.4chan.org/* // @match *://boards.4chan.org/*
// @match *://images.4chan.org/* // @match *://images.4chan.org/*
// @match *://sys.4chan.org/* // @match *://sys.4chan.org/*
// @match *://api.4chan.org/*
// @match *://*.foolz.us/api/* // @match *://*.foolz.us/api/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_openInTab
// @run-at document-start // @run-at document-start
// @updateURL https://github.com/MayhemYDG/4chan-x/raw/stable/4chan_x.user.js // @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 // @downloadURL https://github.com/MayhemYDG/4chan-x/raw/stable/4chan_x.user.js
// @icon http://mayhemydg.github.com/4chan-x/favicon.gif // @icon data:image/gif;base64,R0lGODlhEAAQAKECAAAAAGbMM////////yH5BAEKAAIALAAAAAAQABAAAAIxlI+pq+D9DAgUoFkPDlbs7lGiI2bSVnKglnJMOL6omczxVZK3dH/41AG6Lh7i6qUoAAA7
// ==/UserScript== // ==/UserScript==
/* LICENSE /* LICENSE
@ -81,6 +86,7 @@
Config = { Config = {
main: { main: {
Enhancing: { Enhancing: {
'Disable 4chan\'s extension': [true, 'Avoid conflicts between 4chan X and 4chan\'s inline extension.'],
'404 Redirect': [true, 'Redirect dead threads and images.'], '404 Redirect': [true, 'Redirect dead threads and images.'],
'Keybinds': [true, 'Bind actions to keyboard shortcuts.'], 'Keybinds': [true, 'Bind actions to keyboard shortcuts.'],
'Time Formatting': [true, 'Localize and format timestamps arbitrarily.'], 'Time Formatting': [true, 'Localize and format timestamps arbitrarily.'],
@ -937,6 +943,9 @@
return (_ref2 = $.id('boardNavDesktopFoot')) != null ? _ref2.hidden = true : void 0; return (_ref2 = $.id('boardNavDesktopFoot')) != null ? _ref2.hidden = true : void 0;
}, },
initFeatures: function() { initFeatures: function() {
if (Conf['Disable 4chan\'s extension']) {
localStorage.setItem('4chan-settings', '{"disableAll":true}');
}
if (Conf['Resurrect Quotes']) { if (Conf['Resurrect Quotes']) {
try { try {
Quotify.init(); Quotify.init();
@ -1086,7 +1095,7 @@
return $.on(d, 'DOMNodeInserted', Main.addStyle); return $.on(d, 'DOMNodeInserted', Main.addStyle);
} }
}, },
css: "/* general */\n.dialog.reply {\n display: block;\n border: 1px solid rgba(0, 0, 0, .25);\n padding: 0;\n}\n.move {\n cursor: move;\n}\nlabel {\n cursor: pointer;\n}\na[href=\"javascript:;\"] {\n text-decoration: none;\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/* quote */\n.inlined {\n opacity: .5;\n}\n#qp input, .forwarded {\n display: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\n text-decoration: none;\n border-bottom: 1px dashed;\n}\n.inline {\n border: 1px solid rgba(128, 128, 128, .5);\n display: table;\n margin: 2px 0;\n}\n.inline .post {\n border: 0 !important;\n display: table !important;\n margin: 0 !important;\n padding: 1px 2px !important;\n}\n#qp {\n position: fixed;\n padding: 2px 2px 5px;\n}\n#qp .post {\n border: none;\n margin: 0;\n padding: 0;\n}\n#qp img {\n max-height: 300px;\n max-width: 500px;\n}\n.qphl {\n outline: 2px solid rgba(216, 94, 49, .7);\n}\n\n/* file */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull {\n display: none;\n}\n#ihover {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n max-height: 100%;\n max-width: 75%;\n position: fixed;\n padding-bottom: 16px;\n}" css: "/* general */\n.dialog.reply {\n display: block;\n border: 1px solid rgba(0, 0, 0, .25);\n padding: 0;\n}\n.move {\n cursor: move;\n}\nlabel {\n cursor: pointer;\n}\na[href=\"javascript:;\"] {\n text-decoration: none;\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/* quote */\n.quotelink.deadlink {\n text-decoration: underline !important;\n}\n.inlined {\n opacity: .5;\n}\n#qp input, .forwarded {\n display: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\n text-decoration: none;\n border-bottom: 1px dashed;\n}\n.inline {\n border: 1px solid rgba(128, 128, 128, .5);\n display: table;\n margin: 2px 0;\n}\n.inline .post {\n border: 0 !important;\n display: table !important;\n margin: 0 !important;\n padding: 1px 2px !important;\n}\n#qp {\n position: fixed;\n padding: 2px 2px 5px;\n}\n#qp .post {\n border: none;\n margin: 0;\n padding: 0;\n}\n#qp img {\n max-height: 300px;\n max-width: 500px;\n}\n.qphl {\n outline: 2px solid rgba(216, 94, 49, .7);\n}\n\n/* file */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull {\n display: none;\n}\n#ihover {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n max-height: 100%;\n max-width: 75%;\n position: fixed;\n padding-bottom: 16px;\n}"
}; };
Redirect = { Redirect = {
@ -1230,6 +1239,7 @@
}; };
Build = { Build = {
spoilerRange: {},
shortFilename: function(filename, isReply) { shortFilename: function(filename, isReply) {
var threshold; var threshold;
threshold = isReply ? 30 : 40; threshold = isReply ? 30 : 40;
@ -1239,120 +1249,139 @@
return filename; return filename;
} }
}, },
post: function(o) { postFromObject: function(data, board) {
var board, bq, capcode, comment, container, date, dateUTC, email, file, fl, flag, flagTitle, html, isOP, name, pi, post, postID, subject, threadID, tripcode, uniqueID; var o;
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; 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 ? encodeURIComponent(data.email) : '',
subject: data.sub,
flagCode: data.country,
flagName: data.country_name,
date: data.now,
dateUTC: data.time,
comment: data.com,
isSticky: !!data.sticky,
isClosed: !!data.closed
};
if (data.ext || data.filedeleted) {
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 a, board, capcode, capcodeClass, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, 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, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file;
isOP = postID === threadID; isOP = postID === threadID;
html = []; staticPath = '//static.4chan.org';
html.push("<input type=checkbox name=" + postID + " value=delete> ");
html.push("<span class=subject>" + subject + "</span> ");
html.push("<span class='nameBlock");
html.push((function() {
switch (capcode) {
case 'M':
return ' capcodeMod';
case 'A':
return ' capcodeAdmin';
case 'D':
return ' capcodeDeveloper';
default:
return '';
}
})());
html.push("'>");
if (email) { if (email) {
html.push("<a href=mailto:" + email + " class=useremail>"); emailStart = '<a href="mailto:' + email + '" class="useremail">';
} emailEnd = '</a>';
html.push("<span class=name>" + name + "</span>"); } else {
if (tripcode) { emailStart = '';
html.push(" <span class=postertrip>" + tripcode + "</span>"); emailEnd = '';
}
if (uniqueID) {
html.push(" <span class='posteruid id_" + uniqueID + "'>(ID: <span class=hand title='Highlight posts by this ID'>" + uniqueID + "</span>)</span>");
} }
subject = subject ? "<span class=subject>" + subject + "</span>" : '';
userID = !capcode && uniqueID ? (" <span class='posteruid id_" + uniqueID + "'>(ID: ") + ("<span class=hand title='Highlight posts by this ID'>" + uniqueID + "</span>)</span> ") : '';
switch (capcode) { switch (capcode) {
case 'M': case 'admin':
html.push(' <strong class="capcode hand id_mod" title="Highlight posts by Moderators">## Mod</strong>'); case 'admin_highlight':
html.push(' <img src=//static.4chan.org/image/modicon.gif alt="This user is a 4chan Moderator." title="This user is a 4chan Moderator." class=identityIcon>'); capcodeClass = " capcodeAdmin";
capcodeStart = " <strong class='capcode hand id_admin'" + "title='Highlight posts by the Administrator'>## Admin</strong>";
capcode = (" <img src='" + staticPath + "/image/adminicon.gif' ") + "alt='This user is the 4chan Administrator.' " + "title='This user is the 4chan Administrator.' class=identityIcon>";
break; break;
case 'A': case 'mod':
html.push(' <strong class="capcode hand id_admin" title="Highlight posts by the Administrator">## Admin</strong>'); capcodeClass = " capcodeMod";
html.push(' <img src=//static.4chan.org/image/adminicon.gif alt="This user is the 4chan Administrator." title="This user is the 4chan Administrator." class=identityIcon>'); capcodeStart = " <strong class='capcode hand id_mod' " + "title='Highlight posts by Moderators'>## Moderator</strong>";
capcode = (" <img src='" + staticPath + "/image/modicon.gif' ") + "alt='This user is a 4chan Moderator.' " + "title='This user is a 4chan Moderator.' class=identityIcon>";
break; break;
case 'D': case 'developer':
html.push(' <strong class="capcode hand id_mod" title="Highlight posts by Moderators">## Mod</strong>'); capcodeClass = " capcodeDeveloper";
html.push(' <img src=//static.4chan.org/image/developericon.gif alt="This user is a 4chan Developer." title="This user is a 4chan Developer." class=identityIcon>'); capcodeStart = " <strong class='capcode hand id_developer' " + "title='Highlight posts by Developers'>## Developer</strong>";
capcode = (" <img src='" + staticPath + "/image/developericon.gif' ") + "alt='This user is a 4chan Developer.' " + "title='This user is a 4chan Developer.' class=identityIcon>";
break;
default:
capcodeClass = '';
capcodeStart = '';
capcode = '';
} }
if (email) { flag = flagCode ? (" <img src='" + staticPath + "/image/country/" + (board === 'pol' ? 'troll/' : '')) + flagCode.toLowerCase() + (".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>") : '';
html.push('</a>'); if (file != null ? file.isDeleted : void 0) {
} fileHTML = isOP ? ("<div class=file id=f" + postID + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "/image/filedeleted.gif' alt='File deleted.'>") + "</span></div>" : ("<div id=f" + postID + " class=file><span class=fileThumb>") + ("<img src='" + staticPath + "/image/filedeleted-res.gif' alt='File deleted.'>") + "</span></div>";
if (flag) { } else if (file) {
html.push(" <img src=//static.4chan.org/image/country/" + (flag.toLowerCase()) + ".gif alt=" + flag + " title='" + flagTitle + "' class=countryFlag>"); ext = file.name.slice(-3);
} if (!file.twidth && !file.theight && ext === 'gif') {
html.push('</span> '); file.twidth = file.width;
html.push("<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span> "); file.theight = file.height;
html.push('<span class="postNum desktop">'); }
html.push("<a href=/" + board + "/res/" + threadID + "#p" + postID + " title='Highlight this post'>No.</a>"); fileSize = $.bytesToString(file.size);
html.push("<a href=\"" + (g.REPLY ? "javascript:quote('" + postID + "');" : "/" + board + "/res/" + threadID + "#q" + postID) + "\" title='Quote this post'>" + postID + "</a>"); fileThumb = file.turl;
html.push('</span>');
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('<div class=fileInfo>');
html.push("<span id=fT" + postID + " class=fileText" + (file.isSpoiler ? " title='file.name'" : '') + ">File: ");
html.push("<a href=" + file.url + " target=_blank>" + file.origin + "</a>");
html.push('-(');
if (file.isSpoiler) { if (file.isSpoiler) {
html.push('Spoiler Image, '); fileSize = "Spoiler Image, " + fileSize;
if (!isArchived) {
fileThumb = '//static.4chan.org/image/spoiler';
if (spoilerRange = Build.spoilerRange[board]) {
fileThumb += ("-" + board) + Math.floor(1 + spoilerRange * Math.random());
}
fileThumb += '.png';
file.twidth = file.theight = 100;
}
} }
html.push("" + ($.bytesToString(file.size)) + ", "); imgSrc = ("<a class='fileThumb" + (file.isSpoiler ? ' imgspoiler' : '') + "' href='" + file.url + "' target=_blank>") + ("<img src='" + fileThumb + "' alt='" + fileSize + "' data-md5=" + file.MD5 + " style='width:" + file.twidth + "px;height:" + file.theight + "px'></a>");
html.push(/\.pdf$/i.test(file.name) ? "PDF" : "" + file.width + "x" + file.height); a = $.el('a', {
if (!file.isSpoiler) { innerHTML: file.name
html.push(", <span title='" + file.name + "'>" + (Build.shortFilename(file.name)) + "</span>");
}
html.push(")</span></div>");
html.push("<a class='fileThumb" + (file.isSpoiler ? ' imgspoiler' : '') + "' href=" + file.url + " target=_blank>");
html.push("<img src=" + file.turl + " alt='" + (file.isSpoiler ? 'Spoiler Image, ' : '') + ($.bytesToString(file.size)) + "' data-md5='" + file.MD5 + "' style='height:" + file.theight + "px;width:" + file.twidth + "px'>");
html.push('</a>');
fl = $.el('div', {
id: "f" + postID,
className: 'file',
innerHTML: html.join('')
}); });
filename = a.textContent.replace(/%22/g, '"');
a.textContent = Build.shortFilename(filename);
shortFilename = a.innerHTML;
a.textContent = filename;
filename = a.innerHTML.replace(/'/g, '&apos;');
fileDims = ext === 'pdf' ? 'PDF' : "" + file.width + "x" + file.height;
fileInfo = ("<span class=fileText id=fT" + postID + (file.isSpoiler ? " title='" + filename + "'" : '') + ">File: <a href='" + file.url + "' target=_blank>" + file.timestamp + "</a>") + ("-(" + fileSize + ", " + fileDims + (file.isSpoiler ? '' : ", <span title='" + filename + "'>" + shortFilename + "</span>")) + ")</span>";
fileHTML = "<div id=f" + postID + " class=file><div class=fileInfo>" + fileInfo + "</div>" + imgSrc + "</div>";
} else {
fileHTML = '';
} }
post = $.el('div', { tripcode = tripcode ? " <span class=postertrip>" + tripcode + "</span>" : '';
id: "p" + postID, sticky = isSticky ? ' <img src=//static.4chan.org/image/sticky.gif alt=Sticky title=Sticky style="height:16px;width:16px">' : '';
className: "post " + (isOP ? 'op' : 'reply') closed = isClosed ? ' <img src=//static.4chan.org/image/closed.gif alt=Closed title=Closed style="height:16px;width:16px">' : '';
});
if (fl && isOP) {
$.add(post, fl);
}
$.add(post, pi);
if (fl && !isOP) {
$.add(post, fl);
}
$.add(post, bq);
container = $.el('div', { container = $.el('div', {
id: "pc" + postID, id: "pc" + postID,
className: "postContainer " + (isOP ? 'op' : 'reply') + "Container" className: "postContainer " + (isOP ? 'op' : 'reply') + "Container",
innerHTML: (isOP ? '' : "<div class=sideArrows id=sa" + postID + ">&gt;&gt;</div>") + ("<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'>") + ("<div class='postInfoM mobile' id=pim" + postID + ">") + ("<span class='nameBlock" + capcodeClass + "'>") + ("<span class=name>" + (name || '') + "</span>") + tripcode + capcodeStart + capcode + userID + flag + sticky + closed + ("<br>" + subject) + ("</span><span class='dateTime postNum' data-utc=" + dateUTC + ">" + date) + '<br><em>' + ("<a href=" + ("/" + board + "/res/" + threadID + "#p" + postID) + ">No.</a>") + ("<a href='" + (g.REPLY && g.THREAD_ID === threadID ? "javascript:quote(" + postID + ")" : "/" + board + "/res/" + threadID + "#q" + postID) + "'>" + postID + "</a>") + '</em></span>' + '</div>' + (isOP ? fileHTML : '') + ("<div class='postInfo desktop' id=pi" + postID + ">") + ("<input type=checkbox name=" + postID + " value=delete> ") + ("" + subject + " ") + ("<span class='nameBlock" + capcodeClass + "'>") + emailStart + ("<span class=name>" + (name || '') + "</span>") + tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed + ' </span> ' + ("<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span> ") + "<span class='postNum desktop'>" + ("<a href=" + ("/" + board + "/res/" + threadID + "#p" + postID) + " title='Highlight this post'>No.</a>") + ("<a href='" + (g.REPLY && g.THREAD_ID === threadID ? "javascript:quote(" + postID + ")" : "/" + board + "/res/" + threadID + "#q" + postID) + "' title='Quote this post'>" + postID + "</a>") + '</span>' + '</div>' + (isOP ? '' : fileHTML) + ("<blockquote class=postMessage id=m" + postID + ">" + (comment || '') + "</blockquote> ") + '</div>'
}); });
if (!isOP) { _ref = $$('.quotelink', container);
$.add(container, $.el('div', { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
id: "sa" + postID, quote = _ref[_i];
className: 'sideArrows', href = quote.getAttribute('href');
textContent: '>>' if (href[0] === '/') {
})); continue;
}
quote.href = "/" + board + "/res/" + href;
} }
$.add(container, post);
return container; return container;
} }
}; };
@ -1397,7 +1426,7 @@
} }
root.textContent = "Loading post No." + postID + "..."; root.textContent = "Loading post No." + postID + "...";
if (threadID) { if (threadID) {
return $.cache("/" + board + "/res/" + threadID, function() { return $.cache("//api.4chan.org/" + board + "/res/" + threadID + ".json", function() {
return Get.fetchedPost(this, board, threadID, postID, root, context); return Get.fetchedPost(this, board, threadID, postID, root, context);
}); });
} else if (url = Redirect.post(board, postID)) { } else if (url = Redirect.post(board, postID)) {
@ -1420,7 +1449,7 @@
return $.add(root, nodes.root); return $.add(root, nodes.root);
}, },
fetchedPost: function(req, board, threadID, postID, root, context) { fetchedPost: function(req, board, threadID, postID, root, context) {
var doc, href, link, pc, post, quote, status, thread, url, _i, _len, _ref; var post, posts, spoilerRange, status, thread, url, _i, _len;
if (post = g.posts["" + board + "." + postID]) { if (post = g.posts["" + board + "." + postID]) {
Get.insert(post, root, context); Get.insert(post, root, context);
return; return;
@ -1429,48 +1458,44 @@
if (status !== 200) { if (status !== 200) {
if (url = Redirect.post(board, postID)) { if (url = Redirect.post(board, postID)) {
$.cache(url, function() { $.cache(url, function() {
return Get.archivedPost(this, board, postID, root); return Get.archivedPost(this, board, postID, root, context);
}); });
} else { } else {
$.addClass(root, 'warning'); $.addClass(root, 'warning');
root.textContent = status === 404 ? "Thread No." + threadID + " has not been found." : "Error " + req.status + ": " + req.statusText + "."; root.textContent = status === 404 ? "Thread No." + threadID + " 404'd." : "Error " + req.status + ": " + req.statusText + ".";
} }
return; return;
} }
doc = d.implementation.createHTMLDocument(''); posts = JSON.parse(req.response).posts;
doc.documentElement.innerHTML = req.response; if (spoilerRange = posts[0].custom_spoiler) {
if (!(pc = doc.getElementById("pc" + postID))) { Build.spoilerRange[board] = spoilerRange;
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); postID = +postID;
_ref = $$('.quotelink', pc); for (_i = 0, _len = posts.length; _i < _len; _i++) {
for (_i = 0, _len = _ref.length; _i < _len; _i++) { post = posts[_i];
quote = _ref[_i]; if (post.no === postID) {
href = quote.getAttribute('href'); break;
if (href[0] === '/') { }
continue; if (post.no > postID) {
if (url = Redirect.post(board, postID)) {
$.cache(url, function() {
return Get.parseArchivedPost(this, board, postID, root, context);
});
} else {
$.addClass(root, 'warning');
root.textContent = "Post No." + postID + " was not 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;
board = g.boards[board] || new Board(board); board = g.boards[board] || new Board(board);
thread = g.threads["" + board + "." + threadID] || new Thread(threadID, board); thread = g.threads["" + board + "." + threadID] || new Thread(threadID, board);
post = new Post(pc, thread, board); post = new Post(Build.postFromObject(post, board), thread, board);
Main.callbackNodes(Post, [post]); Main.callbackNodes(Post, [post]);
return Get.insert(post, root, context); return Get.insert(post, root, context);
}, },
archivedPost: function(req, board, postID, root, context) { archivedPost: function(req, board, postID, root, context) {
var bq, comment, data, post, postContainer, thread, threadID; var bq, comment, data, o, post, thread, threadID;
if (post = g.posts["" + board + "." + postID]) { if (post = g.posts["" + board + "." + postID]) {
Get.insert(post, root, context); Get.insert(post, root, context);
return; return;
@ -1512,37 +1537,49 @@
}); });
comment = bq.innerHTML.replace(/(^|>)(&gt;[^<$]+)(<|$)/g, '$1<span class=quote>$2</span>$3'); comment = bq.innerHTML.replace(/(^|>)(&gt;[^<$]+)(<|$)/g, '$1<span class=quote>$2</span>$3');
threadID = data.thread_num; threadID = data.thread_num;
postContainer = Build.post({ o = {
postID: postID, postID: postID,
threadID: threadID, threadID: threadID,
board: board, board: board,
name: data.name, name: data.name_processed,
capcode: data.capcode, capcode: (function() {
switch (data.capcode) {
case 'M':
return 'mod';
case 'A':
return 'admin';
case 'D':
return 'developer';
}
})(),
tripcode: data.trip, tripcode: data.trip,
uniqueID: data.poster_hash, uniqueID: data.poster_hash,
email: data.email, email: encodeURIComponent(data.email),
subject: data.title, subject: data.title_processed,
flag: data.poster_country, flagCode: data.poster_country,
flagName: data.poster_country_name_processed,
date: data.fourchan_date, date: data.fourchan_date,
dateUTC: data.timestamp, dateUTC: data.timestamp,
comment: comment, comment: comment
file: { };
name: data.media_filename, if (data.media_filename) {
origin: data.media_orig, o.file = {
name: data.media_filename_processed,
timestamp: data.media_orig,
url: data.media_link || data.remote_media_link, url: data.media_link || data.remote_media_link,
height: data.media_h, height: data.media_h,
width: data.media_w, width: data.media_w,
isSpoiler: data.spoiler === '1',
MD5: data.media_hash, MD5: data.media_hash,
size: data.media_size, size: data.media_size,
turl: data.thumb_link || ("//thumbs.4chan.org/" + board + "/thumb/" + data.preview_orig), turl: data.thumb_link || ("//thumbs.4chan.org/" + board + "/thumb/" + data.preview_orig),
theight: data.preview_h, theight: data.preview_h,
twidth: data.preview_w twidth: data.preview_w,
} isSpoiler: data.spoiler === '1'
}); };
}
board = g.boards[board] || new Board(board); board = g.boards[board] || new Board(board);
thread = g.threads["" + board + "." + threadID] || new Thread(threadID, board); thread = g.threads["" + board + "." + threadID] || new Thread(threadID, board);
post = new Post(postContainer, thread, board, { post = new Post(Build.post(o, true), thread, board, {
isArchived: true isArchived: true
}); });
Main.callbackNodes(Post, [post]); Main.callbackNodes(Post, [post]);

View File

@ -19,11 +19,16 @@ HEADER = """
// @match *://boards.4chan.org/* // @match *://boards.4chan.org/*
// @match *://images.4chan.org/* // @match *://images.4chan.org/*
// @match *://sys.4chan.org/* // @match *://sys.4chan.org/*
// @match *://api.4chan.org/*
// @match *://*.foolz.us/api/* // @match *://*.foolz.us/api/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_openInTab
// @run-at document-start // @run-at document-start
// @updateURL https://github.com/MayhemYDG/4chan-x/raw/stable/4chan_x.user.js // @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 // @downloadURL https://github.com/MayhemYDG/4chan-x/raw/stable/4chan_x.user.js
// @icon http://mayhemydg.github.com/4chan-x/favicon.gif // @icon data:image/gif;base64,R0lGODlhEAAQAKECAAAAAGbMM////////yH5BAEKAAIALAAAAAAQABAAAAIxlI+pq+D9DAgUoFkPDlbs7lGiI2bSVnKglnJMOL6omczxVZK3dH/41AG6Lh7i6qUoAAA7
// ==/UserScript== // ==/UserScript==
/* LICENSE /* LICENSE

View File

@ -4,6 +4,22 @@ alpha
Fix Quote Backlinks not affecting inlined quotes. Fix Quote Backlinks not affecting inlined quotes.
Fix Quote Highlighting not affecting inlined quotes. Fix Quote Highlighting not affecting inlined quotes.
master
- Mayhem
Use 4chan's API to fetch posts for:
- Thread Updater.
- Quote Inlining.
- Quote Previewing.
- Thread Expansion.
- Comment Expansion.
This will make fetching faster, and reduce bandwidth usage.
Add an option to disable 4chan's inline extension. Enabled by default.
Fix compatibility with Scriptish's auto-udpater.
2.34.10
- Mayhem
Fix 4chan X. Blame moot.
2.34.9 2.34.9
- Mayhem - Mayhem
Add /g/, /k/, /w/, /an/, /cgl/, /ck/, /lit/, /toy/ and /x/ archived image redirection. Add /g/, /k/, /w/, /an/, /cgl/, /ck/, /lit/, /toy/ and /x/ archived image redirection.

View File

@ -1 +1 @@
postMessage({version:'2.34.9'},'*') postMessage({version:'2.34.10'},'*')

View File

@ -1,6 +1,7 @@
Config = Config =
main: main:
Enhancing: Enhancing:
'Disable 4chan\'s extension': [true, 'Avoid conflicts between 4chan X and 4chan\'s inline extension.']
'404 Redirect': [true, 'Redirect dead threads and images.'] '404 Redirect': [true, 'Redirect dead threads and images.']
'Keybinds': [true, 'Bind actions to keyboard shortcuts.'] 'Keybinds': [true, 'Bind actions to keyboard shortcuts.']
'Time Formatting': [true, 'Localize and format timestamps arbitrarily.'] 'Time Formatting': [true, 'Localize and format timestamps arbitrarily.']
@ -741,6 +742,9 @@ Main =
$.id('boardNavDesktopFoot')?.hidden = true $.id('boardNavDesktopFoot')?.hidden = true
initFeatures: -> initFeatures: ->
if Conf['Disable 4chan\'s extension']
localStorage.setItem '4chan-settings', '{"disableAll":true}'
if Conf['Resurrect Quotes'] if Conf['Resurrect Quotes']
try try
Quotify.init() Quotify.init()
@ -935,6 +939,9 @@ body.fourchan_x {
} }
/* quote */ /* quote */
.quotelink.deadlink {
text-decoration: underline !important;
}
.inlined { .inlined {
opacity: .5; opacity: .5;
} }
@ -1060,6 +1067,7 @@ Redirect =
url or '' url or ''
Build = Build =
spoilerRange: {}
shortFilename: (filename, isReply) -> shortFilename: (filename, isReply) ->
# FILENAME SHORTENING SCIENCE: # FILENAME SHORTENING SCIENCE:
# OPs have a +10 characters threshold. # OPs have a +10 characters threshold.
@ -1069,117 +1077,249 @@ Build =
"#{filename[...threshold - 5]}(...).#{filename[-3..]}" "#{filename[...threshold - 5]}(...).#{filename[-3..]}"
else else
filename filename
post: (o) -> 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: if data.email then encodeURIComponent data.email else ''
subject: data.sub
flagCode: data.country
flagName: data.country_name
date: data.now
dateUTC: data.time
comment: data.com
# thread status
isSticky: !!data.sticky
isClosed: !!data.closed
# file
if data.ext or data.filedeleted
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 postID, threadID, board
name, capcode, tripcode, uniqueID, email, subject, flag, flagTitle, date, dateUTC, comment name, capcode, tripcode, uniqueID, email, subject, flagCode, flagName, date, dateUTC
isSticky, isClosed
comment
file file
} = o } = o
isOP = postID is threadID isOP = postID is threadID
# post info staticPath = '//static.4chan.org'
html = []
# input if email
html.push "<input type=checkbox name=#{postID} value=delete> " emailStart = '<a href="mailto:' + email + '" class="useremail">'
# subject emailEnd = '</a>'
html.push "<span class=subject>#{subject}</span> " else
# name block emailStart = ''
html.push "<span class='nameBlock" emailEnd = ''
html.push switch capcode
when 'M' then ' capcodeMod' subject =
when 'A' then ' capcodeAdmin' if subject
when 'D' then ' capcodeDeveloper' "<span class=subject>#{subject}</span>"
else ''
html.push "'>"
# mail start
html.push "<a href=mailto:#{email} class=useremail>" if email
# name
html.push "<span class=name>#{name}</span>"
# tripcode
html.push " <span class=postertrip>#{tripcode}</span>" if tripcode
# user id
html.push " <span class='posteruid id_#{uniqueID}'>(ID: <span class=hand title='Highlight posts by this ID'>#{uniqueID}</span>)</span>" if uniqueID
# capcode
switch capcode
when 'M'
html.push ' <strong class="capcode hand id_mod" title="Highlight posts by Moderators">## Mod</strong>'
html.push ' <img src=//static.4chan.org/image/modicon.gif alt="This user is a 4chan Moderator." title="This user is a 4chan Moderator." class=identityIcon>'
when 'A'
html.push ' <strong class="capcode hand id_admin" title="Highlight posts by the Administrator">## Admin</strong>'
html.push ' <img src=//static.4chan.org/image/adminicon.gif alt="This user is the 4chan Administrator." title="This user is the 4chan Administrator." class=identityIcon>'
when 'D'
html.push ' <strong class="capcode hand id_mod" title="Highlight posts by Moderators">## Mod</strong>'
html.push ' <img src=//static.4chan.org/image/developericon.gif alt="This user is a 4chan Developer." title="This user is a 4chan Developer." class=identityIcon>'
# mail end
html.push '</a>' if email
# flag
# XXX what about troll flags in /pol/?
html.push " <img src=//static.4chan.org/image/country/#{flag.toLowerCase()}.gif alt=#{flag} title='#{flagTitle}' class=countryFlag>" if flag
# end name block
html.push '</span> '
# date
html.push "<span class=dateTime data-utc=#{dateUTC}>#{date}</span> "
# post num
html.push '<span class="postNum desktop">'
html.push "<a href=/#{board}/res/#{threadID}#p#{postID} title='Highlight this post'>No.</a>"
html.push "<a href=\"#{
if g.REPLY
"javascript:quote('#{postID}');"
else else
"/#{board}/res/#{threadID}#q#{postID}" ''
}\" title='Quote this post'>#{postID}</a>"
# XXX closed/sticky?
html.push '</span>'
pi = $.el 'div',
id: "pi#{postID}"
className: 'postInfo desktop'
innerHTML: html.join ''
bq = $.el 'blockquote', userID =
id: "m#{postID}" if !capcode and uniqueID
className: 'postMessage' " <span class='posteruid id_#{uniqueID}'>(ID: " +
innerHTML: comment "<span class=hand title='Highlight posts by this ID'>#{uniqueID}</span>)</span> "
else
''
# file switch capcode
if file.name # XXX need to fix support of Flash when 'admin', 'admin_highlight'
html = [] capcodeClass = " capcodeAdmin"
html.push '<div class=fileInfo>' capcodeStart = " <strong class='capcode hand id_admin'" +
html.push "<span id=fT#{postID} class=fileText#{if file.isSpoiler then " title='file.name'" else ''}>File: " "title='Highlight posts by the Administrator'>## Admin</strong>"
html.push "<a href=#{file.url} target=_blank>#{file.origin}</a>" capcode = " <img src='#{staticPath}/image/adminicon.gif' " +
html.push '-(' "alt='This user is the 4chan Administrator.' " +
html.push 'Spoiler Image, ' if file.isSpoiler "title='This user is the 4chan Administrator.' class=identityIcon>"
html.push "#{$.bytesToString file.size}, " when 'mod'
html.push if /\.pdf$/i.test file.name capcodeClass = " capcodeMod"
"PDF" capcodeStart = " <strong class='capcode hand id_mod' " +
"title='Highlight posts by Moderators'>## Moderator</strong>"
capcode = " <img src='#{staticPath}/image/modicon.gif' " +
"alt='This user is a 4chan Moderator.' " +
"title='This user is a 4chan Moderator.' class=identityIcon>"
when 'developer'
capcodeClass = " capcodeDeveloper"
capcodeStart = " <strong class='capcode hand id_developer' " +
"title='Highlight posts by Developers'>## Developer</strong>"
capcode = " <img src='#{staticPath}/image/developericon.gif' " +
"alt='This user is a 4chan Developer.' " +
"title='This user is a 4chan Developer.' class=identityIcon>"
else
capcodeClass = ''
capcodeStart = ''
capcode = ''
flag =
if flagCode
" <img src='#{staticPath}/image/country/#{if board is 'pol' then 'troll/' else ''}" +
flagCode.toLowerCase() + ".gif' alt=#{flagCode} title='#{flagName}' class=countryFlag>"
else
''
if file?.isDeleted
fileHTML =
if isOP
"<div class=file id=f#{postID}><div class=fileInfo></div><span class=fileThumb>" +
"<img src='#{staticPath}/image/filedeleted.gif' alt='File deleted.'>" +
"</span></div>"
else else
"#{file.width}x#{file.height}" "<div id=f#{postID} class=file><span class=fileThumb>" +
html.push ", <span title='#{file.name}'>#{Build.shortFilename file.name}</span>" unless file.isSpoiler "<img src='#{staticPath}/image/filedeleted-res.gif' alt='File deleted.'>" +
html.push ")</span></div>" "</span></div>"
html.push "<a class='fileThumb#{if file.isSpoiler then ' imgspoiler' else ''}' href=#{file.url} target=_blank>" else if file
html.push "<img src=#{file.turl} alt='#{if file.isSpoiler then 'Spoiler Image, ' else ''}#{$.bytesToString file.size}' data-md5='#{file.MD5}' style='height:#{file.theight}px;width:#{file.twidth}px'>" ext = file.name[-3..]
html.push '</a>' if !file.twidth and !file.theight and ext is 'gif' # wtf ?
fl = $.el 'div', file.twidth = file.width
id: "f#{postID}" file.theight = file.height
className: 'file'
innerHTML: html.join ''
post = $.el 'div', fileSize = $.bytesToString file.size
id: "p#{postID}"
className: "post #{if isOP then 'op' else 'reply'}" fileThumb = file.turl
$.add post, fl if fl and isOP if file.isSpoiler
$.add post, pi fileSize = "Spoiler Image, #{fileSize}"
$.add post, fl if fl and !isOP unless isArchived
$.add post, bq fileThumb = '//static.4chan.org/image/spoiler'
if spoilerRange = Build.spoilerRange[board]
# Randomize the spoiler image.
fileThumb += "-#{board}" + Math.floor 1 + spoilerRange * Math.random()
fileThumb += '.png'
file.twidth = file.theight = 100
imgSrc = "<a class='fileThumb#{if file.isSpoiler then ' imgspoiler' else ''}' href='#{file.url}' target=_blank>" +
"<img src='#{fileThumb}' alt='#{fileSize}' data-md5=#{file.MD5} style='width:#{file.twidth}px;height:#{file.theight}px'></a>"
# Ha Ha filenames.
# html -> text, translate WebKit's %22s into "s
a = $.el 'a', innerHTML: file.name
filename = a.textContent.replace /%22/g, '"'
# shorten filename, get html
a.textContent = Build.shortFilename filename
shortFilename = a.innerHTML
# get html
a.textContent = filename
filename = a.innerHTML.replace /'/g, '&apos;'
fileDims = if ext is 'pdf' then 'PDF' else "#{file.width}x#{file.height}"
fileInfo = "<span class=fileText id=fT#{postID}#{if file.isSpoiler then " title='#{filename}'" else ''}>File: <a href='#{file.url}' target=_blank>#{file.timestamp}</a>" +
"-(#{fileSize}, #{fileDims}#{
if file.isSpoiler
''
else
", <span title='#{filename}'>#{shortFilename}</span>"
}" + ")</span>"
fileHTML = "<div id=f#{postID} class=file><div class=fileInfo>#{fileInfo}</div>#{imgSrc}</div>"
else
fileHTML = ''
tripcode =
if tripcode
" <span class=postertrip>#{tripcode}</span>"
else
''
sticky =
if isSticky
' <img src=//static.4chan.org/image/sticky.gif alt=Sticky title=Sticky style="height:16px;width:16px">'
else
''
closed =
if isClosed
' <img src=//static.4chan.org/image/closed.gif alt=Closed title=Closed style="height:16px;width:16px">'
else
''
container = $.el 'div', container = $.el 'div',
id: "pc#{postID}" id: "pc#{postID}"
className: "postContainer #{if isOP then 'op' else 'reply'}Container" className: "postContainer #{if isOP then 'op' else 'reply'}Container"
unless isOP innerHTML: \
$.add container, $.el 'div', (if isOP then '' else "<div class=sideArrows id=sa#{postID}>&gt;&gt;</div>") +
id: "sa#{postID}" "<div id=p#{postID} class='post #{if isOP then 'op' else 'reply'}#{
className: 'sideArrows' if capcode is 'admin_highlight'
textContent: '>>' ' highlightPost'
$.add container, post else
''
}'>" +
"<div class='postInfoM mobile' id=pim#{postID}>" +
"<span class='nameBlock#{capcodeClass}'>" +
"<span class=name>#{name or ''}</span>" + tripcode +
capcodeStart + capcode + userID + flag + sticky + closed +
"<br>#{subject}" +
"</span><span class='dateTime postNum' data-utc=#{dateUTC}>#{date}" +
'<br><em>' +
"<a href=#{"/#{board}/res/#{threadID}#p#{postID}"}>No.</a>" +
"<a href='#{
if g.REPLY and g.THREAD_ID is threadID
"javascript:quote(#{postID})"
else
"/#{board}/res/#{threadID}#q#{postID}"
}'>#{postID}</a>" +
'</em></span>' +
'</div>' +
(if isOP then fileHTML else '') +
"<div class='postInfo desktop' id=pi#{postID}>" +
"<input type=checkbox name=#{postID} value=delete> " +
"#{subject} " +
"<span class='nameBlock#{capcodeClass}'>" +
emailStart +
"<span class=name>#{name or ''}</span>" + tripcode +
capcodeStart + emailEnd + capcode + userID + flag + sticky + closed +
' </span> ' +
"<span class=dateTime data-utc=#{dateUTC}>#{date}</span> " +
"<span class='postNum desktop'>" +
"<a href=#{"/#{board}/res/#{threadID}#p#{postID}"} title='Highlight this post'>No.</a>" +
"<a href='#{
if g.REPLY and g.THREAD_ID is threadID
"javascript:quote(#{postID})"
else
"/#{board}/res/#{threadID}#q#{postID}"
}' title='Quote this post'>#{postID}</a>" +
'</span>' +
'</div>' +
(if isOP then '' else fileHTML) +
"<blockquote class=postMessage id=m#{postID}>#{comment or ''}</blockquote> " +
'</div>'
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 container
@ -1213,7 +1353,7 @@ Get =
root.textContent = "Loading post No.#{postID}..." root.textContent = "Loading post No.#{postID}..."
if threadID if threadID
$.cache "/#{board}/res/#{threadID}", -> $.cache "//api.4chan.org/#{board}/res/#{threadID}.json", ->
Get.fetchedPost @, board, threadID, postID, root, context Get.fetchedPost @, board, threadID, postID, root, context
else if url = Redirect.post board, postID else if url = Redirect.post board, postID
$.cache url, -> $.cache url, ->
@ -1242,43 +1382,37 @@ Get =
# The thread can die by the time we check a quote. # The thread can die by the time we check a quote.
if url = Redirect.post board, postID if url = Redirect.post board, postID
$.cache url, -> $.cache url, ->
Get.archivedPost @, board, postID, root Get.archivedPost @, board, postID, root, context
else else
$.addClass root, 'warning' $.addClass root, 'warning'
root.textContent = root.textContent =
if status is 404 if status is 404
"Thread No.#{threadID} has not been found." "Thread No.#{threadID} 404'd."
else else
"Error #{req.status}: #{req.statusText}." "Error #{req.status}: #{req.statusText}."
return return
doc = d.implementation.createHTMLDocument '' posts = JSON.parse(req.response).posts
doc.documentElement.innerHTML = req.response if spoilerRange = posts[0].custom_spoiler
Build.spoilerRange[board] = spoilerRange
unless pc = doc.getElementById "pc#{postID}" postID = +postID
# The post can be deleted by the time we check a quote. for post in posts
if url = Redirect.post board, postID break if post.no is postID # we found it!
$.cache url, -> if post.no > postID
Get.archivedPost @, board, postID, root # The post can be deleted by the time we check a quote.
else if url = Redirect.post board, postID
$.addClass root, 'warning' $.cache url, ->
root.textContent = "Post No.#{postID} has not been found." Get.parseArchivedPost @, board, postID, root, context
return else
pc = d.importNode pc, true $.addClass root, 'warning'
root.textContent = "Post No.#{postID} was not found."
for quote in $$ '.quotelink', pc return
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}"
board = g.boards[board] or board = g.boards[board] or
new Board board new Board board
thread = g.threads["#{board}.#{threadID}"] or thread = g.threads["#{board}.#{threadID}"] or
new Thread threadID, board new Thread threadID, board
post = new Post pc, thread, board post = new Post Build.postFromObject(post, board), thread, board
Main.callbackNodes Post, [post] Main.callbackNodes Post, [post]
Get.insert post, root, context Get.insert post, root, context
archivedPost: (req, board, postID, root, context) -> archivedPost: (req, board, postID, root, context) ->
@ -1332,42 +1466,46 @@ Get =
comment = bq.innerHTML.replace /(^|>)(&gt;[^<$]+)(<|$)/g, '$1<span class=quote>$2</span>$3' comment = bq.innerHTML.replace /(^|>)(&gt;[^<$]+)(<|$)/g, '$1<span class=quote>$2</span>$3'
threadID = data.thread_num threadID = data.thread_num
postContainer = Build.post o =
# id # id
postID: postID postID: postID
threadID: threadID threadID: threadID
board: board board: board
# info # info
name: data.name name: data.name_processed
capcode: data.capcode capcode: switch data.capcode
tripcode: data.trip when 'M' then 'mod'
uniqueID: data.poster_hash when 'A' then 'admin'
email: data.email when 'D' then 'developer'
subject: data.title tripcode: data.trip
flag: data.poster_country uniqueID: data.poster_hash
# XXX flagTitle: data.??? email: encodeURIComponent data.email
date: data.fourchan_date subject: data.title_processed
dateUTC: data.timestamp flagCode: data.poster_country
comment: comment flagName: data.poster_country_name_processed
date: data.fourchan_date
dateUTC: data.timestamp
comment: comment
# file # file
file: if data.media_filename
name: data.media_filename o.file =
origin: data.media_orig name: data.media_filename_processed
timestamp: data.media_orig
url: data.media_link or data.remote_media_link url: data.media_link or data.remote_media_link
height: data.media_h height: data.media_h
width: data.media_w width: data.media_w
isSpoiler: data.spoiler is '1'
MD5: data.media_hash MD5: data.media_hash
size: data.media_size size: data.media_size
turl: data.thumb_link or "//thumbs.4chan.org/#{board}/thumb/#{data.preview_orig}" turl: data.thumb_link or "//thumbs.4chan.org/#{board}/thumb/#{data.preview_orig}"
theight: data.preview_h theight: data.preview_h
twidth: data.preview_w twidth: data.preview_w
isSpoiler: data.spoiler is '1'
board = g.boards[board] or board = g.boards[board] or
new Board board new Board board
thread = g.threads["#{board}.#{threadID}"] or thread = g.threads["#{board}.#{threadID}"] or
new Thread threadID, board new Thread threadID, board
post = new Post postContainer, thread, board, post = new Post Build.post(o, true), thread, board,
isArchived: true isArchived: true
Main.callbackNodes Post, [post] Main.callbackNodes Post, [post]
Get.insert post, root, context Get.insert post, root, context