diff --git a/4chan_x.user.js b/4chan_x.user.js
index 45d3af7b6..5b8131429 100644
--- a/4chan_x.user.js
+++ b/4chan_x.user.js
@@ -72,7 +72,7 @@
*/
(function() {
- var $, $$, Anonymize, AutoGif, Conf, Config, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, GetTitle, ImageExpand, ImageHover, Keybinds, Main, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Threading, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base;
+ var $, $$, Anonymize, AutoGif, Conf, Config, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, GetTitle, ImageExpand, ImageHover, Keybinds, Main, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base;
Config = {
main: {
@@ -153,8 +153,7 @@
sauces: ['http://iqdb.org/?url=$1', 'http://www.google.com/searchbyimage?image_url=$1', '#http://tineye.com/search?url=$1', '#http://saucenao.com/search.php?db=999&url=$1', '#http://3d.iqdb.org/?url=$1', '#http://regex.info/exif.cgi?imgurl=$2', '# uploaders:', '#http://imgur.com/upload?url=$2', '#http://omploader.org/upload?url1=$2', '# "View Same" in archives:', '#http://archive.foolz.us/$4/image/$3/', '#https://archive.installgentoo.net/$4/image/$3'].join('\n'),
time: '%m/%d/%y(%a)%H:%M',
backlink: '>>%id',
- fileInfoR: '%l (%s, %r)',
- fileInfoT: '%l (%s, %r)',
+ fileInfo: '%l (%p%s, %r)',
favicon: 'ferongr',
hotkeys: {
openQR: ['i', 'Open QR with post number inserted'],
@@ -217,22 +216,20 @@
rect = el.getBoundingClientRect();
UI.dx = e.clientX - rect.left;
UI.dy = e.clientY - rect.top;
- UI.width = d.body.clientWidth - el.offsetWidth;
- return UI.height = d.body.clientHeight - el.offsetHeight;
+ UI.width = d.documentElement.clientWidth - rect.width;
+ return UI.height = d.documentElement.clientHeight - rect.height;
},
drag: function(e) {
- var bottom, left, right, style, top;
+ var left, style, top;
left = e.clientX - UI.dx;
top = e.clientY - UI.dy;
- left = left < 10 ? 0 : UI.width - left < 10 ? null : left;
- top = top < 10 ? 0 : UI.height - top < 10 ? null : top;
- right = left === null ? 0 : null;
- bottom = top === null ? 0 : null;
+ left = left < 10 ? '0px' : UI.width - left < 10 ? null : left + 'px';
+ top = top < 10 ? '0px' : UI.height - top < 10 ? null : top + 'px';
style = UI.el.style;
+ style.left = left;
style.top = top;
- style.right = right;
- style.bottom = bottom;
- return style.left = left;
+ style.right = left === null ? '0px' : null;
+ return style.bottom = top === null ? '0px' : null;
},
dragend: function() {
var el;
@@ -242,20 +239,19 @@
return d.removeEventListener('mouseup', UI.dragend, false);
},
hover: function(e) {
- var clientHeight, clientWidth, clientX, clientY, el, height, style, top, _ref;
+ var clientHeight, clientWidth, clientX, clientY, height, style, top, _ref;
clientX = e.clientX, clientY = e.clientY;
- el = UI.el;
- style = el.style;
- _ref = d.body, clientHeight = _ref.clientHeight, clientWidth = _ref.clientWidth;
- height = el.offsetHeight;
+ style = UI.el.style;
+ _ref = d.documentElement, clientHeight = _ref.clientHeight, clientWidth = _ref.clientWidth;
+ height = UI.el.offsetHeight;
top = clientY - 120;
- style.top = clientHeight <= height || top <= 0 ? 0 : top + height >= clientHeight ? clientHeight - height : top;
+ style.top = clientHeight <= height || top <= 0 ? '0px' : top + height >= clientHeight ? clientHeight - height + 'px' : top + 'px';
if (clientX <= clientWidth - 400) {
- style.left = clientX + 45;
+ style.left = clientX + 45 + 'px';
return style.right = null;
} else {
style.left = null;
- return style.right = clientWidth - clientX + 45;
+ return style.right = clientWidth - clientX + 45 + 'px';
}
},
hoverend: function() {
@@ -617,12 +613,12 @@
};
},
node: function(post) {
- var el, filter, firstThread, isOP, key, result, thisThread, value, _i, _len, _ref;
+ var filter, firstThread, isOP, key, result, root, thisThread, value, _i, _len, _ref;
if (post.isInlined) {
return;
}
- post.isOP = post["class"] === 'op';
- isOP = post.isOP, el = post.el;
+ isOP = post.id === post.threadId;
+ root = post.root;
for (key in Filter.filters) {
value = Filter[key](post);
if (value === false) {
@@ -637,38 +633,32 @@
if (result === true) {
if (isOP) {
if (!g.REPLY) {
- ThreadHiding.hide(post.el.parentNode);
+ ThreadHiding.hide(root.parentNode);
} else {
continue;
}
} else {
- ReplyHiding.hide(post.root);
+ ReplyHiding.hide(root);
}
return;
}
- if (isOP) {
- $.addClass(el, result[0]);
- } else {
- $.addClass(el.parentNode, result[0]);
- }
+ $.addClass((isOP ? root.parentNode : root), result[0]);
if (isOP && result[1] && !g.REPLY) {
- thisThread = el.parentNode;
- if (firstThread = $('div[class=op]')) {
- $.before(firstThread.parentNode, [thisThread, thisThread.nextElementSibling]);
+ thisThread = root.parentNode;
+ if (firstThread = $('div[class=thread]')) {
+ $.before(firstThread, [thisThread, thisThread.nextElementSibling]);
}
}
}
}
},
name: function(post) {
- var name;
- name = post.isOP ? $('.postername', post.el) : $('.commentpostername', post.el);
- return name.textContent;
+ return $('.name', post.el).textContent;
},
uniqueid: function(post) {
var uid;
if (uid = $('.posteruid', post.el)) {
- return uid.textContent;
+ return uid.textContent.slice(5, -1);
}
return false;
},
@@ -681,44 +671,42 @@
},
mod: function(post) {
var mod;
- if (mod = (post.isOP ? $('.commentpostername', post.el) : $('.commentpostername ~ .commentpostername', post.el))) {
+ if (mod = $('.capcode', post.el)) {
return mod.textContent;
}
return false;
},
email: function(post) {
var mail;
- if (mail = $('.linkmail', post.el)) {
- return mail.href;
+ if (mail = $('.useremail', post.el)) {
+ return mail.pathname;
}
return false;
},
subject: function(post) {
- var sub;
- sub = post.isOP ? $('.filetitle', post.el) : $('.replytitle', post.el);
- return sub.textContent;
+ return $('.subject', post.el).textContent || false;
},
comment: function(post) {
var data, i, nodes, text, _i, _ref;
text = [];
- nodes = d.evaluate('.//br|.//text()', post.el.lastChild, null, 7, null);
+ nodes = d.evaluate('.//br|.//text()', post.el.lastElementChild, null, 7, null);
for (i = _i = 0, _ref = nodes.snapshotLength; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
text.push((data = nodes.snapshotItem(i).data) ? data : '\n');
}
return text.join('');
},
filename: function(post) {
- var file, filesize;
- filesize = post.filesize;
- if (filesize && (file = $('span', filesize))) {
+ var file, fileInfo;
+ fileInfo = post.fileInfo;
+ if (fileInfo && (file = $('.fileText > span', fileInfo))) {
return file.title;
}
return false;
},
dimensions: function(post) {
- var filesize, match;
- filesize = post.filesize;
- if (filesize && (match = filesize.textContent.match(/\d+x\d+/))) {
+ var fileInfo, match;
+ fileInfo = post.fileInfo;
+ if (fileInfo && (match = fileInfo.textContent.match(/\d+x\d+/))) {
return match[0];
}
return false;
@@ -735,7 +723,7 @@
var img;
img = post.img;
if (img) {
- return img.getAttribute('md5');
+ return img.dataset.md5;
}
return false;
}
@@ -753,7 +741,7 @@
_ref = post.quotes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quote = _ref[_i];
- if ((el = $.id(quote.hash.slice(1))) && el.parentNode.parentNode.parentNode.hidden) {
+ if ((el = $.id(quote.hash.slice(1))) && el.hidden) {
$.addClass(quote, 'filtered');
if (Conf['Recursive Filtering']) {
ReplyHiding.hide(post.root);
@@ -766,22 +754,21 @@
ExpandComment = {
init: function() {
var a, _i, _len, _ref;
- _ref = $$('.abbr > a');
+ _ref = $$('.abbr');
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
a = _ref[_i];
- $.on(a, 'click', ExpandComment.expand);
+ $.on(a.firstElementChild, 'click', ExpandComment.expand);
}
},
expand: function(e) {
var a, replyID, threadID, _, _ref;
e.preventDefault();
- _ref = this.href.match(/(\d+)#(\d+)/), _ = _ref[0], threadID = _ref[1], replyID = _ref[2];
+ _ref = this.href.match(/(\d+)#p(\d+)/), _ = _ref[0], threadID = _ref[1], replyID = _ref[2];
this.textContent = "Loading " + replyID + "...";
- threadID = this.pathname.split('/').pop() || $.x('ancestor::div[@class="thread"]/div', this).id;
a = this;
- return $.cache(this.pathname, (function() {
+ return $.cache(this.pathname, function() {
return ExpandComment.parse(this, a, threadID, replyID);
- }));
+ });
},
parse: function(req, a, threadID, replyID) {
var doc, href, node, post, quote, quotes, _i, _len;
@@ -791,16 +778,15 @@
}
doc = d.implementation.createHTMLDocument('');
doc.documentElement.innerHTML = req.response;
- Threading.op($('body > form', doc).firstChild);
- node = d.importNode(doc.getElementById(replyID));
+ node = d.importNode(doc.getElementById("m" + replyID));
quotes = node.getElementsByClassName('quotelink');
for (_i = 0, _len = quotes.length; _i < _len; _i++) {
quote = quotes[_i];
- if (quote.hash === (href = quote.getAttribute('href'))) {
- quote.pathname = "/" + g.BOARD + "/res/" + threadID;
- } else if (href !== quote.href) {
- quote.href = "res/" + href;
+ href = quote.getAttribute('href');
+ if (href[0] === '/') {
+ continue;
}
+ quote.href = "res/" + href;
}
post = {
el: node,
@@ -823,51 +809,44 @@
if (Conf['Indicate Cross-thread Quotes']) {
QuoteCT.node(post);
}
- return $.replace(a.parentNode.parentNode, node.lastChild);
+ return $.replace(a.parentNode.parentNode, node);
}
};
ExpandThread = {
init: function() {
var a, span, _i, _len, _ref, _results;
- _ref = $$('.omittedposts');
+ _ref = $$('.summary');
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
span = _ref[_i];
a = $.el('a', {
textContent: "+ " + span.textContent,
- className: 'omittedposts',
+ className: 'summary desktop',
href: 'javascript:;'
});
- $.on(a, 'click', ExpandThread.cb.toggle);
+ $.on(a, 'click', function() {
+ return ExpandThread.toggle(this.parentNode);
+ });
_results.push($.replace(span, a));
}
return _results;
},
- cb: {
- toggle: function() {
- var thread;
- thread = this.parentNode;
- return ExpandThread.toggle(thread);
- }
- },
toggle: function(thread) {
- var a, backlink, num, pathname, prev, table, threadID, _i, _len, _ref, _ref1, _results;
- threadID = thread.firstChild.id;
- pathname = "/" + g.BOARD + "/res/" + threadID;
- a = $('.omittedposts', thread);
+ var a, backlink, num, pathname, replies, reply, _i, _j, _len, _len1, _ref;
+ pathname = "/" + g.BOARD + "/res/" + thread.id.slice(1);
+ a = $('.summary', thread);
switch (a.textContent[0]) {
case '+':
- if ((_ref = $('.op .container', thread)) != null) {
- _ref.textContent = '';
- }
a.textContent = a.textContent.replace('+', '\u00d7 Loading...');
- return $.cache(pathname, (function() {
- return ExpandThread.parse(this, pathname, thread, a);
- }));
+ $.cache(pathname, function() {
+ return ExpandThread.parse(this, thread, a);
+ });
+ break;
case '\u00d7':
a.textContent = a.textContent.replace('\u00d7 Loading...', '+');
- return $.cache.requests[pathname].abort();
+ $.cache.requests[pathname].abort();
+ break;
case '-':
a.textContent = a.textContent.replace('-', '+');
num = (function() {
@@ -881,25 +860,23 @@
return 5;
}
})();
- table = $.x("following::br[@clear]/preceding::table[" + num + "]", a);
- while ((prev = table.previousSibling) && (prev.nodeName !== 'A')) {
- $.rm(prev);
+ replies = $$('.replyContainer', thread);
+ replies.splice(replies.length - num, num);
+ for (_i = 0, _len = replies.length; _i < _len; _i++) {
+ reply = replies[_i];
+ $.rm(reply);
}
- _ref1 = $$('.backlink', $('.op', thread));
- _results = [];
- for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
- backlink = _ref1[_i];
+ _ref = $$('.backlink', a.previousElementSibling);
+ for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
+ backlink = _ref[_j];
if (!$.id(backlink.hash.slice(1))) {
- _results.push($.rm(backlink));
- } else {
- _results.push(void 0);
+ $.rm(backlink);
}
}
- return _results;
}
},
- parse: function(req, pathname, thread, a) {
- var doc, href, link, next, nodes, quote, reply, table, _i, _j, _len, _len1, _ref, _ref1;
+ parse: function(req, thread, a) {
+ var backlink, doc, href, id, link, nodes, post, quote, reply, threadID, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3;
if (req.status !== 200) {
a.textContent = "" + req.status + " " + req.statusText;
$.off(a, 'click', ExpandThread.cb.toggle);
@@ -908,97 +885,180 @@
a.textContent = a.textContent.replace('\u00d7 Loading...', '-');
doc = d.implementation.createHTMLDocument('');
doc.documentElement.innerHTML = req.response;
+ threadID = thread.id.slice(1);
nodes = [];
- _ref = $$('.reply', doc);
+ _ref = $$('.replyContainer', doc);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
reply = _ref[_i];
- table = d.importNode(reply.parentNode.parentNode.parentNode);
- _ref1 = $$('.quotelink', table);
+ reply = d.importNode(reply);
+ _ref1 = $$('.quotelink', reply);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
quote = _ref1[_j];
- if (quote.hash === (href = quote.getAttribute('href'))) {
- quote.pathname = pathname;
- } else if (href !== quote.href) {
- quote.href = "res/" + href;
+ href = quote.getAttribute('href');
+ if (href[0] === '/') {
+ continue;
}
+ quote.href = "res/" + href;
}
- link = $('.quotejs', table);
- link.href = "res/" + thread.firstChild.id + "#" + reply.id;
- link.nextSibling.href = "res/" + thread.firstChild.id + "#q" + reply.id;
- nodes.push(table);
+ id = reply.id.slice(2);
+ link = $('.postInfo > .postNum > a:first-child', reply);
+ link.href = "res/" + threadID + "#p" + id;
+ link.nextSibling.href = "res/" + threadID + "#q" + id;
+ nodes.push(reply);
}
- while ((next = a.nextSibling) && !next.clear) {
- $.rm(next);
+ _ref2 = $$('.summary ~ .replyContainer', a.parentNode);
+ for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
+ post = _ref2[_k];
+ $.rm(post);
}
- return $.before(next, nodes);
+ _ref3 = $$('.backlink', a.previousElementSibling);
+ for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
+ backlink = _ref3[_l];
+ if (!$.id(backlink.hash.slice(1))) {
+ $.rm(backlink);
+ }
+ }
+ return $.after(a, nodes);
+ }
+ };
+
+ ThreadHiding = {
+ init: function() {
+ var a, hiddenThreads, thread, _i, _len, _ref;
+ hiddenThreads = $.get("hiddenThreads/" + g.BOARD + "/", {});
+ _ref = $$('.thread');
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ thread = _ref[_i];
+ a = $.el('a', {
+ className: 'hide_thread_button',
+ innerHTML: '[ - ]',
+ href: 'javascript:;'
+ });
+ $.on(a, 'click', ThreadHiding.cb);
+ $.prepend(thread, a);
+ if (thread.id.slice(1) in hiddenThreads) {
+ ThreadHiding.hide(thread);
+ }
+ }
+ },
+ cb: function() {
+ return ThreadHiding.toggle(this.parentNode);
+ },
+ toggle: function(thread) {
+ var hiddenThreads, id;
+ hiddenThreads = $.get("hiddenThreads/" + g.BOARD + "/", {});
+ id = thread.id.slice(1);
+ if (thread.hidden || /\bhidden_thread\b/.test(thread.firstChild.className)) {
+ ThreadHiding.show(thread);
+ delete hiddenThreads[id];
+ } else {
+ ThreadHiding.hide(thread);
+ hiddenThreads[id] = Date.now();
+ }
+ return $.set("hiddenThreads/" + g.BOARD + "/", hiddenThreads);
+ },
+ hide: function(thread) {
+ var a, num, opInfo, span, text;
+ if (!Conf['Show Stubs']) {
+ thread.hidden = true;
+ thread.nextElementSibling.hidden = true;
+ return;
+ }
+ if (thread.firstChild.className === 'block') {
+ return;
+ }
+ num = 0;
+ if (span = $('.summary', thread)) {
+ num = Number(span.textContent.match(/\d+/));
+ }
+ num += $$('.opContainer ~ .replyContainer', thread).length;
+ text = num === 1 ? '1 reply' : "" + num + " replies";
+ opInfo = $('.op > .postInfo > .nameBlock', thread).textContent;
+ a = $('.hide_thread_button', thread);
+ $.addClass(a, 'hidden_thread');
+ a.firstChild.textContent = '[ + ]';
+ return $.add(a, $.tn(" " + opInfo + " (" + text + ")"));
+ },
+ show: function(thread) {
+ var a;
+ a = $('.hide_thread_button', thread);
+ $.removeClass(a, 'hidden_thread');
+ a.innerHTML = '[ - ]';
+ thread.hidden = false;
+ return thread.nextElementSibling.hidden = false;
}
};
ReplyHiding = {
init: function() {
- this.td = $.el('td', {
- noWrap: true,
- className: 'replyhider',
- innerHTML: '[ - ]'
- });
return Main.callbacks.push(this.node);
},
node: function(post) {
- var td;
- if (post["class"]) {
+ var button;
+ if (post.isInlined || /\bop\b/.test(post["class"])) {
return;
}
- td = ReplyHiding.td.cloneNode(true);
- $.on(td.firstChild, 'click', ReplyHiding.toggle);
- $.replace(post.el.previousSibling, td);
+ button = post.root.firstElementChild;
+ $.addClass(button, 'hide_reply_button');
+ button.innerHTML = '[ - ]';
+ $.on(button.firstChild, 'click', ReplyHiding.toggle);
if (post.id in g.hiddenReplies) {
return ReplyHiding.hide(post.root);
}
},
toggle: function() {
- var id, parent, quote, table, _i, _j, _len, _len1, _ref, _ref1;
- parent = this.parentNode;
- if (parent.className === 'replyhider') {
- ReplyHiding.hide(parent.parentNode.parentNode.parentNode);
- id = parent.nextSibling.id;
- _ref = $$(".quotelink[href='#" + id + "'], .backlink[href='#" + id + "']");
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- quote = _ref[_i];
- $.addClass(quote, 'filtered');
- }
- g.hiddenReplies[id] = Date.now();
- } else {
- table = parent.nextSibling;
- table.hidden = false;
- $.rm(parent);
- id = table.firstChild.firstChild.lastChild.id;
- _ref1 = $$(".quotelink[href$='#" + id + "'], .backlink[href='#" + id + "']");
- for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
- quote = _ref1[_j];
+ var button, id, quote, quotes, root, _i, _j, _len, _len1;
+ button = this.parentNode;
+ root = button.parentNode;
+ id = root.id.slice(2);
+ quotes = $$(".quotelink[href$='#p" + id + "'], .backlink[href='#p" + id + "']");
+ if (/\bstub\b/.test(button.className)) {
+ ReplyHiding.show(root);
+ for (_i = 0, _len = quotes.length; _i < _len; _i++) {
+ quote = quotes[_i];
$.removeClass(quote, 'filtered');
}
delete g.hiddenReplies[id];
+ } else {
+ ReplyHiding.hide(root);
+ for (_j = 0, _len1 = quotes.length; _j < _len1; _j++) {
+ quote = quotes[_j];
+ $.addClass(quote, 'filtered');
+ }
+ g.hiddenReplies[id] = Date.now();
}
return $.set("hiddenReplies/" + g.BOARD + "/", g.hiddenReplies);
},
- hide: function(table) {
- var div, name, trip, uid, _ref, _ref1;
- if (table.hidden) {
+ hide: function(root) {
+ var button, el, stub;
+ button = root.firstElementChild;
+ if (button.hidden) {
return;
}
- table.hidden = true;
+ button.hidden = true;
+ el = root.lastElementChild;
+ el.hidden = true;
if (!Conf['Show Stubs']) {
return;
}
- name = $('.commentpostername', table).textContent;
- uid = ((_ref = $('.posteruid', table)) != null ? _ref.textContent : void 0) || '';
- trip = ((_ref1 = $('.postertrip', table)) != null ? _ref1.textContent : void 0) || '';
- div = $.el('div', {
- className: 'stub',
- innerHTML: "[ + ] " + name + " " + uid + " " + trip + ""
+ stub = $.el('div', {
+ className: 'hide_reply_button stub',
+ innerHTML: '[ + ] '
});
- $.on(div.firstChild, 'click', ReplyHiding.toggle);
- return $.before(table, div);
+ $.add(stub.firstChild, $.tn($('.nameBlock', el).textContent));
+ $.on(stub.firstChild, 'click', ReplyHiding.toggle);
+ return $.after(button, stub);
+ },
+ show: function(root) {
+ var button, el;
+ el = root.lastElementChild;
+ button = root.firstElementChild;
+ el.hidden = false;
+ button.hidden = false;
+ if (!Conf['Show Stubs']) {
+ return;
+ }
+ return $.rm(button.nextElementSibling);
}
};
@@ -1013,7 +1073,7 @@
return $.on(d, 'keydown', Keybinds.keydown);
},
keydown: function(e) {
- var key, o, range, selEnd, selStart, ta, thread, value, _ref, _ref1;
+ var key, link, o, range, selEnd, selStart, ta, thread, value;
if (!(key = Keybinds.keyCode(e)) || /TEXTAREA|INPUT/.test(e.target.nodeName) && !(e.altKey || e.ctrlKey || e.keyCode === 27)) {
return;
}
@@ -1071,16 +1131,16 @@
Keybinds.img(thread, true);
break;
case Conf.zero:
- window.location = "/" + g.BOARD + "/0#0";
+ window.location = "/" + g.BOARD + "/0#delform";
break;
case Conf.nextPage:
- if ((_ref = $('input[value=Next]')) != null) {
- _ref.click();
+ if (link = $('link[rel=next]', d.head)) {
+ window.location = link.href;
}
break;
case Conf.previousPage:
- if ((_ref1 = $('input[value=Previous]')) != null) {
- _ref1.click();
+ if (link = $('link[rel=prev]', d.head)) {
+ window.location.href = link.href;
}
break;
case Conf.nextThread:
@@ -1198,13 +1258,13 @@
if (all) {
return $.id('imageExpand').click();
} else {
- thumb = $('img[md5]', $('.replyhl', thread) || thread);
+ thumb = $('img[data-md5]', $('.post.highlight', thread) || thread);
return ImageExpand.toggle(thumb.parentNode);
}
},
qr: function(thread, quote) {
if (quote) {
- QR.quote.call($('.quotejs + .quotejs', $('.replyhl', thread) || thread));
+ QR.quote.call($('.postInfo > .postNum > a[title="Quote this post"]', $('.post.highlight', thread) || thread));
} else {
QR.open();
}
@@ -1212,7 +1272,7 @@
},
open: function(thread, tab) {
var id, url;
- id = thread.firstChild.id;
+ id = thread.id.slice(1);
url = "//boards.4chan.org/" + g.BOARD + "/res/" + id;
if (tab) {
return $.open(url);
@@ -1221,29 +1281,25 @@
}
},
hl: function(delta, thread) {
- var next, rect, replies, reply, td, _i, _len;
- if (td = $('.replyhl', thread)) {
- td.className = 'reply';
- td.removeAttribute('tabindex');
- rect = td.getBoundingClientRect();
- if (rect.bottom >= 0 && rect.top <= d.body.clientHeight) {
- next = delta === +1 ? $.x('following::td[@class="reply"]', td) : $.x('preceding::td[@class="reply"]', td);
+ var next, post, rect, replies, reply, _i, _len;
+ if (post = $('.reply.highlight', thread)) {
+ $.removeClass(post, 'highlight');
+ post.removeAttribute('tabindex');
+ rect = post.getBoundingClientRect();
+ if (rect.bottom >= 0 && rect.top <= d.documentElement.clientHeight) {
+ next = $.x('child::div[contains(@class,"post reply")]', delta === +1 ? post.parentNode.nextElementSibling : post.parentNode.previousElementSibling);
if (!next) {
- td.className = 'replyhl';
- td.tabIndex = 0;
- td.focus();
+ this.focus(post);
return;
}
if (!(g.REPLY || $.x('ancestor::div[@class="thread"]', next) === thread)) {
return;
}
rect = next.getBoundingClientRect();
- if (rect.top < 0 || rect.bottom > d.body.clientHeight) {
+ if (rect.top < 0 || rect.bottom > d.documentElement.clientHeight) {
next.scrollIntoView(delta === -1);
}
- next.className = 'replyhl';
- next.tabIndex = 0;
- next.focus();
+ this.focus(next);
return;
}
}
@@ -1254,13 +1310,16 @@
for (_i = 0, _len = replies.length; _i < _len; _i++) {
reply = replies[_i];
rect = reply.getBoundingClientRect();
- if (delta === +1 && rect.top >= 0 || delta === -1 && rect.bottom <= d.body.clientHeight) {
- reply.className = 'replyhl';
- reply.tabIndex = 0;
- reply.focus();
+ if (delta === +1 && rect.top >= 0 || delta === -1 && rect.bottom <= d.documentElement.clientHeight) {
+ this.focus(reply);
return;
}
}
+ },
+ focus: function(post) {
+ $.addClass(post, 'highlight');
+ post.tabIndex = 0;
+ return post.focus();
}
};
@@ -1312,7 +1371,7 @@
return thread;
}
}
- return $('form[name=delform]');
+ return $('.board');
},
scroll: function(delta) {
var i, rect, thread, top, _ref, _ref1;
@@ -1347,7 +1406,7 @@
}
return $('textarea', QR.el).focus();
});
- $.before($('form[name=post]'), link);
+ $.before($.id('postForm'), link);
}
script = $.el('script', {
textContent: 'Recaptcha.focus_response_field=function(){}'
@@ -1365,7 +1424,7 @@
return $.on(d, 'dragstart dragend', QR.drag);
},
node: function(post) {
- return $.on($('.quotejs + .quotejs', post.el), 'click', QR.quote);
+ return $.on($('.postInfo > .postNum > a[title="Quote this post"]', post.el), 'click', QR.quote);
},
open: function() {
if (QR.el) {
@@ -1429,15 +1488,15 @@
if (data == null) {
data = {};
}
+ if (!QR.el) {
+ return;
+ }
if (g.dead) {
value = 404;
disabled = true;
QR.cooldown.auto = false;
}
value = QR.cooldown.seconds || data.progress || value;
- if (!QR.el) {
- return;
- }
input = QR.status.input;
input.value = QR.cooldown.auto && Conf['Cooldown'] ? value ? "Auto " + value : 'Auto' : value || 'Submit';
return input.disabled = disabled || false;
@@ -1484,12 +1543,12 @@
}
QR.open();
if (!g.REPLY) {
- $('select', QR.el).value = $.x('ancestor::div[@class="thread"]', this).firstChild.id;
+ $('select', QR.el).value = $.x('ancestor::div[@class="thread"]', this).id.slice(1);
}
- id = this.previousElementSibling.hash.slice(1);
+ id = this.previousSibling.hash.slice(2);
text = ">>" + id + "\n";
sel = window.getSelection();
- if ((s = sel.toString()) && id === ((_ref = $.x('ancestor-or-self::blockquote/preceding-sibling::input', sel.anchorNode)) != null ? _ref.name : void 0)) {
+ if ((s = sel.toString()) && id === ((_ref = $.x('ancestor-or-self::blockquote', sel.anchorNode)) != null ? _ref.id.match(/\d+$/)[0] : void 0)) {
s = s.replace(/\n/g, '\n>');
text += ">" + s + "\n";
}
@@ -1497,7 +1556,6 @@
caretPos = ta.selectionStart;
QR.selected.el.lastChild.textContent = QR.selected.com = ta.value = ta.value.slice(0, caretPos) + text + ta.value.slice(ta.selectionEnd);
ta.focus();
- ta.selectionEnd = ta.selectionStart = caretPos + text.length;
range = caretPos + text.length;
return ta.setSelectionRange(range, range);
},
@@ -1783,7 +1841,7 @@
this.timeout = Date.now() + 26 * $.MINUTE;
challenge = this.challenge.firstChild.value;
this.img.alt = challenge;
- this.img.src = "http://www.google.com/recaptcha/api/image?c=" + challenge;
+ this.img.src = "//www.google.com/recaptcha/api/image?c=" + challenge;
return this.input.value = null;
},
count: function(count) {
@@ -1819,7 +1877,7 @@
}
},
dialog: function() {
- var e, fileInput, mimeTypes, name, spoiler, ta, thread, threads, _i, _j, _len, _len1, _ref, _ref1;
+ var e, fileInput, id, mimeTypes, name, spoiler, ta, thread, threads, _i, _j, _len, _len1, _ref, _ref1;
QR.el = UI.dialog('qr', 'top:0;right:0;', '\
\
Quick Reply
\
@@ -1841,7 +1899,7 @@
});
ta.style.cssText = $.get('QR.size', '');
}
- mimeTypes = $('.rules').firstElementChild.textContent.trim().match(/: (.+)/)[1].toLowerCase().replace(/\w+/g, function(type) {
+ mimeTypes = $('ul.rules').firstElementChild.textContent.trim().match(/: (.+)/)[1].toLowerCase().replace(/\w+/g, function(type) {
switch (type) {
case 'jpg':
return 'image/jpeg';
@@ -1853,18 +1911,19 @@
});
QR.mimeTypes = mimeTypes.split(', ');
QR.mimeTypes.push('');
- fileInput = $('[type=file]', QR.el);
- fileInput.max = $('[name=MAX_FILE_SIZE]').value;
+ fileInput = $('input[type=file]', QR.el);
+ fileInput.max = $('input[name=MAX_FILE_SIZE]').value;
fileInput.accept = mimeTypes;
- QR.spoiler = !!$('#com_submit + label');
+ QR.spoiler = !!$('input[name=spoiler]');
spoiler = $('#spoilerLabel', QR.el);
spoiler.hidden = !QR.spoiler;
if (!g.REPLY) {
threads = '
';
- _ref = $$('.op');
+ _ref = $$('.thread');
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
thread = _ref[_i];
- threads += "
";
+ id = thread.id.slice(1);
+ threads += "
";
}
$.prepend($('.move > span', QR.el), $.el('select', {
innerHTML: threads,
@@ -1901,8 +1960,9 @@
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
name = _ref1[_j];
$.on($("[name=" + name + "]", QR.el), 'input keyup change paste', function() {
+ var _ref2;
QR.selected[this.name] = this.value;
- if (QR.cooldown.auto && QR.selected === QR.replies[0] && parseInt(QR.status.input.value.match(/\d+/)) < 6) {
+ if (QR.cooldown.auto && QR.selected === QR.replies[0] && (0 < (_ref2 = QR.cooldown.seconds) && _ref2 < 6)) {
return QR.cooldown.auto = false;
}
});
@@ -1920,7 +1980,7 @@
}
return _results;
});
- QR.status.input = $('[type=submit]', QR.el);
+ QR.status.input = $('input[type=submit]', QR.el);
QR.status();
QR.cooldown.init();
QR.captcha.init();
@@ -1993,7 +2053,7 @@
upfile: reply.file,
spoiler: reply.spoiler,
mode: 'regist',
- pwd: (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $('[name=pwd]').value,
+ pwd: (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $('input[name=pwd]').value,
recaptcha_challenge_field: challenge,
recaptcha_response_field: response + ' '
};
@@ -2032,7 +2092,7 @@
}
}
};
- return QR.ajax = $.ajax($('form[name=post]').action, callbacks, opts);
+ return QR.ajax = $.ajax($.id('postForm').parentNode.action, callbacks, opts);
},
response: function(html) {
var b, doc, err, node, persona, postNumber, reply, thread, _, _ref;
@@ -2132,7 +2192,7 @@
}
},
dialog: function() {
- var arr, back, checked, description, dialog, favicon, fileInfoR, fileInfoT, hiddenNum, hiddenThreads, indicator, indicators, input, key, li, obj, overlay, ta, time, tr, ul, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3;
+ var arr, back, checked, description, dialog, favicon, fileInfo, hiddenNum, hiddenThreads, indicator, indicators, input, key, li, obj, overlay, ta, time, tr, ul, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3;
dialog = $.el('div', {
id: 'options',
className: 'reply dialog',
@@ -2210,16 +2270,13 @@
\
File Info Formatting is disabled.
\
\
Unread Favicon is disabled.
\
Unread favicons
\
@@ -2274,16 +2331,13 @@
}
(back = $('[name=backlink]', dialog)).value = Conf['backlink'];
(time = $('[name=time]', dialog)).value = Conf['time'];
- (fileInfoR = $('[name=fileInfoR]', dialog)).value = Conf['fileInfoR'];
- (fileInfoT = $('[name=fileInfoT]', dialog)).value = Conf['fileInfoT'];
+ (fileInfo = $('[name=fileInfo]', dialog)).value = Conf['fileInfo'];
$.on(back, 'keyup', $.cb.value);
$.on(back, 'keyup', Options.backlink);
$.on(time, 'keyup', $.cb.value);
$.on(time, 'keyup', Options.time);
- $.on(fileInfoR, 'keyup', $.cb.value);
- $.on(fileInfoR, 'keyup', Options.fileInfo);
- $.on(fileInfoT, 'keyup', $.cb.value);
- $.on(fileInfoT, 'keyup', Options.fileInfo);
+ $.on(fileInfo, 'keyup', $.cb.value);
+ $.on(fileInfo, 'keyup', Options.fileInfo);
favicon = $('select', dialog);
favicon.value = Conf['favicon'];
$.on(favicon, 'change', $.cb.value);
@@ -2322,8 +2376,7 @@
d.body.style.setProperty('overflow', 'hidden', null);
Options.backlink.call(back);
Options.time.call(time);
- Options.fileInfo.call(fileInfoR);
- Options.fileInfo.call(fileInfoT);
+ Options.fileInfo.call(fileInfo);
return Options.favicon.call(favicon);
},
close: function() {
@@ -2358,19 +2411,17 @@
return $.id('backlinkPreview').textContent = Conf['backlink'].replace(/%id/, '123456789');
},
fileInfo: function() {
- var type;
- type = this.name === 'fileInfoR' ? 0 : 1;
FileInfo.data = {
- link: '
1329791824.png',
- size: 996,
+ link: 'javascript:;',
+ spoiler: true,
+ size: '276',
unit: 'KB',
- resolution: '1366x768',
- fullname: '[a.f.k.] Sayonara Zetsubou Sensei - 09.avi_snapshot_03.34_[2011.02.20_06.58.00].jpg',
- shortname: '[a.f.k.] Sayonara Zetsubou Sen(...).jpg',
- type: type
+ resolution: '1280x720',
+ fullname: 'd9bb2efc98dd0df141a94399ff5880b7.jpg',
+ shortname: 'd9bb2efc98dd0df141a94399ff5880(...).jpg'
};
FileInfo.setFormats();
- return $.id("" + this.name + "Preview").innerHTML = FileInfo.funks[type](FileInfo);
+ return $.id('fileInfoPreview').innerHTML = FileInfo.funk(FileInfo);
},
favicon: function() {
Favicon["switch"]();
@@ -2379,119 +2430,6 @@
}
};
- Threading = {
- op: function(node) {
- var nodes, op;
- nodes = [];
- while (node.nodeName !== 'BLOCKQUOTE') {
- nodes.push(node);
- node = node.nextSibling;
- }
- nodes.push(node);
- node = node.nextSibling;
- op = $.el('div', {
- className: 'op'
- });
- $.add(op, nodes);
- op.id = $('input', op).name;
- return $.before(node, op);
- },
- thread: function(node) {
- var div, nodes;
- node = Threading.op(node);
- if (g.REPLY) {
- return;
- }
- nodes = [];
- while (node.nodeName !== 'HR') {
- nodes.push(node);
- node = node.nextElementSibling;
- }
- div = $.el('div', {
- className: 'thread'
- });
- $.add(div, nodes);
- $.before(node, div);
- node = node.nextElementSibling;
- if (!(node.align || node.nodeName === 'CENTER')) {
- return Threading.thread(node);
- }
- }
- };
-
- ThreadHiding = {
- init: function() {
- var a, hiddenThreads, op, thread, _i, _len, _ref;
- hiddenThreads = $.get("hiddenThreads/" + g.BOARD + "/", {});
- _ref = $$('.thread');
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- thread = _ref[_i];
- op = $('.op', thread);
- a = $.el('a', {
- textContent: '[ - ]',
- href: 'javascript:;'
- });
- $.on(a, 'click', ThreadHiding.cb);
- $.prepend(op, a);
- if (op.id in hiddenThreads) {
- ThreadHiding.hide(thread);
- }
- }
- },
- cb: function() {
- return ThreadHiding.toggle(this.parentNode.parentNode);
- },
- toggle: function(thread) {
- var hiddenThreads, id;
- hiddenThreads = $.get("hiddenThreads/" + g.BOARD + "/", {});
- id = $('.op', thread).id;
- if (thread.hidden || thread.firstChild.className === 'block') {
- ThreadHiding.show(thread);
- delete hiddenThreads[id];
- } else {
- ThreadHiding.hide(thread);
- hiddenThreads[id] = Date.now();
- }
- return $.set("hiddenThreads/" + g.BOARD + "/", hiddenThreads);
- },
- hide: function(thread) {
- var a, div, name, num, op, span, text, trip, uid, _ref, _ref1;
- if (!Conf['Show Stubs']) {
- thread.hidden = true;
- thread.nextSibling.hidden = true;
- return;
- }
- if (thread.firstChild.className === 'block') {
- return;
- }
- num = 0;
- if (span = $('.omittedposts', thread)) {
- num = Number(span.textContent.match(/\d+/)[0]);
- }
- num += $$('.op ~ table', thread).length;
- text = num === 1 ? '1 reply' : "" + num + " replies";
- op = $('.op', thread);
- name = $('.postername', op).textContent;
- uid = ((_ref = $('.posteruid', op)) != null ? _ref.textContent : void 0) || '';
- trip = ((_ref1 = $('.postertrip', op)) != null ? _ref1.textContent : void 0) || '';
- a = $.el('a', {
- innerHTML: "
[ + ] " + name + " " + uid + " " + trip + " (" + text + ")",
- href: 'javascript:;'
- });
- $.on(a, 'click', ThreadHiding.cb);
- div = $.el('div', {
- className: 'block'
- });
- $.add(div, a);
- return $.prepend(thread, div);
- },
- show: function(thread, id) {
- $.rm($('.block', thread));
- thread.hidden = false;
- return thread.nextSibling.hidden = false;
- }
- };
-
Updater = {
init: function() {
var checkbox, checked, dialog, html, input, name, title, _i, _len, _ref;
@@ -2507,7 +2445,7 @@
dialog = UI.dialog('updater', 'bottom: 0; right: 0;', html);
this.count = $('#count', dialog);
this.timer = $('#timer', dialog);
- this.br = $('br[clear]');
+ this.thread = $.id("t" + g.THREAD_ID);
_ref = $$('input', dialog);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
input = _ref[_i];
@@ -2566,7 +2504,7 @@
};
},
update: function() {
- var doc, id, newPosts, nodes, reply, scroll, _i, _len, _ref;
+ var count, doc, id, lastPost, nodes, reply, scroll, _i, _len, _ref;
if (this.status === 404) {
Updater.timer.textContent = '';
Updater.count.textContent = 404;
@@ -2583,7 +2521,7 @@
return;
}
Updater.retryCoef = 10;
- Updater.timer.textContent = '-' + Conf['Interval'];
+ Updater.timer.textContent = "-" + Conf['Interval'];
/*
Status Code 304: Not modified
By sending the `If-Modified-Since` header we get a proper status code, and no response.
@@ -2601,25 +2539,26 @@
Updater.lastModified = this.getResponseHeader('Last-Modified');
doc = d.implementation.createHTMLDocument('');
doc.documentElement.innerHTML = this.response;
- id = $('input', Updater.br.previousElementSibling).name;
+ lastPost = Updater.thread.lastElementChild;
+ id = lastPost.id.slice(2);
nodes = [];
- _ref = $$('.reply', doc).reverse();
+ _ref = $$('.replyContainer', doc).reverse();
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
reply = _ref[_i];
- if (reply.id <= id) {
+ if (reply.id.slice(2) <= id) {
break;
}
- nodes.push(reply.parentNode.parentNode.parentNode);
+ nodes.push(reply);
}
- newPosts = nodes.length;
- scroll = Conf['Scrolling'] && Updater.scrollBG() && newPosts && Updater.br.previousElementSibling.getBoundingClientRect().bottom - d.body.clientHeight < 25;
+ count = nodes.length;
+ scroll = Conf['Scrolling'] && Updater.scrollBG() && count && lastPost.getBoundingClientRect().bottom - d.documentElement.clientHeight < 25;
if (Conf['Verbose']) {
- Updater.count.textContent = "+" + newPosts;
- Updater.count.className = newPosts ? 'new' : null;
+ Updater.count.textContent = "+" + count;
+ Updater.count.className = count ? 'new' : null;
}
- $.before(Updater.br, nodes.reverse());
+ $.add(Updater.thread, nodes.reverse());
if (scroll) {
- return Updater.br.previousSibling.scrollIntoView();
+ return nodes[0].scrollIntoView();
}
}
},
@@ -2638,7 +2577,7 @@
},
retry: function() {
this.count.textContent = 'Retry';
- this.count.className = '';
+ this.count.className = null;
return this.update();
},
update: function() {
@@ -2660,13 +2599,13 @@
Watcher = {
init: function() {
- var favicon, html, input, inputs, _i, _len;
+ var favicon, html, input, _i, _len, _ref;
html = '
Thread Watcher
';
this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', html);
$.add(d.body, this.dialog);
- inputs = $$('.op > input');
- for (_i = 0, _len = inputs.length; _i < _len; _i++) {
- input = inputs[_i];
+ _ref = $$('.op input');
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ input = _ref[_i];
favicon = $.el('img', {
className: 'favicon'
});
@@ -2764,15 +2703,17 @@
return Main.callbacks.push(this.node);
},
node: function(post) {
- var name, node;
- if (post["class"] === 'inline') {
+ var name, parent, trip;
+ if (post.isInlined && !post.isCrosspost) {
return;
}
- name = $('.commentpostername, .postername', post.el);
+ name = $('.name', post.el);
name.textContent = 'Anonymous';
- node = name.nextElementSibling;
- if (node.className === 'postertrip' || node.nodeName === 'A') {
- return $.rm(node);
+ if ((trip = name.nextElementSibling) && trip.className === 'postertrip') {
+ $.rm(trip);
+ }
+ if ((parent = name.parentNode).className === 'useremail' && !/^sage$/i.test(parent.pathname)) {
+ return $.replace(parent, name);
}
}
};
@@ -2807,7 +2748,7 @@
case '$2':
return "' + img.href + '";
case '$3':
- return "' + img.firstChild.getAttribute('md5').replace(/\=*$/, '') + '";
+ return "' + img.firstChild.dataset.md5.replace(/\=*$/, '') + '";
case '$4':
return g.BOARD;
}
@@ -2827,7 +2768,7 @@
node: function(post) {
var img, link, nodes, _i, _len, _ref;
img = post.img;
- if (post["class"] === 'inline' || !img) {
+ if (post.isInlined && !post.isCrosspost || !img) {
return;
}
img = img.parentNode;
@@ -2835,9 +2776,9 @@
_ref = Sauce.links;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
- nodes.push($.tn(' '), link(img));
+ nodes.push($.tn('\u00A0'), link(img));
}
- return $.add(post.filesize, nodes);
+ return $.add(post.fileInfo, nodes);
}
};
@@ -2848,11 +2789,10 @@
node: function(post) {
var img;
img = post.img;
- if (!(img && /^Spoil/.test(img.alt)) || post["class"] === 'inline') {
+ if (!(img && /^Spoiler/.test(img.alt)) || post.isInlined && !post.isCrosspost) {
return;
}
- img.removeAttribute('height');
- img.removeAttribute('width');
+ img.removeAttribute('style');
return img.src = "//thumbs.4chan.org" + (img.parentNode.pathname.replace(/src(\/\d+).+$/, 'thumb$1s.jpg'));
}
};
@@ -2865,11 +2805,11 @@
if ($.isDST()) {
chanOffset--;
}
- this.parse = Date.parse('10/11/11(Tue)18:53') === 1318351980000 ? function(node) {
- return new Date(Date.parse(node.textContent) + chanOffset * $.HOUR);
- } : function(node) {
+ this.parse = Date.parse('10/11/11(Tue)18:53') === 1318351980000 ? function(text) {
+ return new Date(Date.parse(text) + chanOffset * $.HOUR);
+ } : function(text) {
var day, hour, min, month, year, _, _ref;
- _ref = node.textContent.match(/(\d+)\/(\d+)\/(\d+)\(\w+\)(\d+):(\d+)/), _ = _ref[0], month = _ref[1], day = _ref[2], year = _ref[3], hour = _ref[4], min = _ref[5];
+ _ref = text.match(/(\d+)\/(\d+)\/(\d+)\(\w+\)(\d+):(\d+)/), _ = _ref[0], month = _ref[1], day = _ref[2], year = _ref[3], hour = _ref[4], min = _ref[5];
year = "20" + year;
month--;
hour = chanOffset + Number(hour);
@@ -2878,17 +2818,13 @@
return Main.callbacks.push(this.node);
},
node: function(post) {
- var node, time;
- if (post["class"] === 'inline') {
+ var node;
+ if (post.isInlined && !post.isCrosspost) {
return;
}
- node = $('.posttime', post.el) || $('span[id]', post.el).previousSibling;
- Time.date = Time.parse(node);
- time = $.el('time', {
- textContent: ' ' + Time.funk(Time) + ' '
- });
- time.setAttribute('datetime', Time.date.toISOString());
- return $.replace(node, time);
+ node = $('.postInfo > .dateTime', post.el);
+ Time.date = Time.parse(node.textContent);
+ return node.textContent = Time.funk(Time);
},
foo: function() {
var code;
@@ -2976,42 +2912,34 @@
return Main.callbacks.push(this.node);
},
node: function(post) {
- var data, link, node, regexp, resolution, size, span, unit, _, _ref;
- if (post["class"] === 'inline' || !(node = post.filesize)) {
+ var alt, node, span;
+ if (post.isInlined && !post.isCrosspost || !post.fileInfo) {
return;
}
- regexp = /^File: (<.+>)-\((?:Spoiler Image, )?([\d\.]+) (\w+), (\d+x\d+|PDF)/;
- _ref = node.innerHTML.match(regexp), _ = _ref[0], link = _ref[1], size = _ref[2], unit = _ref[3], resolution = _ref[4];
- data = {
- link: link,
- size: size,
- unit: unit,
- resolution: resolution
+ node = post.fileInfo.firstElementChild;
+ alt = post.img.alt;
+ span = $('span', node);
+ FileInfo.data = {
+ link: post.img.parentNode.href,
+ spoiler: /^Spoiler/.test(alt),
+ size: alt.match(/\d+/)[0],
+ unit: alt.match(/\w+$/)[0],
+ resolution: span.previousSibling.textContent.match(/\d+x\d+|PDF/)[0],
+ fullname: span.title,
+ shortname: span.textContent
};
- if (span = $('span', node)) {
- data.fullname = span.title;
- data.shortname = span.textContent;
- }
- data.type = +(!span);
- FileInfo.data = data;
- return node.innerHTML = FileInfo.funks[data.type](FileInfo);
+ return node.innerHTML = FileInfo.funk(FileInfo);
},
setFormats: function() {
- var code, format, funks, i, param, _i;
- funks = [];
- for (i = _i = 0; _i <= 1; i = ++_i) {
- format = i ? Conf['fileInfoT'] : Conf['fileInfoR'];
- param = i ? /%([BKlMrs])/g : /%([BKlLMnNrs])/g;
- code = format.replace(param, function(s, c) {
- if (c in FileInfo.formatters) {
- return "' + f.formatters." + c + "() + '";
- } else {
- return s;
- }
- });
- funks.push(Function('f', "return '" + code + "'"));
- }
- return this.funks = funks;
+ var code;
+ code = Conf['fileInfo'].replace(/%([BKlLMnNprs])/g, function(s, c) {
+ if (c in FileInfo.formatters) {
+ return "' + f.formatters." + c + "() + '";
+ } else {
+ return s;
+ }
+ });
+ return this.funk = Function('f', "return '" + code + "'");
},
convertUnit: function(unitT) {
var i, size, unitF, units;
@@ -3040,25 +2968,28 @@
},
formatters: {
l: function() {
- if (FileInfo.data.type === 0) {
- return FileInfo.data.link.replace(/>\d+\.\w+, ">" + (this.n()) + "<");
- } else {
- return FileInfo.data.link;
- }
+ return "
" + (this.n()) + "";
},
L: function() {
- return FileInfo.data.link.replace(/>\d+\.\w+, ">" + FileInfo.data.fullname + "<");
+ return "
" + (this.N()) + "";
},
n: function() {
if (FileInfo.data.fullname === FileInfo.data.shortname) {
return FileInfo.data.fullname;
} else {
- return "
" + FileInfo.data.fullname + "" + FileInfo.data.shortname + "";
+ return "
" + FileInfo.data.shortname + "" + FileInfo.data.fullname + "";
}
},
N: function() {
return FileInfo.data.fullname;
},
+ p: function() {
+ if (FileInfo.data.spoiler) {
+ return 'Spoiler, ';
+ } else {
+ return '';
+ }
+ },
s: function() {
return "" + FileInfo.data.size + " " + FileInfo.data.unit;
},
@@ -3078,18 +3009,19 @@
};
GetTitle = function(thread) {
- var el, span;
- el = $('.filetitle', thread);
+ var el, op, span;
+ op = $('.op', thread);
+ el = $('.subject', op);
if (!el.textContent) {
- el = $('blockquote', thread);
+ el = $('blockquote', op);
if (!el.textContent) {
- el = $('.postername', thread);
+ el = $('.nameBlock', op);
}
}
span = $.el('span', {
innerHTML: el.innerHTML.replace(/
/g, ' ')
});
- return "/" + g.BOARD + "/ - " + span.textContent;
+ return "/" + g.BOARD + "/ - " + (span.textContent.trim());
};
TitlePost = {
@@ -3106,7 +3038,7 @@
return Main.callbacks.push(this.node);
},
node: function(post) {
- var a, container, el, link, qid, quote, quotes, root, _i, _len, _ref;
+ var a, container, el, link, qid, quote, quotes, _i, _len, _ref;
if (post.isInlined) {
return;
}
@@ -3114,17 +3046,17 @@
_ref = post.quotes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quote = _ref[_i];
- if (qid = quote.hash.slice(1)) {
+ if (qid = quote.hash.slice(2)) {
quotes[qid] = true;
}
}
a = $.el('a', {
- href: "#" + post.id,
- className: post.root.hidden ? 'filtered backlink' : 'backlink',
+ href: "#p" + post.id,
+ className: post.el.hidden ? 'filtered backlink' : 'backlink',
textContent: QuoteBacklink.funk(post.id)
});
for (qid in quotes) {
- if (!(el = $.id(qid)) || el.className === 'op' && !Conf['OP Backlinks']) {
+ if (!(el = $.id("pi" + qid)) || !Conf['OP Backlinks'] && /\bop\b/.test(el.parentNode.className)) {
continue;
}
link = a.cloneNode(true);
@@ -3136,16 +3068,14 @@
} else {
link.setAttribute('onclick', "replyhl('" + post.id + "');");
}
- if (!((container = $('.container', el)) && container.parentNode === el)) {
+ if (!(container = $.id("blc" + qid))) {
container = $.el('span', {
- className: 'container'
+ className: 'container',
+ id: "blc" + qid
});
- $.add(container, [$.tn(' '), link]);
- root = $('.reportbutton', el) || $('span[id]', el);
- $.after(root, container);
- } else {
- $.add(container, [$.tn(' '), link]);
+ $.add(el, container);
}
+ $.add(container, [$.tn(' '), link]);
}
}
};
@@ -3177,11 +3107,11 @@
return;
}
e.preventDefault();
- id = this.hash.slice(1);
+ id = this.hash.slice(2);
if (/\binlined\b/.test(this.className)) {
QuoteInline.rm(this, id);
} else {
- if ($.x("ancestor::*[@id='" + id + "']", this)) {
+ if ($.x("ancestor::div[contains(@id,'p" + id + "')]", this)) {
return;
}
QuoteInline.add(this, id);
@@ -3189,61 +3119,64 @@
return this.classList.toggle('inlined');
},
add: function(q, id) {
- var el, i, inline, pathname, root, table, threadID;
- root = q.parentNode.nodeName === 'FONT' ? q.parentNode : q.nextSibling ? q.nextSibling : q;
- if (el = $.id(id)) {
- inline = QuoteInline.table(id, el.innerHTML);
- if ((i = Unread.replies.indexOf(el.parentNode.parentNode.parentNode)) !== -1) {
+ var clonePost, el, i, inline, pathname, root;
+ root = $.x('ancestor::*[parent::blockquote]', q);
+ if (el = $.id("p" + id)) {
+ if (/\bop\b/.test(el.className)) {
+ $.removeClass(el.parentNode, 'qphl');
+ } else {
+ $.removeClass(el, 'qphl');
+ }
+ clonePost = QuoteInline.clone(id, el);
+ if (/\bbacklink\b/.test(q.className)) {
+ $.after(q.parentNode, clonePost);
+ if (Conf['Forward Hiding']) {
+ $.addClass(el.parentNode, 'forwarded');
+ ++el.dataset.forwarded || (el.dataset.forwarded = 1);
+ }
+ } else {
+ $.after(root, clonePost);
+ }
+ if ((i = Unread.replies.indexOf(el)) !== -1) {
Unread.replies.splice(i, 1);
Unread.update(true);
}
- if (/\bbacklink\b/.test(q.className)) {
- $.after(q.parentNode, inline);
- if (Conf['Forward Hiding']) {
- table = $.x('ancestor::table', el);
- $.addClass(table, 'forwarded');
- ++table.title || (table.title = 1);
- }
- return;
- }
- return $.after(root, inline);
- } else {
- inline = $.el('td', {
- className: 'reply inline',
- id: "i" + id,
- innerHTML: "Loading " + id + "..."
- });
- $.after(root, inline);
- pathname = q.pathname;
- threadID = pathname.split('/').pop();
- return $.cache(pathname, (function() {
- return QuoteInline.parse(this, pathname, id, threadID, inline);
- }));
+ return;
}
+ inline = $.el('div', {
+ className: 'inline',
+ id: "i" + id,
+ textContent: "Loading " + id + "..."
+ });
+ $.after(root, inline);
+ pathname = q.pathname;
+ return $.cache(pathname, function() {
+ return QuoteInline.parse(this, pathname, id, inline);
+ });
},
rm: function(q, id) {
- var inlined, table, _i, _len, _ref;
- table = $.x("following::*[@id='i" + id + "']", q);
- $.rm(table);
+ var div, inlined, _i, _len, _ref;
+ div = $.x("following::div[@id='i_pc" + id + "']", q);
+ $.rm(div);
if (!Conf['Forward Hiding']) {
return;
}
- _ref = $$('.backlink.inlined', table);
+ _ref = $$('.backlink.inlined', div);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
inlined = _ref[_i];
- table = $.x('ancestor::table', $.id(inlined.hash.slice(1)));
- if (!--table.title) {
- $.removeClass(table, 'forwarded');
+ div = $.id(inlined.hash.slice(1));
+ if (!--div.dataset.forwarded) {
+ $.removeClass(div.parentNode, 'forwarded');
}
}
if (/\bbacklink\b/.test(q.className)) {
- table = $.x('ancestor::table', $.id(id));
- if (!--table.title) {
- return $.removeClass(table, 'forwarded');
+ div = $.id("p" + id);
+ if (!--div.dataset.forwarded) {
+ return $.removeClass(div.parentNode, 'forwarded');
}
}
},
- parse: function(req, pathname, id, threadID, inline) {
+ parse: function(req, pathname, id, inline) {
var doc, href, link, newInline, node, quote, _i, _len, _ref;
if (!inline.parentNode) {
return;
@@ -3254,29 +3187,36 @@
}
doc = d.implementation.createHTMLDocument('');
doc.documentElement.innerHTML = req.response;
- node = id === threadID ? Threading.op($('body > form', doc).firstChild) : doc.getElementById(id);
- newInline = QuoteInline.table(id, node.innerHTML);
+ node = doc.getElementById("p" + id);
+ newInline = QuoteInline.clone(id, node);
_ref = $$('.quotelink', newInline);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quote = _ref[_i];
- if ((href = quote.getAttribute('href')) === quote.hash) {
- quote.pathname = pathname;
- } else if (!g.REPLY && href !== quote.href) {
- quote.href = "res/" + href;
+ href = quote.getAttribute('href');
+ if (href[0] === '/') {
+ continue;
}
+ quote.href = "res/" + href;
}
- link = $('.quotejs', newInline);
- link.href = "" + pathname + "#" + id;
+ link = $('.postInfo > .postNum > a:first-child', newInline);
+ link.href = "" + pathname + "#p" + id;
link.nextSibling.href = "" + pathname + "#q" + id;
- $.addClass(newInline, 'crossquote');
+ $.addClass(newInline, 'crosspost');
return $.replace(inline, newInline);
},
- table: function(id, html) {
- return $.el('table', {
- className: 'inline',
- id: "i" + id,
- innerHTML: "
| " + html + " |
"
+ clone: function(id, el) {
+ var clone, node, _i, _len, _ref;
+ clone = $.el('div', {
+ className: 'postContainer inline',
+ id: "i_pc" + id
});
+ $.add(clone, el.cloneNode(true));
+ _ref = $$('[id]', clone);
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ node = _ref[_i];
+ node.id = "i_" + node.id;
+ }
+ return clone;
}
};
@@ -3300,36 +3240,38 @@
}
},
mouseover: function(e) {
- var el, id, node, qp, quote, replyID, threadID, _i, _len, _ref;
+ var el, id, qp, quote, replyID, _i, _len, _ref;
if (/\binlined\b/.test(this.className)) {
return;
}
qp = UI.el = $.el('div', {
id: 'qp',
- className: 'reply dialog'
+ className: 'reply dialog post'
});
$.add(d.body, qp);
- id = this.hash.slice(1);
- if (el = $.id(id)) {
+ id = this.hash.slice(2);
+ if (el = $.id("p" + id)) {
qp.innerHTML = el.innerHTML;
if (Conf['Quote Highlighting']) {
- $.addClass(el, 'qphl');
+ if (/\bop\b/.test(el.className)) {
+ $.addClass(el.parentNode, 'qphl');
+ } else {
+ $.addClass(el, 'qphl');
+ }
}
- node = /\bbacklink\b/.test(this.className) ? this.parentNode : $.x('ancestor::blockquote', this);
- replyID = $.x('preceding-sibling::input', node).name;
+ replyID = $.x('ancestor::div[contains(@class,"postContainer")]', this).id.slice(2);
_ref = $$('.quotelink, .backlink', qp);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quote = _ref[_i];
- if (quote.hash.slice(1) === replyID) {
+ if (quote.hash.slice(2) === replyID) {
$.addClass(quote, 'forwardlink');
}
}
} else {
qp.textContent = "Loading " + id + "...";
- threadID = this.pathname.split('/').pop() || $.x('ancestor::div[@class="thread"]', this).firstChild.id;
- $.cache(this.pathname, (function() {
- return QuotePreview.parse(this, id, threadID);
- }));
+ $.cache(this.pathname, function() {
+ return QuotePreview.parse(this, id);
+ });
UI.hover(e);
}
$.on(this, 'mousemove', UI.hover);
@@ -3337,15 +3279,19 @@
},
mouseout: function() {
var el;
- if (el = $.id(this.hash.slice(1))) {
- $.removeClass(el, 'qphl');
- }
UI.hoverend();
+ if (el = $.id(this.hash.slice(1))) {
+ if (/\bop\b/.test(el.className)) {
+ $.removeClass(el.parentNode, 'qphl');
+ } else {
+ $.removeClass(el, 'qphl');
+ }
+ }
$.off(this, 'mousemove', UI.hover);
return $.off(this, 'mouseout click', QuotePreview.mouseout);
},
- parse: function(req, id, threadID) {
- var doc, node, post, qp;
+ parse: function(req, id) {
+ var doc, fileInfo, img, node, post, qp;
if (!((qp = UI.el) && qp.textContent === ("Loading " + id + "..."))) {
return;
}
@@ -3355,13 +3301,18 @@
}
doc = d.implementation.createHTMLDocument('');
doc.documentElement.innerHTML = req.response;
- node = id === threadID ? Threading.op($('body > form', doc).firstChild) : doc.getElementById(id);
+ node = doc.getElementById("p" + id);
qp.innerHTML = node.innerHTML;
post = {
- root: qp,
- filesize: $('.filesize', qp),
- img: $('img[md5]', qp)
+ el: qp
};
+ if (fileInfo = $('.fileInfo', qp)) {
+ img = fileInfo.nextElementSibling.firstElementChild;
+ if (img.alt !== 'File deleted.') {
+ post.fileInfo = fileInfo;
+ post.img = img;
+ }
+ }
if (Conf['Image Auto-Gif']) {
AutoGif.node(post);
}
@@ -3380,13 +3331,13 @@
},
node: function(post) {
var quote, _i, _len, _ref;
- if (post["class"] === 'inline') {
+ if (post.isInlined && !post.isCrosspost) {
return;
}
_ref = post.quotes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quote = _ref[_i];
- if (quote.hash.slice(1) === post.threadId) {
+ if (quote.hash.slice(2) === post.threadId) {
$.add(quote, $.tn('\u00A0(OP)'));
}
}
@@ -3399,7 +3350,7 @@
},
node: function(post) {
var path, quote, _i, _len, _ref;
- if (post["class"] === 'inline') {
+ if (post.isInlined && !post.isCrosspost) {
return;
}
_ref = post.quotes;
@@ -3422,10 +3373,10 @@
},
node: function(post) {
var a, board, data, i, id, index, m, node, nodes, quote, quotes, snapshot, text, _i, _j, _len, _ref;
- if (post["class"] === 'inline') {
+ if (post.isInlined && !post.isCrosspost) {
return;
}
- snapshot = d.evaluate('.//text()[not(parent::a)]', post.el.lastChild, null, 6, null);
+ snapshot = d.evaluate('.//text()[not(parent::a)]', post.el.lastElementChild, null, 6, null);
for (i = _i = 0, _ref = snapshot.snapshotLength; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
node = snapshot.snapshotItem(i);
data = node.data;
@@ -3440,12 +3391,12 @@
nodes.push($.tn(text));
}
id = quote.match(/\d+$/)[0];
- board = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : $('.quotejs', post.el).pathname.split('/')[1];
+ board = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : $('.postInfo > .postNum > a:first-child', post.el).pathname.split('/')[1];
nodes.push(a = $.el('a', {
textContent: "" + quote + "\u00A0(Dead)"
}));
if (board === g.BOARD && $.id(id)) {
- a.href = "#" + id;
+ a.href = "#p" + id;
a.className = 'quotelink';
a.setAttribute('onclick', "replyhl('" + id + "');");
} else {
@@ -3466,7 +3417,7 @@
ReportButton = {
init: function() {
this.a = $.el('a', {
- className: 'reportbutton',
+ className: 'report_button',
innerHTML: '[ ! ]',
href: 'javascript:;'
});
@@ -3474,9 +3425,9 @@
},
node: function(post) {
var a;
- if (!(a = $('.reportbutton', post.el))) {
+ if (!(a = $('.report_button', post.el))) {
a = ReportButton.a.cloneNode(true);
- $.after($('span[id]', post.el), [$.tn(' '), a]);
+ $.add($('.postInfo', post.el), a);
}
return $.on(a, 'click', ReportButton.report);
},
@@ -3522,7 +3473,7 @@
imgcount = $.id('imagecount');
imgcount.textContent = ++ThreadStats.images;
if (ThreadStats.images > ThreadStats.imgLimit) {
- return imgcount.className = 'warning';
+ return $.addClass(imgcount, 'warning');
}
}
};
@@ -3537,20 +3488,21 @@
replies: [],
foresee: [],
node: function(post) {
- var count, index;
+ var count, el, index;
if ((index = Unread.foresee.indexOf(post.id)) !== -1) {
Unread.foresee.splice(index, 1);
return;
}
- if (post.root.hidden || post["class"]) {
+ el = post.el;
+ if (el.hidden || /\bop\b/.test(post["class"]) || post.isInlined) {
return;
}
- count = Unread.replies.push(post.root);
+ count = Unread.replies.push(el);
return Unread.update(count === 1);
},
scroll: function() {
var bottom, height, i, reply, _i, _len, _ref;
- height = d.body.clientHeight;
+ height = d.documentElement.clientHeight;
_ref = Unread.replies;
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
reply = _ref[i];
@@ -3757,17 +3709,18 @@
return Main.callbacks.push(this.node);
},
node: function(post) {
- var img, src;
- if (post.root.hidden || !post.img) {
+ var gif, img, src;
+ img = post.img;
+ if (post.el.hidden || !img) {
return;
}
- src = post.img.parentNode.href;
- if (/gif$/.test(src) && !/spoiler/.test(post.img.src)) {
- img = $.el('img');
- $.on(img, 'load', function() {
- return post.img.src = src;
+ src = img.parentNode.href;
+ if (/gif$/.test(src) && !/spoiler/.test(img.src)) {
+ gif = $.el('img');
+ $.on(gif, 'load', function() {
+ return img.src = src;
});
- return img.src = src;
+ return gif.src = src;
}
}
};
@@ -3784,7 +3737,7 @@
}
a = post.img.parentNode;
$.on(a, 'click', ImageExpand.cb.toggle);
- if (ImageExpand.on && !post.root.hidden && post["class"] !== 'inline') {
+ if (ImageExpand.on && !post.el.hidden) {
return ImageExpand.expand(post.img);
}
},
@@ -3800,7 +3753,7 @@
var i, thumb, thumbs, _i, _j, _k, _len, _len1, _len2, _ref;
ImageExpand.on = this.checked;
if (ImageExpand.on) {
- thumbs = $$('img[md5]');
+ thumbs = $$('img[data-md5]');
if (Conf['Expand From Current']) {
for (i = _i = 0, _len = thumbs.length; _i < _len; i = ++_i) {
thumb = thumbs[i];
@@ -3815,7 +3768,7 @@
ImageExpand.expand(thumb);
}
} else {
- _ref = $$('img[md5][hidden]');
+ _ref = $$('img[data-md5][hidden]');
for (_k = 0, _len2 = _ref.length; _k < _len2; _k++) {
thumb = _ref[_k];
ImageExpand.contract(thumb);
@@ -3837,7 +3790,7 @@
case 'fit screen':
klass = 'fitwidth fitheight';
}
- $('body > form').className = klass;
+ $.id('delform').className = klass;
if (/\bfitheight\b/.test(klass)) {
$.on(window, 'resize', ImageExpand.resize);
if (!ImageExpand.style) {
@@ -3913,7 +3866,7 @@
});
},
dialog: function() {
- var controls, form, imageType, select;
+ var controls, imageType, select;
controls = $.el('div', {
id: 'imgControls',
innerHTML: "
"
@@ -3925,11 +3878,10 @@
$.on(select, 'change', $.cb.value);
$.on(select, 'change', ImageExpand.cb.typeChange);
$.on($('input', controls), 'click', ImageExpand.cb.all);
- form = $('body > form');
- return $.prepend(form, controls);
+ return $.prepend($.id('delform'), controls);
},
resize: function() {
- return ImageExpand.style.textContent = ".fitheight img[md5] + img {max-height:" + d.body.clientHeight + "px;}";
+ return ImageExpand.style.textContent = ".fitheight img[data-md5] + img {max-height:" + d.documentElement.clientHeight + "px;}";
}
};
@@ -3948,7 +3900,6 @@
val = Conf[key];
Conf[key] = $.get(key, val);
}
- $.on(window, 'message', Main.message);
switch (location.hostname) {
case 'sys.4chan.org':
if (/report/.test(location.search)) {
@@ -3971,11 +3922,12 @@
}
$.ready(Options.init);
if (Conf['Quick Reply'] && Conf['Hide Original Post Form'] && g.BOARD !== 'f') {
- Main.css += 'form[name=post] { display: none; }';
+ Main.css += '#postForm { display: none; }';
}
Main.addStyle();
now = Date.now();
if (Conf['Check for Updates'] && $.get('lastUpdate', 0) < now - 6 * $.HOUR) {
+ $.on(window, 'message', Main.message);
$.ready(function() {
return $.add(d.head, $.el('script', {
src: 'https://raw.github.com/mayhemydg/4chan-x/master/latest.js'
@@ -4058,7 +4010,7 @@
return $.ready(Main.ready);
},
ready: function() {
- var MutationObserver, form, nav, node, nodes, observer, _i, _j, _len, _len1, _ref, _ref1;
+ var MutationObserver, a, board, nav, node, nodes, observer, _i, _j, _len, _len1, _ref, _ref1;
if (d.title === '4chan - 404') {
Redirect.init();
return;
@@ -4068,13 +4020,13 @@
}
$.addClass(d.body, "chanx_" + (Main.version.split('.')[1]));
$.addClass(d.body, $.engine);
- _ref = ['navtop', 'navbot'];
+ _ref = ['boardNavDesktop', 'boardNavDesktopFoot'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
nav = _ref[_i];
- $.addClass($("a[href$='/" + g.BOARD + "/']", $.id(nav)), 'current');
+ if (a = $("a[href$='/" + g.BOARD + "/']", $.id(nav))) {
+ $.addClass(a, 'current');
+ }
}
- form = $('form[name=delform]');
- Threading.thread(form.firstElementChild);
Favicon.init();
if (Conf['Quick Reply']) {
QR.init();
@@ -4114,9 +4066,7 @@
}
} else {
if (Conf['Thread Hiding']) {
- setTimeout(function() {
- return ThreadHiding.init();
- });
+ ThreadHiding.init();
}
if (Conf['Thread Expansion']) {
setTimeout(function() {
@@ -4134,8 +4084,9 @@
});
}
}
+ board = $('.board');
nodes = [];
- _ref1 = $$('.op, a + table', form);
+ _ref1 = $$('.postContainer', board);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
node = _ref1[_j];
nodes.push(Main.preParse(node));
@@ -4143,12 +4094,12 @@
Main.node(nodes, true);
if (MutationObserver = window.WebKitMutationObserver || window.MozMutationObserver || window.OMutationObserver || window.MutationObserver) {
observer = new MutationObserver(Main.observer);
- return observer.observe(form, {
+ return observer.observe(board, {
childList: true,
subtree: true
});
} else {
- return $.on(form, 'DOMNodeInserted', Main.listener);
+ return $.on(board, 'DOMNodeInserted', Main.listener);
}
},
flatten: function(parent, obj) {
@@ -4180,20 +4131,29 @@
}
},
preParse: function(node) {
- var klass, post;
- klass = node.className;
+ var el, fileInfo, img, post, rootClass;
+ rootClass = node.className;
+ el = $('.post', node);
post = {
root: node,
- el: klass === 'op' ? node : node.firstChild.firstChild.lastChild,
- "class": klass,
- id: node.getElementsByTagName('input')[0].name,
- threadId: g.THREAD_ID || $.x('ancestor::div[@class="thread"]', node).firstChild.id,
- isInlined: /\binline\b/.test(klass),
- filesize: node.getElementsByClassName('filesize')[0] || false,
- quotes: node.getElementsByClassName('quotelink'),
- backlinks: node.getElementsByClassName('backlink')
+ el: el,
+ "class": el.className,
+ id: el.id.slice(1),
+ threadId: g.THREAD_ID || $.x('ancestor::div[@class="thread"]', node).id.slice(1),
+ isInlined: /\binline\b/.test(rootClass),
+ isCrosspost: /\bcrosspost\b/.test(rootClass),
+ quotes: el.getElementsByClassName('quotelink'),
+ backlinks: el.getElementsByClassName('backlink'),
+ fileInfo: false,
+ img: false
};
- post.img = post.filesize ? node.getElementsByTagName('img')[0] : false;
+ if (fileInfo = $('.fileInfo', el)) {
+ img = fileInfo.nextElementSibling.firstElementChild;
+ if (img.alt !== 'File deleted.') {
+ post.fileInfo = fileInfo;
+ post.img = img;
+ }
+ }
return post;
},
node: function(nodes, notify) {
@@ -4221,7 +4181,7 @@
_ref = mutation.addedNodes;
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
addedNode = _ref[_j];
- if (addedNode.nodeName === 'TABLE') {
+ if (/\bpostContainer\b/.test(addedNode.className)) {
nodes.push(Main.preParse(addedNode));
}
}
@@ -4233,7 +4193,7 @@
listener: function(e) {
var target;
target = e.target;
- if (target.nodeName === 'TABLE') {
+ if (/\bpostContainer\b/.test(addedNode.className)) {
return Main.node([Main.preParse(target)]);
}
},
@@ -4242,8 +4202,9 @@
callbacks: [],
css: '\
/* dialog styling */\
-.dialog {\
+.dialog.reply {\
border: 1px solid rgba(0,0,0,.25);\
+ padding: 0;\
}\
.move {\
cursor: move;\
@@ -4254,16 +4215,25 @@ label, .favicon {\
a[href="javascript:;"] {\
text-decoration: none;\
}\
-\
-.block ~ *,\
-#content > [name=tab]:not(:checked) + div,\
-#updater:not(:hover) > :not(.move),\
-#qp > input, #qp .inline, .forwarded {\
- display: none;\
+.warning {\
+ color: red;\
}\
\
-.autohide:not(:hover) > form {\
- display: none;\
+.hide_thread_button {\
+ float: left;\
+}\
+\
+.hidden_thread ~ *,\
+[hidden],\
+#content > [name=tab]:not(:checked) + div,\
+#updater:not(:hover) > :not(.move),\
+.autohide:not(:hover) > form,\
+#qp input, #qp .inline, .forwarded {\
+ display: none !important;\
+}\
+\
+h1 {\
+ text-align: center;\
}\
#qr > .move {\
min-width: 300px;\
@@ -4404,6 +4374,8 @@ a[href="javascript:;"] {\
}\
.field {\
border: 1px solid #CCC;\
+ box-sizing: border-box;\
+ -moz-box-sizing: border-box;\
color: #333;\
font: 13px sans-serif;\
margin: 0;\
@@ -4427,6 +4399,7 @@ textarea.field {\
min-height: 120px;\
}\
.field:only-child {\
+ display: block;\
min-width: 100%;\
}\
.captcha {\
@@ -4436,6 +4409,7 @@ textarea.field {\
text-align: center;\
}\
.captcha > img {\
+ display: block;\
height: 57px;\
width: 300px;\
}\
@@ -4450,41 +4424,21 @@ textarea.field {\
width: 30%;\
}\
\
-.new {\
- background: lime;\
-}\
-.warning {\
- color: red;\
-}\
-.replyhider {\
- vertical-align: top;\
-}\
-\
-.filesize + br + a {\
- float: left;\
- pointer-events: none;\
-}\
-.filename:hover > .fntrunc,\
-.filename:not(:hover) > .fnfull {\
+.fileText:hover .fntrunc,\
+.fileText:not(:hover) .fnfull {\
display: none;\
}\
-img[md5], img[md5] + img {\
- pointer-events: all;\
-}\
-.fitwidth img[md5] + img {\
+.fitwidth img[data-md5] + img {\
max-width: 100%;\
}\
-.gecko > .fitwidth img[md5] + img,\
-.presto > .fitwidth img[md5] + img {\
- width: 100%;\
-}\
+\
/* revealed spoilers do not have height/width,\
this fixes "expanded" auto-gifs */\
-img[md5] {\
+.op > div > a > img[data-md5] {\
max-height: 252px;\
max-width: 252px;\
}\
-input ~ a > img[md5] {\
+.reply > div > a > img[data-md5] {\
max-height: 127px;\
max-width: 127px;\
}\
@@ -4537,18 +4491,20 @@ input ~ a > img[md5] {\
#options label {\
text-decoration: underline;\
}\
-#content > div {\
+#content {\
height: 450px;\
overflow: auto;\
}\
#content textarea {\
+ box-sizing: border-box;\
+ -moz-box-sizing: border-box;\
margin: 0;\
min-height: 100px;\
resize: vertical;\
width: 100%;\
}\
#sauces {\
- height: 320px;\
+ height: 300px;\
}\
\
#updater {\
@@ -4561,9 +4517,8 @@ input ~ a > img[md5] {\
border: none;\
background: transparent;\
}\
-\
-#stats {\
- border: none;\
+.new {\
+ background: lime;\
}\
\
#watcher {\
@@ -4587,36 +4542,44 @@ input ~ a > img[md5] {\
text-decoration: underline;\
}\
\
-#qp {\
- padding-bottom: 5px;\
-}\
-#qp > a > img {\
+#qp img {\
max-height: 300px;\
max-width: 500px;\
}\
.qphl {\
outline: 2px solid rgba(216, 94, 49, .7);\
}\
+.qphl.opContainer {\
+ outline-offset: -2px;\
+}\
+div.opContainer {\
+ display: block !important;\
+}\
.inlined {\
opacity: .5;\
}\
-.inline .reply {\
+.inline {\
+ overflow: hidden;\
background-color: rgba(255, 255, 255, 0.15);\
border: 1px solid rgba(128, 128, 128, 0.5);\
}\
-.filetitle, .replytitle, .postername, .commentpostername, .postertrip {\
+.inline .post {\
background: none;\
+ border: none;\
}\
-.filter_highlight.op,\
-.filter_highlight > td[id] {\
+.filter_highlight.thread > .opContainer {\
+ box-shadow: inset 5px 0 rgba(255,0,0,0.5);\
+}\
+.filter_highlight > .reply {\
box-shadow: -5px 0 rgba(255,0,0,0.5);\
}\
.filtered {\
- text-decoration: line-through;\
+ text-decoration: underline line-through;\
}\
.quotelink.forwardlink,\
.backlink.forwardlink {\
- color: #4C4CA9;\
+ text-decoration: none;\
+ border-bottom: 1px dashed;\
}\
'
};
diff --git a/changelog b/changelog
index 2d4031218..7803125da 100644
--- a/changelog
+++ b/changelog
@@ -1,5 +1,6 @@
master
- Mayhem
+ Add Spoiler indicator option in File Info Formatting
Remove archive.no-ip.org archive redirections.
2.29.5
diff --git a/script.coffee b/script.coffee
index 5f63ae6f5..856e298f8 100644
--- a/script.coffee
+++ b/script.coffee
@@ -114,8 +114,7 @@ Config =
].join '\n'
time: '%m/%d/%y(%a)%H:%M'
backlink: '>>%id'
- fileInfoR: '%l (%s, %r)'
- fileInfoT: '%l (%s, %r)'
+ fileInfo: '%l (%p%s, %r)'
favicon: 'ferongr'
hotkeys:
# QR & Options
@@ -175,32 +174,29 @@ UI =
d.addEventListener 'mouseup', UI.dragend, false
#distance from pointer to el edge is constant; calculate it here.
# XXX opera reports el.offsetLeft / el.offsetTop as 0
- rect = el.getBoundingClientRect()
- UI.dx = e.clientX - rect.left
- UI.dy = e.clientY - rect.top
- #factor out el from document dimensions
- UI.width = d.body.clientWidth - el.offsetWidth
- UI.height = d.body.clientHeight - el.offsetHeight
+ rect = el.getBoundingClientRect()
+ UI.dx = e.clientX - rect.left
+ UI.dy = e.clientY - rect.top
+ UI.width = d.documentElement.clientWidth - rect.width
+ UI.height = d.documentElement.clientHeight - rect.height
drag: (e) ->
left = e.clientX - UI.dx
- top = e.clientY - UI.dy
+ top = e.clientY - UI.dy
left =
- if left < 10 then 0
+ if left < 10 then '0px'
else if UI.width - left < 10 then null
- else left
+ else left + 'px'
top =
- if top < 10 then 0
+ if top < 10 then '0px'
else if UI.height - top < 10 then null
- else top
- right = if left is null then 0 else null
- bottom = if top is null then 0 else null
+ else top + 'px'
#using null instead of '' is 4% faster
#these 4 statements are 40% faster than 1 style.cssText
{style} = UI.el
- style.top = top
- style.right = right
- style.bottom = bottom
style.left = left
+ style.top = top
+ style.right = if left is null then '0px' else null
+ style.bottom = if top is null then '0px' else null
dragend: ->
#$ coffee -bpe '{a} = {b} = c'
#var a, b;
@@ -211,26 +207,25 @@ UI =
d.removeEventListener 'mouseup', UI.dragend, false
hover: (e) ->
{clientX, clientY} = e
- {el} = UI
- {style} = el
- {clientHeight, clientWidth} = d.body
- height = el.offsetHeight
+ {style} = UI.el
+ {clientHeight, clientWidth} = d.documentElement
+ height = UI.el.offsetHeight
top = clientY - 120
style.top =
if clientHeight <= height or top <= 0
- 0
+ '0px'
else if top + height >= clientHeight
- clientHeight - height
+ clientHeight - height + 'px'
else
- top
+ top + 'px'
if clientX <= clientWidth - 400
- style.left = clientX + 45
+ style.left = clientX + 45 + 'px'
style.right = null
else
style.left = null
- style.right = clientWidth - clientX + 45
+ style.right = clientWidth - clientX + 45 + 'px'
hoverend: ->
$.rm UI.el
@@ -513,8 +508,8 @@ Filter =
node: (post) ->
return if post.isInlined
- post.isOP = post.class is 'op'
- {isOP, el} = post
+ isOP = post.id is post.threadId
+ {root} = post
for key of Filter.filters
value = Filter[key] post
if value is false
@@ -528,62 +523,57 @@ Filter =
if result is true
if isOP
unless g.REPLY
- ThreadHiding.hide post.el.parentNode
+ ThreadHiding.hide root.parentNode
else
continue
else
- ReplyHiding.hide post.root
+ ReplyHiding.hide root
return
# Highlight
- if isOP
- $.addClass el, result[0]
- else
- $.addClass el.parentNode, result[0]
+ $.addClass (if isOP then root.parentNode else root), result[0]
if isOP and result[1] and not g.REPLY
- # Put the highlighted OPs' threads on top of the board pages...
- thisThread = el.parentNode
+ # Put the highlighted OPs' thread on top of the board page...
+ thisThread = root.parentNode
# ...before the first non highlighted thread.
- if firstThread = $ 'div[class=op]'
- $.before firstThread.parentNode, [thisThread, thisThread.nextElementSibling]
+ if firstThread = $ 'div[class=thread]'
+ $.before firstThread, [thisThread, thisThread.nextElementSibling]
name: (post) ->
- name = if post.isOP then $ '.postername', post.el else $ '.commentpostername', post.el
- name.textContent
+ $('.name', post.el).textContent
uniqueid: (post) ->
if uid = $ '.posteruid', post.el
- return uid.textContent
+ return uid.textContent[5...-1]
false
tripcode: (post) ->
if trip = $ '.postertrip', post.el
return trip.textContent
false
mod: (post) ->
- if mod = (if post.isOP then $ '.commentpostername', post.el else $ '.commentpostername ~ .commentpostername', post.el)
+ if mod = $ '.capcode', post.el
return mod.textContent
false
email: (post) ->
- if mail = $ '.linkmail', post.el
- return mail.href
+ if mail = $ '.useremail', post.el
+ return mail.pathname
false
subject: (post) ->
- sub = if post.isOP then $ '.filetitle', post.el else $ '.replytitle', post.el
- sub.textContent
+ $('.subject', post.el).textContent or false
comment: (post) ->
text = []
# XPathResult.ORDERED_NODE_SNAPSHOT_TYPE is 7
- nodes = d.evaluate './/br|.//text()', post.el.lastChild, null, 7, null
+ nodes = d.evaluate './/br|.//text()', post.el.lastElementChild, null, 7, null
for i in [0...nodes.snapshotLength]
text.push if data = nodes.snapshotItem(i).data then data else '\n'
text.join ''
filename: (post) ->
- {filesize} = post
- if filesize and file = $ 'span', filesize
+ {fileInfo} = post
+ if fileInfo and file = $ '.fileText > span', fileInfo
return file.title
false
dimensions: (post) ->
- {filesize} = post
- if filesize and match = filesize.textContent.match /\d+x\d+/
+ {fileInfo} = post
+ if fileInfo and match = fileInfo.textContent.match /\d+x\d+/
return match[0]
false
filesize: (post) ->
@@ -594,7 +584,7 @@ Filter =
md5: (post) ->
{img} = post
if img
- return img.getAttribute 'md5'
+ return img.dataset.md5
false
StrikethroughQuotes =
@@ -603,23 +593,22 @@ StrikethroughQuotes =
node: (post) ->
return if post.isInlined
for quote in post.quotes
- if (el = $.id quote.hash[1..]) and el.parentNode.parentNode.parentNode.hidden
+ if (el = $.id quote.hash[1..]) and el.hidden
$.addClass quote, 'filtered'
ReplyHiding.hide post.root if Conf['Recursive Filtering']
return
ExpandComment =
init: ->
- for a in $$ '.abbr > a'
- $.on a, 'click', ExpandComment.expand
+ for a in $$ '.abbr'
+ $.on a.firstElementChild, 'click', ExpandComment.expand
return
expand: (e) ->
e.preventDefault()
- [_, threadID, replyID] = @href.match /(\d+)#(\d+)/
+ [_, threadID, replyID] = @href.match /(\d+)#p(\d+)/
@textContent = "Loading #{replyID}..."
- threadID = @pathname.split('/').pop() or $.x('ancestor::div[@class="thread"]/div', @).id
a = @
- $.cache @pathname, (-> ExpandComment.parse @, a, threadID, replyID)
+ $.cache @pathname, -> ExpandComment.parse @, a, threadID, replyID
parse: (req, a, threadID, replyID) ->
if req.status isnt 200
a.textContent = "#{req.status} #{req.statusText}"
@@ -628,17 +617,15 @@ ExpandComment =
doc = d.implementation.createHTMLDocument ''
doc.documentElement.innerHTML = req.response
- Threading.op $('body > form', doc).firstChild
# Import the node to fix quote.hashes
# as they're empty when in a different document.
- node = d.importNode doc.getElementById replyID
+ node = d.importNode doc.getElementById "m#{replyID}"
quotes = node.getElementsByClassName 'quotelink'
for quote in quotes
- if quote.hash is href = quote.getAttribute 'href' # Add pathname to in-thread quotes
- quote.pathname = "/#{g.BOARD}/res/#{threadID}"
- else if href isnt quote.href # Fix cross-thread links, not cross-board ones
- quote.href = "res/#{href}"
+ href = quote.getAttribute 'href'
+ continue if href[0] is '/' # Cross-board quote
+ quote.href = "res/#{href}" # Fix pathnames
post =
el: node
threadId: threadID
@@ -654,35 +641,28 @@ ExpandComment =
QuoteOP.node post
if Conf['Indicate Cross-thread Quotes']
QuoteCT.node post
- $.replace a.parentNode.parentNode, node.lastChild
+ $.replace a.parentNode.parentNode, node
ExpandThread =
init: ->
- for span in $$ '.omittedposts'
+ for span in $$ '.summary'
a = $.el 'a',
textContent: "+ #{span.textContent}"
- className: 'omittedposts'
+ className: 'summary desktop'
href: 'javascript:;'
- $.on a, 'click', ExpandThread.cb.toggle
+ $.on a, 'click', -> ExpandThread.toggle @parentNode
$.replace span, a
- cb:
- toggle: ->
- thread = @parentNode
- ExpandThread.toggle thread
-
toggle: (thread) ->
- threadID = thread.firstChild.id
- pathname = "/#{g.BOARD}/res/#{threadID}"
- a = $ '.omittedposts', thread
+ pathname = "/#{g.BOARD}/res/#{thread.id[1..]}"
+ a = $ '.summary', thread
# \u00d7 is ×
switch a.textContent[0]
when '+'
- $('.op .container', thread)?.textContent = ''
a.textContent = a.textContent.replace '+', '\u00d7 Loading...'
- $.cache pathname, (-> ExpandThread.parse @, pathname, thread, a)
+ $.cache pathname, -> ExpandThread.parse @, thread, a
when '\u00d7'
a.textContent = a.textContent.replace '\u00d7 Loading...', '+'
@@ -695,14 +675,15 @@ ExpandThread =
when 'b', 'vg' then 3
when 't' then 1
else 5
- table = $.x "following::br[@clear]/preceding::table[#{num}]", a
- while (prev = table.previousSibling) and (prev.nodeName isnt 'A')
- $.rm prev
- for backlink in $$ '.backlink', $ '.op', thread
- $.rm backlink if !$.id backlink.hash[1..]
+ replies = $$ '.replyContainer', thread
+ replies.splice replies.length - num, num
+ for reply in replies
+ $.rm reply
+ for backlink in $$ '.backlink', a.previousElementSibling
+ $.rm backlink unless $.id backlink.hash[1..]
+ return
-
- parse: (req, pathname, thread, a) ->
+ parse: (req, thread, a) ->
if req.status isnt 200
a.textContent = "#{req.status} #{req.statusText}"
$.off a, 'click', ExpandThread.cb.toggle
@@ -713,74 +694,138 @@ ExpandThread =
doc = d.implementation.createHTMLDocument ''
doc.documentElement.innerHTML = req.response
- nodes = []
- for reply in $$ '.reply', doc
- table = d.importNode reply.parentNode.parentNode.parentNode
- for quote in $$ '.quotelink', table
- if quote.hash is href = quote.getAttribute 'href' # Add pathname to in-thread quotes
- quote.pathname = pathname
- else if href isnt quote.href # Fix cross-thread links, not cross-board ones
- quote.href = "res/#{href}"
- link = $ '.quotejs', table
- link.href = "res/#{thread.firstChild.id}##{reply.id}"
- link.nextSibling.href = "res/#{thread.firstChild.id}#q#{reply.id}"
- nodes.push table
+ threadID = thread.id[1..]
+ nodes = []
+ for reply in $$ '.replyContainer', doc
+ reply = d.importNode reply
+ for quote in $$ '.quotelink', reply
+ href = quote.getAttribute 'href'
+ continue if href[0] is '/' # Cross-board quote
+ quote.href = "res/#{href}" # Fix pathnames
+ id = reply.id[2..]
+ link = $ '.postInfo > .postNum > a:first-child', reply
+ link.href = "res/#{threadID}#p#{id}"
+ link.nextSibling.href = "res/#{threadID}#q#{id}"
+ nodes.push reply
# eat everything, then replace with fresh full posts
- while (next = a.nextSibling) and not next.clear #br[clear]
- $.rm next
- $.before next, nodes
+ for post in $$ '.summary ~ .replyContainer', a.parentNode
+ $.rm post
+ for backlink in $$ '.backlink', a.previousElementSibling
+ $.rm backlink unless $.id backlink.hash[1..]
+ $.after a, nodes
+
+ThreadHiding =
+ init: ->
+ hiddenThreads = $.get "hiddenThreads/#{g.BOARD}/", {}
+ for thread in $$ '.thread'
+ a = $.el 'a',
+ className: 'hide_thread_button'
+ innerHTML: '
[ - ]'
+ href: 'javascript:;'
+ $.on a, 'click', ThreadHiding.cb
+ $.prepend thread, a
+
+ if thread.id[1..] of hiddenThreads
+ ThreadHiding.hide thread
+ return
+
+ cb: ->
+ ThreadHiding.toggle @parentNode
+
+ toggle: (thread) ->
+ hiddenThreads = $.get "hiddenThreads/#{g.BOARD}/", {}
+ id = thread.id[1..]
+ if thread.hidden or /\bhidden_thread\b/.test thread.firstChild.className
+ ThreadHiding.show thread
+ delete hiddenThreads[id]
+ else
+ ThreadHiding.hide thread
+ hiddenThreads[id] = Date.now()
+ $.set "hiddenThreads/#{g.BOARD}/", hiddenThreads
+
+ hide: (thread) ->
+ unless Conf['Show Stubs']
+ thread.hidden = true
+ thread.nextElementSibling.hidden = true
+ return
+
+ return if thread.firstChild.className is 'block' # already hidden by filter
+
+ num = 0
+ if span = $ '.summary', thread
+ num = Number span.textContent.match /\d+/
+ num += $$('.opContainer ~ .replyContainer', thread).length
+ text = if num is 1 then '1 reply' else "#{num} replies"
+ opInfo = $('.op > .postInfo > .nameBlock', thread).textContent
+
+ a = $ '.hide_thread_button', thread
+ $.addClass a, 'hidden_thread'
+ a.firstChild.textContent = '[ + ]'
+ $.add a, $.tn " #{opInfo} (#{text})"
+
+ show: (thread) ->
+ a = $ '.hide_thread_button', thread
+ $.removeClass a, 'hidden_thread'
+ a.innerHTML = '
[ - ]'
+ thread.hidden = false
+ thread.nextElementSibling.hidden = false
ReplyHiding =
init: ->
- @td = $.el 'td',
- noWrap: true
- className: 'replyhider'
- innerHTML: '
[ - ]'
Main.callbacks.push @node
node: (post) ->
- return if post.class
- td = ReplyHiding.td.cloneNode true
- $.on td.firstChild, 'click', ReplyHiding.toggle
- $.replace post.el.previousSibling, td
+ return if post.isInlined or /\bop\b/.test post.class
+ button = post.root.firstElementChild
+ $.addClass button, 'hide_reply_button'
+ button.innerHTML = '
[ - ]'
+ $.on button.firstChild, 'click', ReplyHiding.toggle
if post.id of g.hiddenReplies
ReplyHiding.hide post.root
toggle: ->
- parent = @parentNode
- if parent.className is 'replyhider'
- ReplyHiding.hide parent.parentNode.parentNode.parentNode
- id = parent.nextSibling.id
- for quote in $$ ".quotelink[href='##{id}'], .backlink[href='##{id}']"
- $.addClass quote, 'filtered'
- g.hiddenReplies[id] = Date.now()
- else
- table = parent.nextSibling
- table.hidden = false
- $.rm parent
- id = table.firstChild.firstChild.lastChild.id
- for quote in $$ ".quotelink[href$='##{id}'], .backlink[href='##{id}']"
+ button = @parentNode
+ root = button.parentNode
+ id = root.id[2..]
+ quotes = $$ ".quotelink[href$='#p#{id}'], .backlink[href='#p#{id}']"
+ if /\bstub\b/.test button.className
+ ReplyHiding.show root
+ for quote in quotes
$.removeClass quote, 'filtered'
delete g.hiddenReplies[id]
+ else
+ ReplyHiding.hide root
+ for quote in quotes
+ $.addClass quote, 'filtered'
+ g.hiddenReplies[id] = Date.now()
$.set "hiddenReplies/#{g.BOARD}/", g.hiddenReplies
- hide: (table) ->
- return if table.hidden # already hidden by filter
-
- table.hidden = true
+ hide: (root) ->
+ button = root.firstElementChild
+ return if button.hidden # already hidden once by filter
+ button.hidden = true
+ el = root.lastElementChild
+ el.hidden = true
return unless Conf['Show Stubs']
- name = $('.commentpostername', table).textContent
- uid = $('.posteruid', table)?.textContent or ''
- trip = $('.postertrip', table)?.textContent or ''
+ stub = $.el 'div',
+ className: 'hide_reply_button stub'
+ innerHTML: '
[ + ] '
+ $.add stub.firstChild, $.tn $('.nameBlock', el).textContent
+ $.on stub.firstChild, 'click', ReplyHiding.toggle
+ $.after button, stub
- div = $.el 'div',
- className: 'stub'
- innerHTML: "
[ + ] #{name} #{uid} #{trip}"
- $.on div.firstChild, 'click', ReplyHiding.toggle
- $.before table, div
+ show: (root) ->
+ el = root.lastElementChild
+ button = root.firstElementChild
+ el.hidden = false
+ button.hidden = false
+
+ return unless Conf['Show Stubs']
+
+ $.rm button.nextElementSibling
Keybinds =
init: ->
@@ -839,11 +884,13 @@ Keybinds =
Keybinds.img thread, true
# Board Navigation
when Conf.zero
- window.location = "/#{g.BOARD}/0#0"
+ window.location = "/#{g.BOARD}/0#delform"
when Conf.nextPage
- $('input[value=Next]')?.click()
+ if link = $ 'link[rel=next]', d.head
+ window.location = link.href
when Conf.previousPage
- $('input[value=Previous]')?.click()
+ if link = $ 'link[rel=prev]', d.head
+ window.location.href = link.href
# Thread Navigation
when Conf.nextThread
return if g.REPLY
@@ -896,18 +943,18 @@ Keybinds =
if all
$.id('imageExpand').click()
else
- thumb = $ 'img[md5]', $('.replyhl', thread) or thread
+ thumb = $ 'img[data-md5]', $('.post.highlight', thread) or thread
ImageExpand.toggle thumb.parentNode
qr: (thread, quote) ->
if quote
- QR.quote.call $ '.quotejs + .quotejs', $('.replyhl', thread) or thread
+ QR.quote.call $ '.postInfo > .postNum > a[title="Quote this post"]', $('.post.highlight', thread) or thread
else
QR.open()
$('textarea', QR.el).focus()
open: (thread, tab) ->
- id = thread.firstChild.id
+ id = thread.id[1..]
url = "//boards.4chan.org/#{g.BOARD}/res/#{id}"
if tab
$.open url
@@ -915,40 +962,36 @@ Keybinds =
location.href = url
hl: (delta, thread) ->
- if td = $ '.replyhl', thread
- td.className = 'reply'
- td.removeAttribute 'tabindex'
- rect = td.getBoundingClientRect()
- if rect.bottom >= 0 and rect.top <= d.body.clientHeight # We're at least partially visible
- next =
- if delta is +1
- $.x 'following::td[@class="reply"]', td
- else
- $.x 'preceding::td[@class="reply"]', td
+ if post = $ '.reply.highlight', thread
+ $.removeClass post, 'highlight'
+ post.removeAttribute 'tabindex'
+ rect = post.getBoundingClientRect()
+ if rect.bottom >= 0 and rect.top <= d.documentElement.clientHeight # We're at least partially visible
+ next = $.x 'child::div[contains(@class,"post reply")]',
+ if delta is +1 then post.parentNode.nextElementSibling else post.parentNode.previousElementSibling
unless next
- td.className = 'replyhl'
- td.tabIndex = 0
- td.focus()
+ @focus post
return
return unless g.REPLY or $.x('ancestor::div[@class="thread"]', next) is thread
rect = next.getBoundingClientRect()
- if rect.top < 0 or rect.bottom > d.body.clientHeight
+ if rect.top < 0 or rect.bottom > d.documentElement.clientHeight
next.scrollIntoView delta is -1
- next.className = 'replyhl'
- next.tabIndex = 0
- next.focus()
+ @focus next
return
replies = $$ '.reply', thread
replies.reverse() if delta is -1
for reply in replies
rect = reply.getBoundingClientRect()
- if delta is +1 and rect.top >= 0 or delta is -1 and rect.bottom <= d.body.clientHeight
- reply.className = 'replyhl'
- reply.tabIndex = 0
- reply.focus()
+ if delta is +1 and rect.top >= 0 or delta is -1 and rect.bottom <= d.documentElement.clientHeight
+ @focus reply
return
+ focus: (post) ->
+ $.addClass post, 'highlight'
+ post.tabIndex = 0
+ post.focus()
+
Nav =
# ◀ ▶
init: ->
@@ -988,7 +1031,7 @@ Nav =
if full
return [thread, i, rect]
return thread
- return $ 'form[name=delform]'
+ return $ '.board'
scroll: (delta) ->
[thread, i, rect] = Nav.getThread true
@@ -1014,9 +1057,9 @@ QR =
link = $.el 'h1', innerHTML: "
#{if g.REPLY then 'Quick Reply' else 'New Thread'}"
$.on link.firstChild, 'click', ->
QR.open()
- $('select', QR.el).value = 'new' unless g.REPLY
+ $('select', QR.el).value = 'new' unless g.REPLY
$('textarea', QR.el).focus()
- $.before $('form[name=post]'), link
+ $.before $.id('postForm'), link
# Prevent original captcha input from being focused on reload.
script = $.el 'script',
@@ -1032,7 +1075,7 @@ QR =
$.on d, 'dragstart dragend', QR.drag
node: (post) ->
- $.on $('.quotejs + .quotejs', post.el), 'click', QR.quote
+ $.on $('.postInfo > .postNum > a[title="Quote this post"]', post.el), 'click', QR.quote
open: ->
if QR.el
@@ -1076,12 +1119,12 @@ QR =
$('.warning', QR.el).textContent = null
status: (data={}) ->
+ return unless QR.el
if g.dead
value = 404
disabled = true
QR.cooldown.auto = false
value = QR.cooldown.seconds or data.progress or value
- return unless QR.el
{input} = QR.status
input.value =
if QR.cooldown.auto and Conf['Cooldown']
@@ -1115,14 +1158,13 @@ QR =
e?.preventDefault()
QR.open()
unless g.REPLY
- $('select', QR.el).value = $.x('ancestor::div[@class="thread"]', @).firstChild.id
-
+ $('select', QR.el).value = $.x('ancestor::div[@class="thread"]', @).id[1..]
# Make sure we get the correct number, even with XXX censors
- id = @previousElementSibling.hash[1..]
+ id = @previousSibling.hash[2..]
text = ">>#{id}\n"
sel = window.getSelection()
- if (s = sel.toString()) and id is $.x('ancestor-or-self::blockquote/preceding-sibling::input', sel.anchorNode)?.name
+ if (s = sel.toString()) and id is $.x('ancestor-or-self::blockquote', sel.anchorNode)?.id.match(/\d+$/)[0]
s = s.replace /\n/g, '\n>'
text += ">#{s}\n"
@@ -1136,7 +1178,6 @@ QR =
ta.value[...caretPos] + text + ta.value[ta.selectionEnd..]
ta.focus()
# Move the caret to the end of the new quote.
- ta.selectionEnd = ta.selectionStart = caretPos + text.length
range = caretPos + text.length
ta.setSelectionRange range, range
@@ -1357,7 +1398,7 @@ QR =
@timeout = Date.now() + 26*$.MINUTE
challenge = @challenge.firstChild.value
@img.alt = challenge
- @img.src = "http://www.google.com/recaptcha/api/image?c=#{challenge}"
+ @img.src = "//www.google.com/recaptcha/api/image?c=#{challenge}"
@input.value = null
count: (count) ->
@input.placeholder = switch count
@@ -1405,7 +1446,7 @@ QR =
ta.style.cssText = $.get 'QR.size', ''
# Allow only this board's supported files.
- mimeTypes = $('.rules').firstElementChild.textContent.trim().match(/: (.+)/)[1].toLowerCase().replace /\w+/g, (type) ->
+ mimeTypes = $('ul.rules').firstElementChild.textContent.trim().match(/: (.+)/)[1].toLowerCase().replace /\w+/g, (type) ->
switch type
when 'jpg'
'image/jpeg'
@@ -1416,19 +1457,20 @@ QR =
QR.mimeTypes = mimeTypes.split ', '
# Add empty mimeType to avoid errors with URLs selected in Window's file dialog.
QR.mimeTypes.push ''
- fileInput = $ '[type=file]', QR.el
- fileInput.max = $('[name=MAX_FILE_SIZE]').value
+ fileInput = $ 'input[type=file]', QR.el
+ fileInput.max = $('input[name=MAX_FILE_SIZE]').value
fileInput.accept = mimeTypes
- QR.spoiler = !!$ '#com_submit + label'
+ QR.spoiler = !!$ 'input[name=spoiler]'
spoiler = $ '#spoilerLabel', QR.el
spoiler.hidden = !QR.spoiler
unless g.REPLY
# Make a list with visible threads and an option to create a new one.
threads = '
'
- for thread in $$ '.op'
- threads += "
"
+ for thread in $$ '.thread'
+ id = thread.id[1..]
+ threads += "
"
$.prepend $('.move > span', QR.el), $.el 'select'
innerHTML: threads
title: 'Create a new thread / Reply to a thread'
@@ -1454,7 +1496,7 @@ QR =
QR.selected[@name] = @value
# Disable auto-posting if you're typing in the first reply
# during the last 5 seconds of the cooldown.
- if QR.cooldown.auto and QR.selected is QR.replies[0] and parseInt(QR.status.input.value.match /\d+/) < 6
+ if QR.cooldown.auto and QR.selected is QR.replies[0] and 0 < QR.cooldown.seconds < 6
QR.cooldown.auto = false
# sync between tabs
$.sync 'QR.persona', (persona) ->
@@ -1463,7 +1505,7 @@ QR =
QR.selected[key] = val
$("[name=#{key}]", QR.el).value = val
- QR.status.input = $ '[type=submit]', QR.el
+ QR.status.input = $ 'input[type=submit]', QR.el
QR.status()
QR.cooldown.init()
QR.captcha.init()
@@ -1537,7 +1579,7 @@ QR =
upfile: reply.file
spoiler: reply.spoiler
mode: 'regist'
- pwd: if m = d.cookie.match(/4chan_pass=([^;]+)/) then decodeURIComponent m[1] else $('[name=pwd]').value
+ pwd: if m = d.cookie.match(/4chan_pass=([^;]+)/) then decodeURIComponent m[1] else $('input[name=pwd]').value
recaptcha_challenge_field: challenge
recaptcha_response_field: response + ' '
@@ -1566,7 +1608,7 @@ QR =
# Uploading...
QR.status progress: "#{Math.round e.loaded / e.total * 100}%"
- QR.ajax = $.ajax $('form[name=post]').action, callbacks, opts
+ QR.ajax = $.ajax $.id('postForm').parentNode.action, callbacks, opts
response: (html) ->
doc = d.implementation.createHTMLDocument ''
@@ -1730,16 +1772,13 @@ Options =
File Info Formatting is disabled.
Unread Favicon is disabled.
Unread favicons
@@ -1787,18 +1826,15 @@ Options =
$.on ta, 'change', $.cb.value
#rice
- (back = $ '[name=backlink]', dialog).value = Conf['backlink']
- (time = $ '[name=time]', dialog).value = Conf['time']
- (fileInfoR = $ '[name=fileInfoR]', dialog).value = Conf['fileInfoR']
- (fileInfoT = $ '[name=fileInfoT]', dialog).value = Conf['fileInfoT']
+ (back = $ '[name=backlink]', dialog).value = Conf['backlink']
+ (time = $ '[name=time]', dialog).value = Conf['time']
+ (fileInfo = $ '[name=fileInfo]', dialog).value = Conf['fileInfo']
$.on back, 'keyup', $.cb.value
$.on back, 'keyup', Options.backlink
$.on time, 'keyup', $.cb.value
$.on time, 'keyup', Options.time
- $.on fileInfoR, 'keyup', $.cb.value
- $.on fileInfoR, 'keyup', Options.fileInfo
- $.on fileInfoT, 'keyup', $.cb.value
- $.on fileInfoT, 'keyup', Options.fileInfo
+ $.on fileInfo, 'keyup', $.cb.value
+ $.on fileInfo, 'keyup', Options.fileInfo
favicon = $ 'select', dialog
favicon.value = Conf['favicon']
$.on favicon, 'change', $.cb.value
@@ -1831,8 +1867,7 @@ Options =
Options.backlink.call back
Options.time.call time
- Options.fileInfo.call fileInfoR
- Options.fileInfo.call fileInfoT
+ Options.fileInfo.call fileInfo
Options.favicon.call favicon
close: ->
@@ -1860,118 +1895,21 @@ Options =
backlink: ->
$.id('backlinkPreview').textContent = Conf['backlink'].replace /%id/, '123456789'
fileInfo: ->
- type = if @name is 'fileInfoR' then 0 else 1
FileInfo.data =
- link: '
1329791824.png'
- size: 996
+ link: 'javascript:;'
+ spoiler: true
+ size: '276'
unit: 'KB'
- resolution: '1366x768'
- fullname: '[a.f.k.] Sayonara Zetsubou Sensei - 09.avi_snapshot_03.34_[2011.02.20_06.58.00].jpg'
- shortname: '[a.f.k.] Sayonara Zetsubou Sen(...).jpg'
- type: type
+ resolution: '1280x720'
+ fullname: 'd9bb2efc98dd0df141a94399ff5880b7.jpg'
+ shortname: 'd9bb2efc98dd0df141a94399ff5880(...).jpg'
FileInfo.setFormats()
- $.id("#{@name}Preview").innerHTML = FileInfo.funks[type] FileInfo
+ $.id('fileInfoPreview').innerHTML = FileInfo.funk FileInfo
favicon: ->
Favicon.switch()
Unread.update true
@nextElementSibling.innerHTML = "

"
-Threading =
- op: (node) ->
- nodes = []
- until node.nodeName is 'BLOCKQUOTE'
- nodes.push node
- node = node.nextSibling
- nodes.push node # Add the blockquote.
- node = node.nextSibling
- op = $.el 'div',
- className: 'op'
- $.add op, nodes
- op.id = $('input', op).name
- $.before node, op
-
- thread: (node) ->
- node = Threading.op node
-
- return if g.REPLY
-
- nodes = []
- until node.nodeName is 'HR'
- nodes.push node
- node = node.nextElementSibling # Skip text nodes.
- div = $.el 'div',
- className: 'thread'
- $.add div, nodes
- $.before node, div
-
- node = node.nextElementSibling
- # {N,}SFW
- unless node.align or node.nodeName is 'CENTER'
- Threading.thread node
-
-ThreadHiding =
- init: ->
- hiddenThreads = $.get "hiddenThreads/#{g.BOARD}/", {}
- for thread in $$ '.thread'
- op = $ '.op', thread
- a = $.el 'a',
- textContent: '[ - ]'
- href: 'javascript:;'
- $.on a, 'click', ThreadHiding.cb
- $.prepend op, a
-
- if op.id of hiddenThreads
- ThreadHiding.hide thread
- return
-
- cb: ->
- ThreadHiding.toggle @parentNode.parentNode
-
- toggle: (thread) ->
- hiddenThreads = $.get "hiddenThreads/#{g.BOARD}/", {}
- id = $('.op', thread).id
- if thread.hidden or thread.firstChild.className is 'block'
- ThreadHiding.show thread
- delete hiddenThreads[id]
- else
- ThreadHiding.hide thread
- hiddenThreads[id] = Date.now()
- $.set "hiddenThreads/#{g.BOARD}/", hiddenThreads
-
- hide: (thread) ->
- unless Conf['Show Stubs']
- thread.hidden = true
- thread.nextSibling.hidden = true
- return
-
- return if thread.firstChild.className is 'block' # already hidden by filter
-
- num = 0
- if span = $ '.omittedposts', thread
- num = Number span.textContent.match(/\d+/)[0]
- num += $$('.op ~ table', thread).length
- text = if num is 1 then '1 reply' else "#{num} replies"
- op = $ '.op', thread
- name = $('.postername', op).textContent
- uid = $('.posteruid', op)?.textContent or ''
- trip = $('.postertrip', op)?.textContent or ''
-
- a = $.el 'a',
- innerHTML: "
[ + ] #{name} #{uid} #{trip} (#{text})"
- href: 'javascript:;'
- $.on a, 'click', ThreadHiding.cb
-
- div = $.el 'div',
- className: 'block'
-
- $.add div, a
- $.prepend thread, div
-
- show: (thread, id) ->
- $.rm $ '.block', thread
- thread.hidden = false
- thread.nextSibling.hidden = false
-
Updater =
init: ->
html = "
-#{Conf['Interval']}
"
@@ -1989,9 +1927,9 @@ Updater =
dialog = UI.dialog 'updater', 'bottom: 0; right: 0;', html
- @count = $ '#count', dialog
- @timer = $ '#timer', dialog
- @br = $ 'br[clear]'
+ @count = $ '#count', dialog
+ @timer = $ '#timer', dialog
+ @thread = $.id "t#{g.THREAD_ID}"
for input in $$ 'input', dialog
if input.type is 'checkbox'
@@ -2055,7 +1993,7 @@ Updater =
return
Updater.retryCoef = 10
- Updater.timer.textContent = '-' + Conf['Interval']
+ Updater.timer.textContent = "-#{Conf['Interval']}"
###
Status Code 304: Not modified
@@ -2066,29 +2004,30 @@ Updater =
if @status is 304
if Conf['Verbose']
Updater.count.textContent = '+0'
- Updater.count.className = null
+ Updater.count.className = null
return
Updater.lastModified = @getResponseHeader 'Last-Modified'
doc = d.implementation.createHTMLDocument ''
doc.documentElement.innerHTML = @response
- id = $('input', Updater.br.previousElementSibling).name
+ lastPost = Updater.thread.lastElementChild
+ id = lastPost.id[2..]
nodes = []
- for reply in $$('.reply', doc).reverse()
- break if reply.id <= id #make sure to not insert older posts
- nodes.push reply.parentNode.parentNode.parentNode #table
+ for reply in $$('.replyContainer', doc).reverse()
+ break if reply.id[2..] <= id #make sure to not insert older posts
+ nodes.push reply
- newPosts = nodes.length
- scroll = Conf['Scrolling'] && Updater.scrollBG() && newPosts &&
- Updater.br.previousElementSibling.getBoundingClientRect().bottom - d.body.clientHeight < 25
+ count = nodes.length
+ scroll = Conf['Scrolling'] && Updater.scrollBG() && count &&
+ lastPost.getBoundingClientRect().bottom - d.documentElement.clientHeight < 25
if Conf['Verbose']
- Updater.count.textContent = "+#{newPosts}"
- Updater.count.className = if newPosts then 'new' else null
+ Updater.count.textContent = "+#{count}"
+ Updater.count.className = if count then 'new' else null
- $.before Updater.br, nodes.reverse()
+ $.add Updater.thread, nodes.reverse()
if scroll
- Updater.br.previousSibling.scrollIntoView()
+ nodes[0].scrollIntoView()
timeout: ->
Updater.timeoutID = setTimeout Updater.timeout, 1000
@@ -2104,7 +2043,7 @@ Updater =
retry: ->
@count.textContent = 'Retry'
- @count.className = ''
+ @count.className = null
@update()
update: ->
@@ -2122,8 +2061,7 @@ Watcher =
$.add d.body, @dialog
#add watch buttons
- inputs = $$ '.op > input'
- for input in inputs
+ for input in $$ '.op input'
favicon = $.el 'img',
className: 'favicon'
$.on favicon, 'click', @cb.toggle
@@ -2202,12 +2140,13 @@ Anonymize =
init: ->
Main.callbacks.push @node
node: (post) ->
- return if post.class is 'inline'
- name = $ '.commentpostername, .postername', post.el
+ return if post.isInlined and not post.isCrosspost
+ name = $ '.name', post.el
name.textContent = 'Anonymous'
- node = name.nextElementSibling
- if node.className is 'postertrip' or node.nodeName is 'A'
- $.rm node
+ if (trip = name.nextElementSibling) and trip.className is 'postertrip'
+ $.rm trip
+ if (parent = name.parentNode).className is 'useremail' and not /^sage$/i.test parent.pathname
+ $.replace parent, name
Sauce =
init: ->
@@ -2228,7 +2167,7 @@ Sauce =
when '$2'
"' + img.href + '"
when '$3'
- "' + img.firstChild.getAttribute('md5').replace(/\=*$/, '') + '"
+ "' + img.firstChild.dataset.md5.replace(/\=*$/, '') + '"
when '$4'
g.BOARD
href = Function 'img', "return '#{href}'"
@@ -2242,23 +2181,23 @@ Sauce =
node: (post) ->
{img} = post
- return if post.class is 'inline' or not img
+ return if post.isInlined and not post.isCrosspost or not img
img = img.parentNode
nodes = []
for link in Sauce.links
- nodes.push $.tn(' '), link img
- $.add post.filesize, nodes
+ # \u00A0 is nbsp
+ nodes.push $.tn('\u00A0'), link img
+ $.add post.fileInfo, nodes
RevealSpoilers =
init: ->
Main.callbacks.push @node
node: (post) ->
{img} = post
- if not (img and /^Spoil/.test img.alt) or post.class is 'inline'
+ if not (img and /^Spoiler/.test img.alt) or post.isInlined and not post.isCrosspost
return
- img.removeAttribute 'height'
- img.removeAttribute 'width'
- img.src = "//thumbs.4chan.org#{img.parentNode.pathname.replace(/src(\/\d+).+$/, 'thumb$1s.jpg')}"
+ img.removeAttribute 'style'
+ img.src = "//thumbs.4chan.org#{img.parentNode.pathname.replace /src(\/\d+).+$/, 'thumb$1s.jpg'}"
Time =
init: ->
@@ -2271,11 +2210,11 @@ Time =
@parse =
if Date.parse('10/11/11(Tue)18:53') is 1318351980000
- (node) -> new Date Date.parse(node.textContent) + chanOffset*$.HOUR
+ (text) -> new Date Date.parse(text) + chanOffset*$.HOUR
else # Firefox and Opera do not parse 4chan's time format correctly
- (node) ->
+ (text) ->
[_, month, day, year, hour, min] =
- node.textContent.match /(\d+)\/(\d+)\/(\d+)\(\w+\)(\d+):(\d+)/
+ text.match /(\d+)\/(\d+)\/(\d+)\(\w+\)(\d+):(\d+)/
year = "20#{year}"
month-- # Months start at 0
hour = chanOffset + Number hour
@@ -2283,15 +2222,10 @@ Time =
Main.callbacks.push @node
node: (post) ->
- return if post.class is 'inline'
- # .posttime exists on every board except /f/
- node = $('.posttime', post.el) or $('span[id]', post.el).previousSibling
- Time.date = Time.parse node
- time = $.el 'time',
- textContent: ' ' + Time.funk(Time) + ' '
- # Set the datetime attribute, ISO'd.
- time.setAttribute 'datetime', Time.date.toISOString()
- $.replace node, time
+ return if post.isInlined and not post.isCrosspost
+ node = $ '.postInfo > .dateTime', post.el
+ Time.date = Time.parse node.textContent
+ node.textContent = Time.funk(Time)
foo: ->
code = Conf['time'].replace /%([A-Za-z])/g, (s, c) ->
if c of Time.formatters
@@ -2346,33 +2280,26 @@ FileInfo =
@setFormats()
Main.callbacks.push @node
node: (post) ->
- return if post.class is 'inline' or not node = post.filesize
- regexp = /^File: (<.+>)-\((?:Spoiler Image, )?([\d\.]+) (\w+), (\d+x\d+|PDF)/
- [_, link, size, unit, resolution] =
- node.innerHTML.match regexp
- data =
- link: link
- size: size
- unit: unit
- resolution: resolution
- if span = $ 'span', node
- data.fullname = span.title
- data.shortname = span.textContent
- data.type = +!span
- FileInfo.data = data
- node.innerHTML = FileInfo.funks[data.type] FileInfo
+ return if post.isInlined and not post.isCrosspost or not post.fileInfo
+ node = post.fileInfo.firstElementChild
+ alt = post.img.alt
+ span = $ 'span', node
+ FileInfo.data =
+ link: post.img.parentNode.href
+ spoiler: /^Spoiler/.test alt
+ size: alt.match(/\d+/)[0]
+ unit: alt.match(/\w+$/)[0]
+ resolution: span.previousSibling.textContent.match(/\d+x\d+|PDF/)[0]
+ fullname: span.title
+ shortname: span.textContent
+ node.innerHTML = FileInfo.funk FileInfo
setFormats: ->
- funks = []
- for i in [0..1]
- format = if i then Conf['fileInfoT'] else Conf['fileInfoR']
- param = if i then /%([BKlMrs])/g else /%([BKlLMnNrs])/g
- code = format.replace param, (s, c) ->
- if c of FileInfo.formatters
- "' + f.formatters.#{c}() + '"
- else
- s
- funks.push Function 'f', "return '#{code}'"
- @funks = funks
+ code = Conf['fileInfo'].replace /%([BKlLMnNprs])/g, (s, c) ->
+ if c of FileInfo.formatters
+ "' + f.formatters.#{c}() + '"
+ else
+ s
+ @funk = Function 'f', "return '#{code}'"
convertUnit: (unitT) ->
size = @data.size
unitF = @data.unit
@@ -2388,18 +2315,15 @@ FileInfo =
size = size.toFixed 2
"#{size} #{unitT}"
formatters:
- l: ->
- if FileInfo.data.type is 0
- FileInfo.data.link.replace />\d+\.\w+, ">#{@n()}<"
- else
- FileInfo.data.link
- L: -> FileInfo.data.link.replace />\d+\.\w+, ">#{FileInfo.data.fullname}<"
+ l: -> "
#{@n()}"
+ L: -> "
#{@N()}"
n: ->
if FileInfo.data.fullname is FileInfo.data.shortname
FileInfo.data.fullname
else
- "
#{FileInfo.data.fullname}#{FileInfo.data.shortname}"
+ "
#{FileInfo.data.shortname}#{FileInfo.data.fullname}"
N: -> FileInfo.data.fullname
+ p: -> if FileInfo.data.spoiler then 'Spoiler, ' else ''
s: -> "#{FileInfo.data.size} #{FileInfo.data.unit}"
B: -> FileInfo.convertUnit 'B'
K: -> FileInfo.convertUnit 'KB'
@@ -2407,13 +2331,14 @@ FileInfo =
r: -> FileInfo.data.resolution
GetTitle = (thread) ->
- el = $ '.filetitle', thread
- if not el.textContent
- el = $ 'blockquote', thread
- if not el.textContent
- el = $ '.postername', thread
+ op = $ '.op', thread
+ el = $ '.subject', op
+ unless el.textContent
+ el = $ 'blockquote', op
+ unless el.textContent
+ el = $ '.nameBlock', op
span = $.el 'span', innerHTML: el.innerHTML.replace /
/g, ' '
- "/#{g.BOARD}/ - #{span.textContent}"
+ "/#{g.BOARD}/ - #{span.textContent.trim()}"
TitlePost =
init: ->
@@ -2429,16 +2354,16 @@ QuoteBacklink =
quotes = {}
for quote in post.quotes
# Don't process >>>/b/.
- if qid = quote.hash[1..]
+ if qid = quote.hash[2..]
# Duplicate quotes get overwritten.
quotes[qid] = true
a = $.el 'a',
- href: "##{post.id}"
- className: if post.root.hidden then 'filtered backlink' else 'backlink'
+ href: "#p#{post.id}"
+ className: if post.el.hidden then 'filtered backlink' else 'backlink'
textContent: QuoteBacklink.funk post.id
for qid of quotes
# Don't backlink the OP.
- continue if !(el = $.id qid) or el.className is 'op' and !Conf['OP Backlinks']
+ continue if !(el = $.id "pi#{qid}") or !Conf['OP Backlinks'] and /\bop\b/.test el.parentNode.className
link = a.cloneNode true
if Conf['Quote Preview']
$.on link, 'mouseover', QuotePreview.mouseover
@@ -2446,13 +2371,12 @@ QuoteBacklink =
$.on link, 'click', QuoteInline.toggle
else
link.setAttribute 'onclick', "replyhl('#{post.id}');"
- unless (container = $ '.container', el) and container.parentNode is el
- container = $.el 'span', className: 'container'
- $.add container, [$.tn(' '), link]
- root = $('.reportbutton', el) or $('span[id]', el)
- $.after root, container
- else
- $.add container, [$.tn(' '), link]
+ unless container = $.id "blc#{qid}"
+ container = $.el 'span',
+ className: 'container'
+ id: "blc#{qid}"
+ $.add el, container
+ $.add container, [$.tn(' '), link]
return
QuoteInline =
@@ -2469,53 +2393,56 @@ QuoteInline =
toggle: (e) ->
return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0
e.preventDefault()
- id = @hash[1..]
+ id = @hash[2..]
if /\binlined\b/.test @className
QuoteInline.rm @, id
else
- return if $.x "ancestor::*[@id='#{id}']", @
+ return if $.x "ancestor::div[contains(@id,'p#{id}')]", @
QuoteInline.add @, id
@classList.toggle 'inlined'
add: (q, id) ->
- root = if q.parentNode.nodeName is 'FONT' then q.parentNode else if q.nextSibling then q.nextSibling else q
- if el = $.id id
- inline = QuoteInline.table id, el.innerHTML
- if (i = Unread.replies.indexOf el.parentNode.parentNode.parentNode) isnt -1
+ root = $.x 'ancestor::*[parent::blockquote]', q
+ if el = $.id "p#{id}"
+ if /\bop\b/.test el.className
+ $.removeClass el.parentNode, 'qphl'
+ else
+ $.removeClass el, 'qphl'
+ clonePost = QuoteInline.clone id, el
+ if /\bbacklink\b/.test q.className
+ $.after q.parentNode, clonePost
+ if Conf['Forward Hiding']
+ $.addClass el.parentNode, 'forwarded'
+ # Will only unhide if there's no inlined backlinks of it anymore.
+ ++el.dataset.forwarded or el.dataset.forwarded = 1
+ else
+ $.after root, clonePost
+ if (i = Unread.replies.indexOf el) isnt -1
Unread.replies.splice i, 1
Unread.update true
- if /\bbacklink\b/.test q.className
- $.after q.parentNode, inline
- if Conf['Forward Hiding']
- table = $.x 'ancestor::table', el
- $.addClass table, 'forwarded'
- # Will only unhide if there's no inlined backlinks of it anymore.
- ++table.title or table.title = 1
- return
- $.after root, inline
- else
- inline = $.el 'td',
- className: 'reply inline'
- id: "i#{id}"
- innerHTML: "Loading #{id}..."
- $.after root, inline
- {pathname} = q
- threadID = pathname.split('/').pop()
- $.cache pathname, (-> QuoteInline.parse @, pathname, id, threadID, inline)
+ return
+
+ inline = $.el 'div',
+ className: 'inline'
+ id: "i#{id}"
+ textContent: "Loading #{id}..."
+ $.after root, inline
+ {pathname} = q
+ $.cache pathname, -> QuoteInline.parse @, pathname, id, inline
rm: (q, id) ->
- #select the corresponding table or loading td
- table = $.x "following::*[@id='i#{id}']", q
- $.rm table
+ # select the corresponding inlined quote or loading quote
+ div = $.x "following::div[@id='i_pc#{id}']", q
+ $.rm div
return unless Conf['Forward Hiding']
- for inlined in $$ '.backlink.inlined', table
- table = $.x 'ancestor::table', $.id inlined.hash[1..]
- $.removeClass table, 'forwarded' unless --table.title
+ for inlined in $$ '.backlink.inlined', div
+ div = $.id inlined.hash[1..]
+ $.removeClass div.parentNode, 'forwarded' unless --div.dataset.forwarded
if /\bbacklink\b/.test q.className
- table = $.x 'ancestor::table', $.id id
- $.removeClass table, 'forwarded' unless --table.title
+ div = $.id "p#{id}"
+ $.removeClass div.parentNode, 'forwarded' unless --div.dataset.forwarded
- parse: (req, pathname, id, threadID, inline) ->
+ parse: (req, pathname, id, inline) ->
return unless inline.parentNode
if req.status isnt 200
@@ -2525,27 +2452,27 @@ QuoteInline =
doc = d.implementation.createHTMLDocument ''
doc.documentElement.innerHTML = req.response
- node =
- if id is threadID #OP
- Threading.op $('body > form', doc).firstChild
- else
- doc.getElementById id
- newInline = QuoteInline.table id, node.innerHTML
+ node = doc.getElementById "p#{id}"
+ newInline = QuoteInline.clone id, node
for quote in $$ '.quotelink', newInline
- if (href = quote.getAttribute 'href') is quote.hash #add pathname to normal quotes
- quote.pathname = pathname
- else if !g.REPLY and href isnt quote.href #fix x-thread links, not x-board ones
- quote.href = "res/#{href}"
- link = $ '.quotejs', newInline
- link.href = "#{pathname}##{id}"
+ href = quote.getAttribute 'href'
+ continue if href[0] is '/' # Cross-board quote
+ quote.href = "res/#{href}" # Fix pathnames
+ link = $ '.postInfo > .postNum > a:first-child', newInline
+ link.href = "#{pathname}#p#{id}"
link.nextSibling.href = "#{pathname}#q#{id}"
- $.addClass newInline, 'crossquote'
+ $.addClass newInline, 'crosspost'
$.replace inline, newInline
- table: (id, html) ->
- $.el 'table',
- className: 'inline'
- id: "i#{id}"
- innerHTML: "
| #{html} |
"
+
+ clone: (id, el) ->
+ clone = $.el 'div',
+ className: 'postContainer inline'
+ id: "i_pc#{id}"
+ $.add clone, el.cloneNode true
+ for node in $$ '[id]', clone
+ # Don't mess with other features
+ node.id = "i_#{node.id}"
+ clone
QuotePreview =
init: ->
@@ -2560,36 +2487,37 @@ QuotePreview =
return if /\binlined\b/.test @className
qp = UI.el = $.el 'div',
id: 'qp'
- className: 'reply dialog'
+ className: 'reply dialog post'
$.add d.body, qp
- id = @hash[1..]
- if el = $.id id
+ id = @hash[2..]
+ if el = $.id "p#{id}"
qp.innerHTML = el.innerHTML
- $.addClass el, 'qphl' if Conf['Quote Highlighting']
- node =
- if /\bbacklink\b/.test @className
- @parentNode
+ if Conf['Quote Highlighting']
+ if /\bop\b/.test el.className
+ $.addClass el.parentNode, 'qphl'
else
- $.x 'ancestor::blockquote', @
- replyID = $.x('preceding-sibling::input', node).name
+ $.addClass el, 'qphl'
+ replyID = $.x('ancestor::div[contains(@class,"postContainer")]', @).id[2..]
for quote in $$ '.quotelink, .backlink', qp
- if quote.hash[1..] is replyID
+ if quote.hash[2..] is replyID
$.addClass quote, 'forwardlink'
else
qp.textContent = "Loading #{id}..."
- threadID = @pathname.split('/').pop() or $.x('ancestor::div[@class="thread"]', @).firstChild.id
- $.cache @pathname, (-> QuotePreview.parse @, id, threadID)
+ $.cache @pathname, -> QuotePreview.parse @, id
UI.hover e
$.on @, 'mousemove', UI.hover
$.on @, 'mouseout click', QuotePreview.mouseout
mouseout: ->
- if el = $.id @hash[1..]
- $.removeClass el, 'qphl'
UI.hoverend()
+ if el = $.id @hash[1..]
+ if /\bop\b/.test el.className
+ $.removeClass el.parentNode, 'qphl'
+ else
+ $.removeClass el, 'qphl'
$.off @, 'mousemove', UI.hover
$.off @, 'mouseout click', QuotePreview.mouseout
- parse: (req, id, threadID) ->
+ parse: (req, id) ->
return unless (qp = UI.el) and qp.textContent is "Loading #{id}..."
if req.status isnt 200
@@ -2599,16 +2527,15 @@ QuotePreview =
doc = d.implementation.createHTMLDocument ''
doc.documentElement.innerHTML = req.response
- node =
- if id is threadID #OP
- Threading.op $('body > form', doc).firstChild
- else
- doc.getElementById id
+ node = doc.getElementById "p#{id}"
qp.innerHTML = node.innerHTML
post =
- root: qp
- filesize: $ '.filesize', qp
- img: $ 'img[md5]', qp
+ el: qp
+ if fileInfo = $ '.fileInfo', qp
+ img = fileInfo.nextElementSibling.firstElementChild
+ if img.alt isnt 'File deleted.'
+ post.fileInfo = fileInfo
+ post.img = img
if Conf['Image Auto-Gif']
AutoGif.node post
if Conf['Time Formatting']
@@ -2620,9 +2547,9 @@ QuoteOP =
init: ->
Main.callbacks.push @node
node: (post) ->
- return if post.class is 'inline'
+ return if post.isInlined and not post.isCrosspost
for quote in post.quotes
- if quote.hash[1..] is post.threadId
+ if quote.hash[2..] is post.threadId
# \u00A0 is nbsp
$.add quote, $.tn '\u00A0(OP)'
return
@@ -2631,7 +2558,7 @@ QuoteCT =
init: ->
Main.callbacks.push @node
node: (post) ->
- return if post.class is 'inline'
+ return if post.isInlined and not post.isCrosspost
for quote in post.quotes
unless quote.hash
# Make sure this isn't a link to the board we're on.
@@ -2647,11 +2574,11 @@ Quotify =
init: ->
Main.callbacks.push @node
node: (post) ->
- return if post.class is 'inline'
+ return if post.isInlined and not post.isCrosspost
# XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE is 6
# Get all the text nodes that are not inside an anchor.
- snapshot = d.evaluate './/text()[not(parent::a)]', post.el.lastChild, null, 6, null
+ snapshot = d.evaluate './/text()[not(parent::a)]', post.el.lastElementChild, null, 6, null
for i in [0...snapshot.snapshotLength]
node = snapshot.snapshotItem i
@@ -2675,14 +2602,14 @@ Quotify =
m[1]
else
# Get the post's board, whether it's inlined or not.
- $('.quotejs', post.el).pathname.split('/')[1]
+ $('.postInfo > .postNum > a:first-child', post.el).pathname.split('/')[1]
nodes.push a = $.el 'a',
# \u00A0 is nbsp
textContent: "#{quote}\u00A0(Dead)"
if board is g.BOARD and $.id id
- a.href = "##{id}"
+ a.href = "#p#{id}"
a.className = 'quotelink'
a.setAttribute 'onclick', "replyhl('#{id}');"
else
@@ -2702,14 +2629,14 @@ Quotify =
ReportButton =
init: ->
@a = $.el 'a',
- className: 'reportbutton'
+ className: 'report_button'
innerHTML: '[ ! ]'
href: 'javascript:;'
Main.callbacks.push @node
node: (post) ->
- unless a = $ '.reportbutton', post.el
+ unless a = $ '.report_button', post.el
a = ReportButton.a.cloneNode true
- $.after $('span[id]', post.el), [$.tn(' '), a]
+ $.add $('.postInfo', post.el), a
$.on a, 'click', ReportButton.report
report: ->
url = "//sys.4chan.org/#{g.BOARD}/imgboard.php?mode=report&no=#{$.x('preceding-sibling::input', @).name}"
@@ -2739,7 +2666,7 @@ ThreadStats =
imgcount = $.id 'imagecount'
imgcount.textContent = ++ThreadStats.images
if ThreadStats.images > ThreadStats.imgLimit
- imgcount.className = 'warning'
+ $.addClass imgcount, 'warning'
Unread =
init: ->
@@ -2755,12 +2682,13 @@ Unread =
if (index = Unread.foresee.indexOf post.id) isnt -1
Unread.foresee.splice index, 1
return
- return if post.root.hidden or post.class
- count = Unread.replies.push post.root
+ {el} = post
+ return if el.hidden or /\bop\b/.test(post.class) or post.isInlined
+ count = Unread.replies.push el
Unread.update count is 1
scroll: ->
- height = d.body.clientHeight
+ height = d.documentElement.clientHeight
for reply, i in Unread.replies
{bottom} = reply.getBoundingClientRect()
if bottom > height #post is not completely read
@@ -2915,14 +2843,15 @@ AutoGif =
return if g.BOARD is 'gif'
Main.callbacks.push @node
node: (post) ->
- return if post.root.hidden or not post.img
- src = post.img.parentNode.href
- if /gif$/.test(src) and !/spoiler/.test post.img.src
- img = $.el 'img'
- $.on img, 'load', ->
+ {img} = post
+ return if post.el.hidden or not img
+ src = img.parentNode.href
+ if /gif$/.test(src) and !/spoiler/.test img.src
+ gif = $.el 'img'
+ $.on gif, 'load', ->
# Replace the thumbnail once the GIF has finished loading.
- post.img.src = src
- img.src = src
+ img.src = src
+ gif.src = src
ImageExpand =
init: ->
@@ -2933,7 +2862,7 @@ ImageExpand =
return unless post.img
a = post.img.parentNode
$.on a, 'click', ImageExpand.cb.toggle
- if ImageExpand.on and !post.root.hidden and post.class isnt 'inline'
+ if ImageExpand.on and !post.el.hidden
ImageExpand.expand post.img
cb:
toggle: (e) ->
@@ -2943,7 +2872,7 @@ ImageExpand =
all: ->
ImageExpand.on = @checked
if ImageExpand.on #expand
- thumbs = $$ 'img[md5]'
+ thumbs = $$ 'img[data-md5]'
if Conf['Expand From Current']
for thumb, i in thumbs
if thumb.getBoundingClientRect().top > 0
@@ -2952,7 +2881,7 @@ ImageExpand =
for thumb in thumbs
ImageExpand.expand thumb
else #contract
- for thumb in $$ 'img[md5][hidden]'
+ for thumb in $$ 'img[data-md5][hidden]'
ImageExpand.contract thumb
return
typeChange: ->
@@ -2965,7 +2894,7 @@ ImageExpand =
klass = 'fitheight'
when 'fit screen'
klass = 'fitwidth fitheight'
- $('body > form').className = klass
+ $.id('delform').className = klass
if /\bfitheight\b/.test klass
$.on window, 'resize', ImageExpand.resize
unless ImageExpand.style
@@ -3033,11 +2962,10 @@ ImageExpand =
$.on select, 'change', ImageExpand.cb.typeChange
$.on $('input', controls), 'click', ImageExpand.cb.all
- form = $ 'body > form'
- $.prepend form, controls
+ $.prepend $.id('delform'), controls
resize: ->
- ImageExpand.style.textContent = ".fitheight img[md5] + img {max-height:#{d.body.clientHeight}px;}"
+ ImageExpand.style.textContent = ".fitheight img[data-md5] + img {max-height:#{d.documentElement.clientHeight}px;}"
Main =
init: ->
@@ -3050,12 +2978,10 @@ Main =
g.REPLY = true
g.THREAD_ID = pathname[2]
- #load values from localStorage
+ # Load values from localStorage.
for key, val of Conf
Conf[key] = $.get key, val
- $.on window, 'message', Main.message
-
switch location.hostname
when 'sys.4chan.org'
if /report/.test location.search
@@ -3070,12 +2996,13 @@ Main =
$.ready Options.init
if Conf['Quick Reply'] and Conf['Hide Original Post Form'] and g.BOARD isnt 'f'
- Main.css += 'form[name=post] { display: none; }'
+ Main.css += '#postForm { display: none; }'
Main.addStyle()
now = Date.now()
if Conf['Check for Updates'] and $.get('lastUpdate', 0) < now - 6*$.HOUR
+ $.on window, 'message', Main.message
$.ready -> $.add d.head, $.el 'script', src: 'https://raw.github.com/mayhemydg/4chan-x/master/latest.js'
$.set 'lastUpdate', now
@@ -3160,10 +3087,10 @@ Main =
return
$.addClass d.body, "chanx_#{Main.version.split('.')[1]}"
$.addClass d.body, $.engine
- for nav in ['navtop', 'navbot']
- $.addClass $("a[href$='/#{g.BOARD}/']", $.id nav), 'current'
- form = $ 'form[name=delform]'
- Threading.thread form.firstElementChild
+ for nav in ['boardNavDesktop', 'boardNavDesktopFoot']
+ if a = $ "a[href$='/#{g.BOARD}/']", $.id nav
+ # Gotta make it work in temporary boards.
+ $.addClass a, 'current'
Favicon.init()
# Major features.
@@ -3197,7 +3124,7 @@ Main =
else #not reply
if Conf['Thread Hiding']
- setTimeout -> ThreadHiding.init()
+ ThreadHiding.init()
if Conf['Thread Expansion']
setTimeout -> ExpandThread.init()
@@ -3208,18 +3135,19 @@ Main =
if Conf['Index Navigation']
setTimeout -> Nav.init()
+ board = $ '.board'
nodes = []
- for node in $$ '.op, a + table', form
+ for node in $$ '.postContainer', board
nodes.push Main.preParse node
Main.node nodes, true
if MutationObserver = window.WebKitMutationObserver or window.MozMutationObserver or window.OMutationObserver or window.MutationObserver
observer = new MutationObserver Main.observer
- observer.observe form,
+ observer.observe board,
childList: true
subtree: true
else
- $.on form, 'DOMNodeInserted', Main.listener
+ $.on board, 'DOMNodeInserted', Main.listener
flatten: (parent, obj) ->
if obj instanceof Array
@@ -3244,18 +3172,25 @@ Main =
window.location = "https://raw.github.com/mayhemydg/4chan-x/#{version}/4chan_x.user.js"
preParse: (node) ->
- klass = node.className
+ rootClass = node.className
+ el = $ '.post', node
post =
- root: node
- el: if klass is 'op' then node else node.firstChild.firstChild.lastChild
- class: klass
- id: node.getElementsByTagName('input')[0].name
- threadId: g.THREAD_ID or $.x('ancestor::div[@class="thread"]', node).firstChild.id
- isInlined: /\binline\b/.test klass
- filesize: node.getElementsByClassName('filesize')[0] or false
- quotes: node.getElementsByClassName 'quotelink'
- backlinks: node.getElementsByClassName 'backlink'
- post.img = if post.filesize then node.getElementsByTagName('img')[0] else false
+ root: node
+ el: el
+ class: el.className
+ id: el.id[1..]
+ threadId: g.THREAD_ID or $.x('ancestor::div[@class="thread"]', node).id[1..]
+ isInlined: /\binline\b/.test rootClass
+ isCrosspost: /\bcrosspost\b/.test rootClass
+ quotes: el.getElementsByClassName 'quotelink'
+ backlinks: el.getElementsByClassName 'backlink'
+ fileInfo: false
+ img: false
+ if fileInfo = $ '.fileInfo', el
+ img = fileInfo.nextElementSibling.firstElementChild
+ if img.alt isnt 'File deleted.'
+ post.fileInfo = fileInfo
+ post.img = img
post
node: (nodes, notify) ->
for callback in Main.callbacks
@@ -3268,19 +3203,22 @@ Main =
nodes = []
for mutation in mutations
for addedNode in mutation.addedNodes
- nodes.push Main.preParse addedNode if addedNode.nodeName is 'TABLE'
+ if /\bpostContainer\b/.test addedNode.className
+ nodes.push Main.preParse addedNode
Main.node nodes if nodes.length
listener: (e) ->
{target} = e
- Main.node [Main.preParse target] if target.nodeName is 'TABLE'
+ if /\bpostContainer\b/.test addedNode.className
+ Main.node [Main.preParse target]
namespace: '4chan_x.'
version: '2.29.5'
callbacks: []
css: '
/* dialog styling */
-.dialog {
+.dialog.reply {
border: 1px solid rgba(0,0,0,.25);
+ padding: 0;
}
.move {
cursor: move;
@@ -3291,16 +3229,25 @@ label, .favicon {
a[href="javascript:;"] {
text-decoration: none;
}
-
-.block ~ *,
-#content > [name=tab]:not(:checked) + div,
-#updater:not(:hover) > :not(.move),
-#qp > input, #qp .inline, .forwarded {
- display: none;
+.warning {
+ color: red;
}
-.autohide:not(:hover) > form {
- display: none;
+.hide_thread_button {
+ float: left;
+}
+
+.hidden_thread ~ *,
+[hidden],
+#content > [name=tab]:not(:checked) + div,
+#updater:not(:hover) > :not(.move),
+.autohide:not(:hover) > form,
+#qp input, #qp .inline, .forwarded {
+ display: none !important;
+}
+
+h1 {
+ text-align: center;
}
#qr > .move {
min-width: 300px;
@@ -3441,6 +3388,8 @@ a[href="javascript:;"] {
}
.field {
border: 1px solid #CCC;
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
color: #333;
font: 13px sans-serif;
margin: 0;
@@ -3464,6 +3413,7 @@ textarea.field {
min-height: 120px;
}
.field:only-child {
+ display: block;
min-width: 100%;
}
.captcha {
@@ -3473,6 +3423,7 @@ textarea.field {
text-align: center;
}
.captcha > img {
+ display: block;
height: 57px;
width: 300px;
}
@@ -3487,41 +3438,21 @@ textarea.field {
width: 30%;
}
-.new {
- background: lime;
-}
-.warning {
- color: red;
-}
-.replyhider {
- vertical-align: top;
-}
-
-.filesize + br + a {
- float: left;
- pointer-events: none;
-}
-.filename:hover > .fntrunc,
-.filename:not(:hover) > .fnfull {
+.fileText:hover .fntrunc,
+.fileText:not(:hover) .fnfull {
display: none;
}
-img[md5], img[md5] + img {
- pointer-events: all;
-}
-.fitwidth img[md5] + img {
+.fitwidth img[data-md5] + img {
max-width: 100%;
}
-.gecko > .fitwidth img[md5] + img,
-.presto > .fitwidth img[md5] + img {
- width: 100%;
-}
+
/* revealed spoilers do not have height/width,
this fixes "expanded" auto-gifs */
-img[md5] {
+.op > div > a > img[data-md5] {
max-height: 252px;
max-width: 252px;
}
-input ~ a > img[md5] {
+.reply > div > a > img[data-md5] {
max-height: 127px;
max-width: 127px;
}
@@ -3574,18 +3505,20 @@ input ~ a > img[md5] {
#options label {
text-decoration: underline;
}
-#content > div {
+#content {
height: 450px;
overflow: auto;
}
#content textarea {
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
margin: 0;
min-height: 100px;
resize: vertical;
width: 100%;
}
#sauces {
- height: 320px;
+ height: 300px;
}
#updater {
@@ -3598,9 +3531,8 @@ input ~ a > img[md5] {
border: none;
background: transparent;
}
-
-#stats {
- border: none;
+.new {
+ background: lime;
}
#watcher {
@@ -3624,36 +3556,44 @@ input ~ a > img[md5] {
text-decoration: underline;
}
-#qp {
- padding-bottom: 5px;
-}
-#qp > a > img {
+#qp img {
max-height: 300px;
max-width: 500px;
}
.qphl {
outline: 2px solid rgba(216, 94, 49, .7);
}
+.qphl.opContainer {
+ outline-offset: -2px;
+}
+div.opContainer {
+ display: block !important;
+}
.inlined {
opacity: .5;
}
-.inline .reply {
+.inline {
+ overflow: hidden;
background-color: rgba(255, 255, 255, 0.15);
border: 1px solid rgba(128, 128, 128, 0.5);
}
-.filetitle, .replytitle, .postername, .commentpostername, .postertrip {
+.inline .post {
background: none;
+ border: none;
}
-.filter_highlight.op,
-.filter_highlight > td[id] {
+.filter_highlight.thread > .opContainer {
+ box-shadow: inset 5px 0 rgba(255,0,0,0.5);
+}
+.filter_highlight > .reply {
box-shadow: -5px 0 rgba(255,0,0,0.5);
}
.filtered {
- text-decoration: line-through;
+ text-decoration: underline line-through;
}
.quotelink.forwardlink,
.backlink.forwardlink {
- color: #4C4CA9;
+ text-decoration: none;
+ border-bottom: 1px dashed;
}
'