Fix classes

This commit is contained in:
Zixaphir 2015-01-12 03:00:41 -07:00
parent 02fd87266e
commit ba66d077cd
12 changed files with 438 additions and 249 deletions

View File

@ -3141,26 +3141,24 @@
};
Callbacks.prototype.execute = function(nodes) {
var cb, err, errors, i, j, name, node;
i = 0;
while (name = this.keys[i++]) {
j = 0;
cb = this[name];
while (node = nodes[j++]) {
try {
if (!cb.disconnected) {
cb.call(node);
}
} catch (_error) {
err = _error;
if (!errors) {
errors = [];
}
errors.push({
message: ['"', name, '" crashed on node ', this.type, ' No.', node.ID, ' (', node.board, ').'].join(''),
error: err
});
var err, errors, name, node, _i, _j, _len, _len1, _ref;
_ref = this.keys;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
name = _ref[_i];
try {
for (_j = 0, _len1 = nodes.length; _j < _len1; _j++) {
node = nodes[_j];
this[name].call(node);
}
} catch (_error) {
err = _error;
if (!errors) {
errors = [];
}
errors.push({
message: ['"', name, '" crashed on node ', this.type, ' No.', node.ID, ' (', node.board, ').'].join(''),
error: err
});
}
}
if (errors) {
@ -3216,13 +3214,16 @@
}
Thread.prototype.setPage = function(pageNum) {
var icon, key, _i, _len, _ref;
icon = $('.page-num', this.OP.nodes.info);
_ref = ['title', 'textContent'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
key = _ref[_i];
icon[key] = icon[key].replace(/\d+/, pageNum);
var icon, info, quote, _ref;
_ref = this.OP.nodes, info = _ref.info, quote = _ref.quote;
if (!(icon = $('.page-num', info))) {
icon = $.el('span', {
className: 'page-num'
});
$.after(quote, [$.tn(' '), icon]);
}
icon.title = "This thread is on page " + pageNum + " in the original index.";
icon.textContent = "[" + pageNum + "]";
if (this.catalogView) {
return this.catalogView.nodes.pageCount.textContent = pageNum;
}
@ -3239,7 +3240,7 @@
};
Thread.prototype.setStatus = function(type, status) {
var name, typeLC;
var name;
name = "is" + type;
if (this[name] === status) {
return;
@ -3248,7 +3249,6 @@
if (!this.OP) {
return;
}
typeLC = type.toLowerCase();
this.setIcon('Sticky', this.isSticky);
this.setIcon('Closed', this.isClosed && !this.isArchived);
return this.setIcon('Archived', this.isArchived);
@ -3275,7 +3275,7 @@
title: type,
className: "" + typeLC + "Icon retina"
});
root = type !== 'Sticky' && this.isSticky ? $('.stickyIcon', this.OP.nodes.info) : $('.page-num', this.OP.nodes.info) || $('[title="Reply to this post"]', this.OP.nodes.info);
root = type !== 'Sticky' && this.isSticky ? $('.stickyIcon', this.OP.nodes.info) : $('.page-num', this.OP.nodes.info) || this.OP.nodes.quote;
$.after(root, [$.tn(' '), icon]);
if (!this.catalogView) {
return;
@ -3349,11 +3349,12 @@
this.board = this.thread.board;
this.nodes = {
root: root,
thumb: $('.thumb', root),
icons: $('.thread-icons', root),
thumb: $('.catalog-thumb', root),
icons: $('.catalog-icons', root),
postCount: $('.post-count', root),
fileCount: $('.file-count', root),
pageCount: $('.page-count', root)
pageCount: $('.page-count', root),
comment: $('.comment', root)
};
this.thread.catalogView = this;
}
@ -3378,17 +3379,18 @@
}
this.ID = +root.id.slice(2);
this.fullID = "" + this.board + "." + this.ID;
post = $('.post', root);
info = $('.postInfo', post);
if (that.isOriginalMarkup) {
this.cleanup(root, post);
}
post = $('.post', root);
info = $('.postInfo', post);
root.dataset.fullID = this.fullID;
this.nodes = {
root: root,
post: post,
info: info,
nameBlock: $('.nameBlock', info),
quote: $('.postNum > a:nth-of-type(2)', info),
comment: $('.postMessage', post),
links: [],
quotelinks: [],
@ -3396,8 +3398,12 @@
};
if (!(this.isReply = $.hasClass(post, 'reply'))) {
this.thread.OP = this;
this.thread.isArchived = !!$('.archivedIcon', info);
this.thread.isSticky = !!$('.stickyIcon', info);
this.thread.isClosed = !!$('.closedIcon', info);
this.thread.isClosed = this.thread.isArchived || !!$('.closedIcon', info);
if (this.thread.isArchived) {
this.thread.kill();
}
}
this.info = {};
this.info.nameBlock = Conf['Anonymize'] ? 'Anonymous' : this.nodes.nameBlock.textContent.trim();
@ -3448,27 +3454,45 @@
}
Post.prototype.parseComment = function() {
var bq, i, node, nodes, text;
var bq, i, node, nodes, spoilers;
this.nodes.comment.normalize();
bq = this.nodes.comment.cloneNode(true);
nodes = $$('.abbr, .exif, b', bq);
nodes = $$('.abbr, .exif, b, marquee', bq);
i = 0;
while (node = nodes[i++]) {
$.rm(node);
}
this.info.comment = this.nodesToText(bq);
spoilers = $$('s', bq);
return this.info.commentSpoilered = (function() {
var _i, _len;
if (spoilers.length) {
for (_i = 0, _len = spoilers.length; _i < _len; _i++) {
node = spoilers[_i];
$.replace(node, $.tn('[spoiler]'));
}
return this.nodesToText(bq);
} else {
return this.info.comment;
}
}).call(this);
};
Post.prototype.nodesToText = function(bq) {
var i, node, nodes, text;
text = "";
nodes = $.X('.//br|.//text()', bq);
i = 0;
while (node = nodes.snapshotItem(i++)) {
text += node.data || '\n';
}
return this.info.comment = text.trim().replace(/\s+$/gm, '');
return text.trim().replace(/\s+$/gm, '');
};
Post.prototype.parseQuotes = function() {
var quotelink, _i, _len, _ref;
this.quotes = [];
_ref = $$('.quotelink', this.nodes.comment);
_ref = $$(':not(pre) > .quotelink', this.nodes.comment);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quotelink = _ref[_i];
this.parseQuote(quotelink);
@ -3477,7 +3501,7 @@
Post.prototype.parseQuote = function(quotelink) {
var fullID, match;
if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/thread\/\d+.*\#p(\d+)$/))) {
if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/(?:res|thread)\/\d+(?:\/[^#]*)?#p(\d+)$/))) {
return;
}
this.nodes.quotelinks.push(quotelink);
@ -3511,30 +3535,26 @@
size *= 1024;
}
this.file.sizeInBytes = size;
this.file.thumbURL = that.isArchived ? thumb.src : "" + location.protocol + "//t.4cdn.org/" + this.board + "/" + (this.file.URL.match(/(\d+)\./)[1]) + "s.jpg";
this.file.thumbURL = "" + location.protocol + "//t.4cdn.org/" + this.board + "/" + (this.file.URL.match(/(\d+)\./)[1]) + "s.jpg";
this.file.isImage = /(jpg|png|gif)$/i.test(this.file.URL);
this.file.isVideo = /webm$/i.test(this.file.URL);
nameNode = $('a', fileText);
if (this.file.isImage || this.file.isVideo) {
this.file.dimensions = fileText.childNodes[2].data.match(/\d+x\d+/)[0];
this.file.dimensions = nameNode.nextSibling.textContent.match(/\d+x\d+/)[0];
}
return this.file.name = !this.file.isSpoiler && (nameNode = $('a', fileText)) ? nameNode.title || nameNode.textContent : fileText.title;
return this.file.name = fileText.title || nameNode.title || nameNode.textContent;
};
Post.prototype.cleanup = function(root, post) {
var node, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;
Post.prototype.cleanup = function(root) {
var node, _i, _j, _len, _len1, _ref, _ref1;
_ref = $$('.mobile', root);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i];
$.rm(node);
}
_ref1 = $$('[id]:not(.exif)', post);
_ref1 = $$('.desktop', root);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
node = _ref1[_j];
node.removeAttribute('id');
}
_ref2 = $$('.desktop', root);
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
node = _ref2[_k];
$.rmClass(node, 'desktop');
}
};
@ -3606,9 +3626,6 @@
return;
}
this.isHidden = false;
this.labels = this.labels.filter(function(label) {
return !/^(Manually hidden|Recursively hidden|Hidden by)/.test(label);
});
_ref = Get.allQuotelinksLinkingTo(this);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quotelink = _ref[_i];
@ -3637,8 +3654,7 @@
return delete this.nodes.stub;
};
Post.prototype.highlight = function(label, highlight, top) {
this.labels.push(label);
Post.prototype.highlight = function(highlight, top) {
if (__indexOf.call(this.highlights, highlight) < 0) {
this.highlights.push(highlight);
$.addClass(this.nodes.root, highlight);
@ -3751,7 +3767,7 @@
__extends(Clone, _super);
function Clone(origin, context, contractThumb) {
var file, info, inline, inlined, key, nodes, post, root, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
var file, info, inline, inlined, key, nodes, post, root, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3;
this.origin = origin;
this.context = context;
_ref = ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply'];
@ -3760,13 +3776,15 @@
this[key] = origin[key];
}
nodes = origin.nodes;
root = nodes.root.cloneNode(true);
root = contractThumb ? this.cloneWithoutVideo(nodes.root) : nodes.root.cloneNode(true);
post = $('.post', root);
info = $('.postInfo', post);
this.nodes = {
root: root,
post: post,
info: info,
nameBlock: $('.nameBlock', info),
quote: $('.postNum > a:nth-of-type(2)', info),
comment: $('.postMessage', post),
quotelinks: [],
backlinks: info.getElementsByClassName('backlink')
@ -3800,10 +3818,10 @@
this.nodes.uniqueID = $('.posteruid', info);
}
if (nodes.capcode) {
this.nodes.capcode = $('.capcode', info);
this.nodes.capcode = $('.capcode.hand', info);
}
if (nodes.flag) {
this.nodes.flag = $('.countryFlag', info);
this.nodes.flag = $('.flag, .countryFlag', info);
}
if (nodes.date) {
this.nodes.date = $('.dateTime', info);
@ -3818,18 +3836,11 @@
}
file = $('.file', post);
this.file.text = file.firstElementChild;
this.file.thumb = $('img[data-md5], video[data-md5]', file);
this.file.thumb = $('.fileThumb > [data-md5]', file);
this.file.fullImage = $('.full-image', file);
this.file.videoControls = $('.video-controls', this.file.text);
if (contractThumb) {
$.rmClass(root, 'expanded-image');
$.rmClass(this.file.thumb, 'expanding');
}
this.file.isExpanded = $.hasClass(root, 'expanded-image');
if ((_ref4 = this.file.fullImage) != null) {
_ref4.removeAttribute('id');
}
if ((_ref5 = $('.video-controls', this.file.text)) != null) {
_ref5.remove();
ImageExpand.contract(this);
}
}
if (origin.isDead) {
@ -3839,6 +3850,23 @@
root.dataset.clone = origin.clones.push(this) - 1;
}
Clone.prototype.cloneWithoutVideo = function(node) {
var child, clone, _i, _len, _ref;
if (node.tagName === 'VIDEO' && !node.dataset.md5) {
return [];
} else if (node.nodeType === Node.ELEMENT_NODE && $('video', node)) {
clone = node.cloneNode(false);
_ref = node.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
$.add(clone, this.cloneWithoutVideo(child));
}
return clone;
} else {
return node.cloneNode(true);
}
};
return Clone;
})(Post);
@ -4012,12 +4040,13 @@
})();
Notice = (function() {
function Notice(type, content, timeout) {
function Notice(type, content, timeout, onclose) {
this.timeout = timeout;
this.onclose = onclose;
this.close = __bind(this.close, this);
this.add = __bind(this.add, this);
this.el = $.el('div', {
innerHTML: '<a href=javascript:; class="close fa" title=Close>\uf00d</a><div class=message></div>'
innerHTML: "<a href=\"javascript:;\" class=\"close fa fa-times\" title=\"Close\"></a><div class=\"message\"></div>"
});
this.el.style.opacity = 0;
this.setType(type);
@ -4049,7 +4078,8 @@
Notice.prototype.close = function() {
$.off(d, 'visibilitychange', this.add);
return $.rm(this.el);
$.rm(this.el);
return typeof this.onclose === "function" ? this.onclose() : void 0;
};
return Notice;
@ -4089,7 +4119,7 @@
RandomAccessList.prototype.before = function(root, item) {
var prev;
if (item.next === root) {
if (item.next === root || item === root) {
return;
}
this.rmi(item);
@ -4099,6 +4129,8 @@
item.prev = prev;
if (prev) {
return prev.next = item;
} else {
return this.first = item;
}
};
@ -4114,6 +4146,8 @@
item.next = next;
if (next) {
return next.prev = item;
} else {
return this.last = item;
}
};
@ -4125,7 +4159,11 @@
}
this.rmi(item);
item.next = first;
first.prev = item;
if (first) {
first.prev = item;
} else {
this.last = item;
}
this.first = item;
return delete item.prev;
};
@ -4279,7 +4317,28 @@
})();
Polyfill = {
init: function() {},
init: function() {
this.notificationPermission();
this.toBlob();
return this.visibility();
},
notificationPermission: function() {
if (!window.Notification || 'permission' in Notification || !window.webkitNotifications) {
return;
}
return Object.defineProperty(Notification, 'permission', {
get: function() {
switch (webkitNotifications.checkPermission()) {
case 0:
return 'granted';
case 1:
return 'default';
case 2:
return 'denied';
}
}
});
},
toBlob: function() {
var _base;
return (_base = HTMLCanvasElement.prototype).toBlob || (_base.toBlob = function(cb) {
@ -4294,6 +4353,26 @@
type: 'image/png'
}));
});
},
visibility: function() {
if ('visibilityState' in d) {
return;
}
Object.defineProperties(HTMLDocument.prototype, {
visibilityState: {
get: function() {
return this.webkitVisibilityState;
}
},
hidden: {
get: function() {
return this.webkitHidden;
}
}
});
return $.on(d, 'webkitvisibilitychange', function() {
return $.event('visibilitychange');
});
}
};
@ -13442,7 +13521,7 @@
MarkNewIPs.ipCount = this.ipCount;
MarkNewIPs.postIDs = (function() {
var _i, _len, _ref, _results;
_ref = this.post.keys;
_ref = this.posts.keys;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
x = _ref[_i];

View File

@ -3167,26 +3167,24 @@
};
Callbacks.prototype.execute = function(nodes) {
var cb, err, errors, i, j, name, node;
i = 0;
while (name = this.keys[i++]) {
j = 0;
cb = this[name];
while (node = nodes[j++]) {
try {
if (!cb.disconnected) {
cb.call(node);
}
} catch (_error) {
err = _error;
if (!errors) {
errors = [];
}
errors.push({
message: ['"', name, '" crashed on node ', this.type, ' No.', node.ID, ' (', node.board, ').'].join(''),
error: err
});
var err, errors, name, node, _i, _j, _len, _len1, _ref;
_ref = this.keys;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
name = _ref[_i];
try {
for (_j = 0, _len1 = nodes.length; _j < _len1; _j++) {
node = nodes[_j];
this[name].call(node);
}
} catch (_error) {
err = _error;
if (!errors) {
errors = [];
}
errors.push({
message: ['"', name, '" crashed on node ', this.type, ' No.', node.ID, ' (', node.board, ').'].join(''),
error: err
});
}
}
if (errors) {
@ -3242,13 +3240,16 @@
}
Thread.prototype.setPage = function(pageNum) {
var icon, key, _i, _len, _ref;
icon = $('.page-num', this.OP.nodes.info);
_ref = ['title', 'textContent'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
key = _ref[_i];
icon[key] = icon[key].replace(/\d+/, pageNum);
var icon, info, quote, _ref;
_ref = this.OP.nodes, info = _ref.info, quote = _ref.quote;
if (!(icon = $('.page-num', info))) {
icon = $.el('span', {
className: 'page-num'
});
$.after(quote, [$.tn(' '), icon]);
}
icon.title = "This thread is on page " + pageNum + " in the original index.";
icon.textContent = "[" + pageNum + "]";
if (this.catalogView) {
return this.catalogView.nodes.pageCount.textContent = pageNum;
}
@ -3265,7 +3266,7 @@
};
Thread.prototype.setStatus = function(type, status) {
var name, typeLC;
var name;
name = "is" + type;
if (this[name] === status) {
return;
@ -3274,7 +3275,6 @@
if (!this.OP) {
return;
}
typeLC = type.toLowerCase();
this.setIcon('Sticky', this.isSticky);
this.setIcon('Closed', this.isClosed && !this.isArchived);
return this.setIcon('Archived', this.isArchived);
@ -3301,7 +3301,7 @@
title: type,
className: "" + typeLC + "Icon retina"
});
root = type !== 'Sticky' && this.isSticky ? $('.stickyIcon', this.OP.nodes.info) : $('.page-num', this.OP.nodes.info) || $('[title="Reply to this post"]', this.OP.nodes.info);
root = type !== 'Sticky' && this.isSticky ? $('.stickyIcon', this.OP.nodes.info) : $('.page-num', this.OP.nodes.info) || this.OP.nodes.quote;
$.after(root, [$.tn(' '), icon]);
if (!this.catalogView) {
return;
@ -3375,11 +3375,12 @@
this.board = this.thread.board;
this.nodes = {
root: root,
thumb: $('.thumb', root),
icons: $('.thread-icons', root),
thumb: $('.catalog-thumb', root),
icons: $('.catalog-icons', root),
postCount: $('.post-count', root),
fileCount: $('.file-count', root),
pageCount: $('.page-count', root)
pageCount: $('.page-count', root),
comment: $('.comment', root)
};
this.thread.catalogView = this;
}
@ -3404,17 +3405,18 @@
}
this.ID = +root.id.slice(2);
this.fullID = "" + this.board + "." + this.ID;
post = $('.post', root);
info = $('.postInfo', post);
if (that.isOriginalMarkup) {
this.cleanup(root, post);
}
post = $('.post', root);
info = $('.postInfo', post);
root.dataset.fullID = this.fullID;
this.nodes = {
root: root,
post: post,
info: info,
nameBlock: $('.nameBlock', info),
quote: $('.postNum > a:nth-of-type(2)', info),
comment: $('.postMessage', post),
links: [],
quotelinks: [],
@ -3422,8 +3424,12 @@
};
if (!(this.isReply = $.hasClass(post, 'reply'))) {
this.thread.OP = this;
this.thread.isArchived = !!$('.archivedIcon', info);
this.thread.isSticky = !!$('.stickyIcon', info);
this.thread.isClosed = !!$('.closedIcon', info);
this.thread.isClosed = this.thread.isArchived || !!$('.closedIcon', info);
if (this.thread.isArchived) {
this.thread.kill();
}
}
this.info = {};
this.info.nameBlock = Conf['Anonymize'] ? 'Anonymous' : this.nodes.nameBlock.textContent.trim();
@ -3474,27 +3480,45 @@
}
Post.prototype.parseComment = function() {
var bq, i, node, nodes, text;
var bq, i, node, nodes, spoilers;
this.nodes.comment.normalize();
bq = this.nodes.comment.cloneNode(true);
nodes = $$('.abbr, .exif, b', bq);
nodes = $$('.abbr, .exif, b, marquee', bq);
i = 0;
while (node = nodes[i++]) {
$.rm(node);
}
this.info.comment = this.nodesToText(bq);
spoilers = $$('s', bq);
return this.info.commentSpoilered = (function() {
var _i, _len;
if (spoilers.length) {
for (_i = 0, _len = spoilers.length; _i < _len; _i++) {
node = spoilers[_i];
$.replace(node, $.tn('[spoiler]'));
}
return this.nodesToText(bq);
} else {
return this.info.comment;
}
}).call(this);
};
Post.prototype.nodesToText = function(bq) {
var i, node, nodes, text;
text = "";
nodes = $.X('.//br|.//text()', bq);
i = 0;
while (node = nodes.snapshotItem(i++)) {
text += node.data || '\n';
}
return this.info.comment = text.trim().replace(/\s+$/gm, '');
return text.trim().replace(/\s+$/gm, '');
};
Post.prototype.parseQuotes = function() {
var quotelink, _i, _len, _ref;
this.quotes = [];
_ref = $$('.quotelink', this.nodes.comment);
_ref = $$(':not(pre) > .quotelink', this.nodes.comment);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quotelink = _ref[_i];
this.parseQuote(quotelink);
@ -3503,7 +3527,7 @@
Post.prototype.parseQuote = function(quotelink) {
var fullID, match;
if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/thread\/\d+.*\#p(\d+)$/))) {
if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/(?:res|thread)\/\d+(?:\/[^#]*)?#p(\d+)$/))) {
return;
}
this.nodes.quotelinks.push(quotelink);
@ -3517,7 +3541,7 @@
};
Post.prototype.parseFile = function(that) {
var anchor, fileEl, fileText, nameNode, size, thumb, unit, _ref;
var anchor, fileEl, fileText, nameNode, size, thumb, unit;
if (!((fileEl = $('.file', this.nodes.post)) && (thumb = $('img[data-md5]', fileEl)))) {
return;
}
@ -3537,31 +3561,26 @@
size *= 1024;
}
this.file.sizeInBytes = size;
this.file.thumbURL = that.isArchived ? thumb.src : "" + location.protocol + "//t.4cdn.org/" + this.board + "/" + (this.file.URL.match(/(\d+)\./)[1]) + "s.jpg";
this.file.thumbURL = "" + location.protocol + "//t.4cdn.org/" + this.board + "/" + (this.file.URL.match(/(\d+)\./)[1]) + "s.jpg";
this.file.isImage = /(jpg|png|gif)$/i.test(this.file.URL);
this.file.isVideo = /webm$/i.test(this.file.URL);
nameNode = $('a', fileText);
if (this.file.isImage || this.file.isVideo) {
this.file.dimensions = fileText.childNodes[2].data.match(/\d+x\d+/)[0];
this.file.dimensions = nameNode.nextSibling.textContent.match(/\d+x\d+/)[0];
}
this.file.name = !this.file.isSpoiler && (nameNode = $('a', fileText)) ? nameNode.title || nameNode.textContent : fileText.title;
return this.file.name = (_ref = this.file.name) != null ? _ref.replace(/%22/g, '"') : void 0;
return this.file.name = fileText.title || nameNode.title || nameNode.textContent;
};
Post.prototype.cleanup = function(root, post) {
var node, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;
Post.prototype.cleanup = function(root) {
var node, _i, _j, _len, _len1, _ref, _ref1;
_ref = $$('.mobile', root);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i];
$.rm(node);
}
_ref1 = $$('[id]:not(.exif)', post);
_ref1 = $$('.desktop', root);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
node = _ref1[_j];
node.removeAttribute('id');
}
_ref2 = $$('.desktop', root);
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
node = _ref2[_k];
$.rmClass(node, 'desktop');
}
};
@ -3633,9 +3652,6 @@
return;
}
this.isHidden = false;
this.labels = this.labels.filter(function(label) {
return !/^(Manually hidden|Recursively hidden|Hidden by)/.test(label);
});
_ref = Get.allQuotelinksLinkingTo(this);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quotelink = _ref[_i];
@ -3664,8 +3680,7 @@
return delete this.nodes.stub;
};
Post.prototype.highlight = function(label, highlight, top) {
this.labels.push(label);
Post.prototype.highlight = function(highlight, top) {
if (__indexOf.call(this.highlights, highlight) < 0) {
this.highlights.push(highlight);
$.addClass(this.nodes.root, highlight);
@ -3778,7 +3793,7 @@
__extends(Clone, _super);
function Clone(origin, context, contractThumb) {
var file, info, inline, inlined, key, nodes, post, root, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
var file, info, inline, inlined, key, nodes, post, root, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3;
this.origin = origin;
this.context = context;
_ref = ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply'];
@ -3787,13 +3802,15 @@
this[key] = origin[key];
}
nodes = origin.nodes;
root = nodes.root.cloneNode(true);
root = contractThumb ? this.cloneWithoutVideo(nodes.root) : nodes.root.cloneNode(true);
post = $('.post', root);
info = $('.postInfo', post);
this.nodes = {
root: root,
post: post,
info: info,
nameBlock: $('.nameBlock', info),
quote: $('.postNum > a:nth-of-type(2)', info),
comment: $('.postMessage', post),
quotelinks: [],
backlinks: info.getElementsByClassName('backlink')
@ -3827,10 +3844,10 @@
this.nodes.uniqueID = $('.posteruid', info);
}
if (nodes.capcode) {
this.nodes.capcode = $('.capcode', info);
this.nodes.capcode = $('.capcode.hand', info);
}
if (nodes.flag) {
this.nodes.flag = $('.countryFlag', info);
this.nodes.flag = $('.flag, .countryFlag', info);
}
if (nodes.date) {
this.nodes.date = $('.dateTime', info);
@ -3845,18 +3862,11 @@
}
file = $('.file', post);
this.file.text = file.firstElementChild;
this.file.thumb = $('img[data-md5], video[data-md5]', file);
this.file.thumb = $('.fileThumb > [data-md5]', file);
this.file.fullImage = $('.full-image', file);
this.file.videoControls = $('.video-controls', this.file.text);
if (contractThumb) {
$.rmClass(root, 'expanded-image');
$.rmClass(this.file.thumb, 'expanding');
}
this.file.isExpanded = $.hasClass(root, 'expanded-image');
if ((_ref4 = this.file.fullImage) != null) {
_ref4.removeAttribute('id');
}
if ((_ref5 = $('.video-controls', this.file.text)) != null) {
_ref5.remove();
ImageExpand.contract(this);
}
}
if (origin.isDead) {
@ -3866,6 +3876,23 @@
root.dataset.clone = origin.clones.push(this) - 1;
}
Clone.prototype.cloneWithoutVideo = function(node) {
var child, clone, _i, _len, _ref;
if (node.tagName === 'VIDEO' && !node.dataset.md5) {
return [];
} else if (node.nodeType === Node.ELEMENT_NODE && $('video', node)) {
clone = node.cloneNode(false);
_ref = node.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
$.add(clone, this.cloneWithoutVideo(child));
}
return clone;
} else {
return node.cloneNode(true);
}
};
return Clone;
})(Post);
@ -4039,12 +4066,13 @@
})();
Notice = (function() {
function Notice(type, content, timeout) {
function Notice(type, content, timeout, onclose) {
this.timeout = timeout;
this.onclose = onclose;
this.close = __bind(this.close, this);
this.add = __bind(this.add, this);
this.el = $.el('div', {
innerHTML: '<a href=javascript:; class="close fa" title=Close>\uf00d</a><div class=message></div>'
innerHTML: "<a href=\"javascript:;\" class=\"close fa fa-times\" title=\"Close\"></a><div class=\"message\"></div>"
});
this.el.style.opacity = 0;
this.setType(type);
@ -4076,7 +4104,8 @@
Notice.prototype.close = function() {
$.off(d, 'visibilitychange', this.add);
return $.rm(this.el);
$.rm(this.el);
return typeof this.onclose === "function" ? this.onclose() : void 0;
};
return Notice;
@ -4116,7 +4145,7 @@
RandomAccessList.prototype.before = function(root, item) {
var prev;
if (item.next === root) {
if (item.next === root || item === root) {
return;
}
this.rmi(item);
@ -4126,6 +4155,8 @@
item.prev = prev;
if (prev) {
return prev.next = item;
} else {
return this.first = item;
}
};
@ -4141,6 +4172,8 @@
item.next = next;
if (next) {
return next.prev = item;
} else {
return this.last = item;
}
};
@ -4152,7 +4185,11 @@
}
this.rmi(item);
item.next = first;
first.prev = item;
if (first) {
first.prev = item;
} else {
this.last = item;
}
this.first = item;
return delete item.prev;
};
@ -4307,7 +4344,26 @@
Polyfill = {
init: function() {
return this.toBlob();
this.notificationPermission();
this.toBlob();
return this.visibility();
},
notificationPermission: function() {
if (!window.Notification || 'permission' in Notification || !window.webkitNotifications) {
return;
}
return Object.defineProperty(Notification, 'permission', {
get: function() {
switch (webkitNotifications.checkPermission()) {
case 0:
return 'granted';
case 1:
return 'default';
case 2:
return 'denied';
}
}
});
},
toBlob: function() {
var _base;
@ -4323,6 +4379,26 @@
type: 'image/png'
}));
});
},
visibility: function() {
if ('visibilityState' in d) {
return;
}
Object.defineProperties(HTMLDocument.prototype, {
visibilityState: {
get: function() {
return this.webkitVisibilityState;
}
},
hidden: {
get: function() {
return this.webkitHidden;
}
}
});
return $.on(d, 'webkitvisibilitychange', function() {
return $.event('visibilitychange');
});
}
};
@ -13469,7 +13545,7 @@
MarkNewIPs.ipCount = this.ipCount;
MarkNewIPs.postIDs = (function() {
var _i, _len, _ref, _results;
_ref = this.post.keys;
_ref = this.posts.keys;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
x = _ref[_i];

View File

@ -11,21 +11,13 @@ class Callbacks
disconnect: (name) -> @[name].disconnected = true if @[name]
execute: (nodes) ->
i = 0
# c.time 'Features'
while name = @keys[i++]
j = 0
cb = @[name]
# c.time name
while node = nodes[j++]
try
cb.call node unless cb.disconnected
catch err
errors = [] unless errors
errors.push
message: ['"', name, '" crashed on node ', @type, ' No.', node.ID, ' (', node.board, ').'].join('')
error: err
# c.timeEnd name
# c.timeEnd 'Features'
for name in @keys
try
@[name].call node for node in nodes
catch err
errors = [] unless errors
errors.push
message: ['"', name, '" crashed on node ', @type, ' No.', node.ID, ' (', node.board, ').'].join('')
error: err
Main.handleErrors errors if errors

View File

@ -7,9 +7,10 @@ class CatalogThread
@board = @thread.board
@nodes =
root: root
thumb: $ '.thumb', root
icons: $ '.thread-icons', root
postCount: $ '.post-count', root
fileCount: $ '.file-count', root
pageCount: $ '.page-count', root
thumb: $ '.catalog-thumb', root
icons: $ '.catalog-icons', root
postCount: $ '.post-count', root
fileCount: $ '.file-count', root
pageCount: $ '.page-count', root
comment: $ '.comment', root
@thread.catalogView = @

View File

@ -9,4 +9,4 @@
<%= grunt.file.read('src/General/lib/randomaccesslist.class') %>
<%= grunt.file.read('src/General/lib/simpledict.class') %>
<%= grunt.file.read('src/General/lib/set.class') %>
<%= grunt.file.read('src/General/lib/connection.class') %>
<%= grunt.file.read('src/General/lib/connection.class') %>

View File

@ -5,13 +5,18 @@ class Clone extends Post
@[key] = origin[key]
{nodes} = origin
root = nodes.root.cloneNode true
root = if contractThumb
@cloneWithoutVideo nodes.root
else
nodes.root.cloneNode true
post = $ '.post', root
info = $ '.postInfo', post
@nodes =
root: root
post: post
info: info
nameBlock: $ '.nameBlock', info
quote: $ '.postNum > a:nth-of-type(2)', info
comment: $ '.postMessage', post
quotelinks: []
backlinks: info.getElementsByClassName 'backlink'
@ -37,9 +42,9 @@ class Clone extends Post
if nodes.uniqueID
@nodes.uniqueID = $ '.posteruid', info
if nodes.capcode
@nodes.capcode = $ '.capcode', info
@nodes.capcode = $ '.capcode.hand', info
if nodes.flag
@nodes.flag = $ '.countryFlag', info
@nodes.flag = $ '.flag, .countryFlag', info
if nodes.date
@nodes.date = $ '.dateTime', info
@ -53,21 +58,23 @@ class Clone extends Post
@file[key] = val
file = $ '.file', post
@file.text = file.firstElementChild
@file.thumb = $ 'img[data-md5], video[data-md5]', file
@file.thumb = $ '.fileThumb > [data-md5]', file
@file.fullImage = $ '.full-image', file
@file.videoControls = $ '.video-controls', @file.text
# Contract thumbnails in quote preview
if contractThumb
$.rmClass root, 'expanded-image'
$.rmClass @file.thumb, 'expanding'
@file.isExpanded = $.hasClass root, 'expanded-image'
# Remove any #ihover ID
@file.fullImage?.removeAttribute 'id'
# Remove video controls.
($ '.video-controls', @file.text)?.remove()
ImageExpand.contract @ if contractThumb
@isDead = true if origin.isDead
@isClone = true
root.dataset.clone = origin.clones.push(@) - 1
root.dataset.clone = origin.clones.push(@) - 1
cloneWithoutVideo: (node) ->
if node.tagName is 'VIDEO' and !node.dataset.md5 # (exception for WebM thumbnails)
[]
else if node.nodeType is Node.ELEMENT_NODE and $ 'video', node
clone = node.cloneNode false
$.add clone, @cloneWithoutVideo child for child in node.childNodes
clone
else
node.cloneNode true

View File

@ -1,7 +1,7 @@
class Notice
constructor: (type, content, @timeout) ->
constructor: (type, content, @timeout, @onclose) ->
@el = $.el 'div',
innerHTML: '<a href=javascript:; class="close fa" title=Close>\uf00d</a><div class=message></div>'
<%= html('<a href="javascript:;" class="close fa fa-times" title="Close"></a><div class="message"></div>') %>
@el.style.opacity = 0
@setType type
$.on @el.firstElementChild, 'click', @close
@ -27,3 +27,4 @@ class Notice
close: =>
$.off d, 'visibilitychange', @add
$.rm @el
@onclose?()

View File

@ -1,8 +1,19 @@
Polyfill =
init: ->
<% if (type === 'crx') { %>
@notificationPermission()
@toBlob()
<% } %>
@visibility()
notificationPermission: ->
return if !window.Notification or 'permission' of Notification or !window.webkitNotifications
Object.defineProperty Notification, 'permission',
get: ->
switch webkitNotifications.checkPermission()
when 0
'granted'
when 1
'default'
when 2
'denied'
toBlob: ->
HTMLCanvasElement::toBlob or= (cb) ->
data = atob @toDataURL()[22..]
@ -12,3 +23,12 @@ Polyfill =
for i in [0...l] by 1
ui8a[i] = data.charCodeAt i
cb new Blob [ui8a], type: 'image/png'
visibility: ->
# page visibility API
return if 'visibilityState' of d
Object.defineProperties HTMLDocument.prototype,
visibilityState:
get: -> @webkitVisibilityState
hidden:
get: -> @webkitHidden
$.on d, 'webkitvisibilitychange', -> $.event 'visibilitychange'

View File

@ -6,15 +6,16 @@ class Post
@ID = +root.id[2..]
@fullID = "#{@board}.#{@ID}"
@cleanup root, post if that.isOriginalMarkup
post = $ '.post', root
info = $ '.postInfo', post
@cleanup root, post if that.isOriginalMarkup
root.dataset.fullID = @fullID
@nodes =
root: root
post: post
info: info
nameBlock: $ '.nameBlock', info
quote: $ '.postNum > a:nth-of-type(2)', info
comment: $ '.postMessage', post
links: []
quotelinks: []
@ -22,8 +23,10 @@ class Post
unless @isReply = $.hasClass post, 'reply'
@thread.OP = @
@thread.isSticky = !!$ '.stickyIcon', info
@thread.isClosed = !!$ '.closedIcon', info
@thread.isArchived = !!$ '.archivedIcon', info
@thread.isSticky = !!$ '.stickyIcon', info
@thread.isClosed = @thread.isArchived or !!$ '.closedIcon', info
@thread.kill() if @thread.isArchived
@info = {}
@info.nameBlock = if Conf['Anonymize']
@ -71,28 +74,44 @@ class Post
parseComment: ->
# Merge text nodes and remove empty ones.
@nodes.comment.normalize()
# Get the comment's text.
# <br> -> \n
# Remove:
# 'Comment too long'...
# EXIF data. (/p/)
# Rolls. (/tg/)
# Marquees. (/pol/)
# Preceding and following new lines.
# Trailing spaces.
bq = @nodes.comment.cloneNode true
nodes = $$ '.abbr, .exif, b', bq
nodes = $$ '.abbr, .exif, b, marquee', bq
i = 0
while node = nodes[i++]
$.rm node
$.rm node while node = nodes[i++]
@info.comment = @nodesToText bq
# Get the comment's text with spoilers hidden.
spoilers = $$ 's', bq
@info.commentSpoilered = if spoilers.length
for node in spoilers
$.replace node, $.tn '[spoiler]'
@nodesToText bq
else
@info.comment
nodesToText: (bq) ->
text = ""
nodes = $.X './/br|.//text()', bq
i = 0
while node = nodes.snapshotItem i++
text += node.data or '\n'
@info.comment = text.trim().replace /\s+$/gm, ''
text.trim().replace /\s+$/gm, ''
parseQuotes: ->
@quotes = []
for quotelink in $$ '.quotelink', @nodes.comment
# XXX https://github.com/4chan/4chan-JS/issues/77
# 4chan currently creates quote links inside [code] tags; ignore them
for quotelink in $$ ':not(pre) > .quotelink', @nodes.comment
@parseQuote quotelink
return
@ -106,9 +125,8 @@ class Post
return unless match = quotelink.href.match ///
boards\.4chan\.org/
([^/]+) # boardID
/thread/\d+
.* # thread slug
\#p(\d+) # postID
/(?:res|thread)/\d+(?:\/[^#]*)?#p
(\d+) # postID
$
///
@ -122,7 +140,7 @@ class Post
parseFile: (that) ->
return unless (fileEl = $ '.file', @nodes.post) and thumb = $ 'img[data-md5]', fileEl
# Supports JPG/PNG/GIF/WEBM/PDF.
# Supports JPG/PNG/GIF/PDF.
# Flash files are not supported.
anchor = thumb.parentNode
fileText = fileEl.firstElementChild
@ -137,32 +155,17 @@ class Post
unit = ['B', 'KB', 'MB', 'GB'].indexOf @file.size.match(/\w+$/)[0]
size *= 1024 while unit-- > 0
@file.sizeInBytes = size
@file.thumbURL = if that.isArchived
thumb.src
else
"#{location.protocol}//t.4cdn.org/#{@board}/#{@file.URL.match(/(\d+)\./)[1]}s.jpg"
@file.thumbURL = "#{location.protocol}//t.4cdn.org/#{@board}/#{@file.URL.match(/(\d+)\./)[1]}s.jpg"
@file.isImage = /(jpg|png|gif)$/i.test @file.URL
@file.isVideo = /webm$/i.test @file.URL
nameNode = $ 'a', fileText
if @file.isImage or @file.isVideo
@file.dimensions = fileText.childNodes[2].data.match(/\d+x\d+/)[0]
@file.name = if !@file.isSpoiler and nameNode = $ 'a', fileText
nameNode.title or nameNode.textContent
else
fileText.title
<% if (type === 'crx') { %>
# replace %22 with quotes, see:
# crbug.com/81193
# webk.it/62107
# https://www.w3.org/Bugs/Public/show_bug.cgi?id=16909
# http://www.whatwg.org/specs/web-apps/current-work/#multipart-form-data
@file.name = @file.name?.replace /%22/g, '"'
<% } %>
@file.dimensions = nameNode.nextSibling.textContent.match(/\d+x\d+/)[0]
@file.name = fileText.title or nameNode.title or nameNode.textContent
cleanup: (root, post) ->
cleanup: (root) ->
for node in $$ '.mobile', root
$.rm node
for node in $$ '[id]:not(.exif)', post
node.removeAttribute 'id'
for node in $$ '.desktop', root
$.rmClass node, 'desktop'
return
@ -209,9 +212,6 @@ class Post
show: (showRecursively=Conf['Recursive Hiding']) ->
return if !@isHidden
@isHidden = false
@labels = @labels.filter (label) ->
# This is lame.
!/^(Manually hidden|Recursively hidden|Hidden by)/.test label
for quotelink in Get.allQuotelinksLinkingTo @
$.rmClass quotelink, 'filtered'
@ -233,8 +233,7 @@ class Post
@nodes.post.previousElementSibling.hidden = false
$.rm @nodes.stub
delete @nodes.stub
highlight: (label, highlight, top) ->
@labels.push label
highlight: (highlight, top) ->
unless highlight in @highlights
@highlights.push highlight
$.addClass @nodes.root, highlight

View File

@ -21,7 +21,7 @@ class RandomAccessList
@length++
before: (root, item) ->
return if item.next is root
return if item.next is root or item is root
@rmi item
@ -29,7 +29,10 @@ class RandomAccessList
root.prev = item
item.next = root
item.prev = prev
prev.next = item if prev
if prev
prev.next = item
else
@first = item
after: (root, item) ->
return if item.prev is root
@ -40,14 +43,20 @@ class RandomAccessList
root.next = item
item.prev = root
item.next = next
next.prev = item if next
if next
next.prev = item
else
@last = item
prepend: (item) ->
{first} = @
return if item is first or not @[item.ID]
@rmi item
item.next = first
first.prev = item
if first
first.prev = item
else
@last = item
@first = item
delete item.prev
@ -77,4 +86,4 @@ class RandomAccessList
if next
next.prev = prev
else
@last = prev
@last = prev

View File

@ -22,24 +22,28 @@ class Thread
g.threads.push @fullID, board.threads.push @, @
setPage: (pageNum) ->
icon = $ '.page-num', @OP.nodes.info
for key in ['title', 'textContent']
icon[key] = icon[key].replace /\d+/, pageNum
{info, quote} = @OP.nodes
unless icon = $ '.page-num', info
icon = $.el 'span', className: 'page-num'
$.after quote, [$.tn(' '), icon]
icon.title = "This thread is on page #{pageNum} in the original index."
icon.textContent = "[#{pageNum}]"
@catalogView.nodes.pageCount.textContent = pageNum if @catalogView
setCount: (type, count, reachedLimit) ->
return unless @catalogView
el = @catalogView.nodes["#{type}Count"]
el.textContent = count
(if reachedLimit then $.addClass else $.rmClass) el, 'warning'
setStatus: (type, status) ->
name = "is#{type}"
return if @[name] is status
@[name] = status
return unless @OP
typeLC = type.toLowerCase()
@setIcon 'Sticky', @isSticky
@setIcon 'Closed', @isClosed and !@isArchived
@setIcon 'Sticky', @isSticky
@setIcon 'Closed', @isClosed and !@isArchived
@setIcon 'Archived', @isArchived
setIcon: (type, status) ->
@ -58,11 +62,10 @@ class Thread
title: type
className: "#{typeLC}Icon retina"
root =
if type isnt 'Sticky' and @isSticky
$ '.stickyIcon', @OP.nodes.info
else
$('.page-num', @OP.nodes.info) or $('[title="Reply to this post"]', @OP.nodes.info)
root = if type isnt 'Sticky' and @isSticky
$ '.stickyIcon', @OP.nodes.info
else
$('.page-num', @OP.nodes.info) or @OP.nodes.quote
$.after root, [$.tn(' '), icon]
return unless @catalogView
@ -71,6 +74,7 @@ class Thread
pin: ->
@isPinned = true
$.addClass @catalogView.nodes.root, 'pinned' if @catalogView
unpin: ->
@isPinned = false
$.rmClass @catalogView.nodes.root, 'pinned' if @catalogView
@ -81,6 +85,7 @@ class Thread
@OP.nodes.root.parentElement.hidden = true
if button = $ '.hide-post-button', @OP.nodes.root
$.replace button, PostHiding.makeButton false
show: ->
return if !@isHidden
@isHidden = false

View File

@ -7,7 +7,7 @@ MarkNewIPs =
node: ->
MarkNewIPs.ipCount = @ipCount
MarkNewIPs.postIDs = (+x for x in @post.keys)
MarkNewIPs.postIDs = (+x for x in @posts.keys)
$.on d, 'ThreadUpdate', MarkNewIPs.onUpdate
onUpdate: (e) ->