Finish Miscellaneous features (for now), almost finish Monitoring
This commit is contained in:
parent
108129ca78
commit
3d3fc0a868
@ -115,7 +115,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, Build, Callbacks, CatalogLinks, CatalogThread, Clone, Color, Conf, Config, CrossOrigin, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, E, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Flash, Fourchan, Gallery, Get, GlobalMessage, Header, IDColor, IDHighlight, ImageCommon, ImageExpand, ImageHover, ImageLoader, Index, JSColor, Keybinds, Linkify, Main, MarkNewIPs, MascotTools, Mascots, Menu, Nav, Navigate, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteInline, QuoteMarkers, QuotePreview, QuoteStrikeThrough, QuoteThreading, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Rice, Sauce, Settings, SimpleDict, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, editMascot, editTheme, g, userNavigation,
|
var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, Build, Callbacks, CatalogLinks, CatalogThread, Clone, Color, Conf, Config, CrossOrigin, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, E, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Flash, Fourchan, Gallery, Get, GlobalMessage, Header, IDColor, IDHighlight, ImageCommon, ImageExpand, ImageHover, ImageLoader, Index, JSColor, Keybinds, Linkify, Main, MarkNewIPs, MascotTools, Mascots, Menu, Nav, Navigate, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteInline, QuoteMarkers, QuotePreview, QuoteStrikeThrough, QuoteThreading, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReportLink, RevealSpoilers, Rice, Sauce, Settings, SimpleDict, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, editMascot, editTheme, g, userNavigation,
|
||||||
__slice = [].slice,
|
__slice = [].slice,
|
||||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||||
__hasProp = {}.hasOwnProperty,
|
__hasProp = {}.hasOwnProperty,
|
||||||
@ -3207,6 +3207,7 @@
|
|||||||
this.isPinned = false;
|
this.isPinned = false;
|
||||||
this.isSticky = false;
|
this.isSticky = false;
|
||||||
this.isClosed = false;
|
this.isClosed = false;
|
||||||
|
this.isArchived = false;
|
||||||
this.postLimit = false;
|
this.postLimit = false;
|
||||||
this.fileLimit = false;
|
this.fileLimit = false;
|
||||||
this.ipCount = void 0;
|
this.ipCount = void 0;
|
||||||
@ -12747,6 +12748,19 @@
|
|||||||
|
|
||||||
Favicon = {
|
Favicon = {
|
||||||
init: function() {
|
init: function() {
|
||||||
|
return $.asap((function() {
|
||||||
|
return d.head && (Favicon.el = $('link[rel="shortcut icon"]', d.head));
|
||||||
|
}), Favicon.initAsap);
|
||||||
|
},
|
||||||
|
initAsap: function() {
|
||||||
|
var href;
|
||||||
|
Favicon.el.type = 'image/x-icon';
|
||||||
|
href = Favicon.el.href;
|
||||||
|
Favicon.SFW = /ws\.ico$/.test(href);
|
||||||
|
Favicon["default"] = href;
|
||||||
|
return Favicon["switch"]();
|
||||||
|
},
|
||||||
|
"switch": function() {
|
||||||
var f, funreadDeadY, i, items, t;
|
var f, funreadDeadY, i, items, t;
|
||||||
items = {
|
items = {
|
||||||
ferongr: ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///9zBQC/AADpDAP/gID/q6voCwJJTwpOAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxUlEQVR42q1TOwrCQBB9s0FRtJI0WoqFtSLYegoP4gVSeJsUHsHSI3iFeIqRXXgwrhlXwYHHhLwPTB7B36abBCV+0pA4DUBQUNZYQptGtW3jtoKyxgoe0yrBCoyZfL/5ioQ3URZOXW9I341l3oo+NXEZiW4CEuIzvPECopED4OaZ3RNmeAm4u+a8Jr5f17VyVoL8fr8qcltzwlyyj2iqcgPOQ9ExkHAITgD75bYBe0A5S4H/P9htuWMF3QXoQpwaKeT+lnsC6JE5I6aq6fEAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///8AcH4AtswA2PJ55fKi6fIA1/FtpPADAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxElEQVQ4y2NgoBq4/vE/HJOsBiRQUIfA2AzBqQYqUfn00/9FLz+BaQxDCKqBmX7jExijKEDSDJPHrnnbGQhGV4RmOFwdVkNwhQMheYwQxhaIi7b9Z9A3gWAQm2BUoQOgRhgA8o7j1ozLC4LCyAZcx6kZI5qg4kLKqggDFFWxJySsUQVzlb4pwgAJaTRvokcVNgOqOv8zcHBCsL07DgNg8YsczzA5MxtUL+DMD8g0slxI/H8GQ/P/DJKyeKIRpglXZsIiBwBhP5O+VbI/JgAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///8oeQBJ3ABV/wHM/7Lu/+ZU/gAqUP3dAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAx0lEQVQ4y2NgoBYI+cfwH4ZJVgMS0KhEYGyG4FQDkzjzf9P/d/+fgWl0QwiqgSkI/c8IxsgKkDXD5LFq9rwDweiK0A2HqcNqCK5wICSPEcLYAtH+AMN/IXMIBrEJRie6OEgjDAC5x3FqxuUFNiEUA67j1IweTTBxBQ1puAG86jgSEraogskJWSBcwCGF5k30qMJmgMFEhv/MXBAs5oLDAFj8IsczTE7UEeECbhU8+QGZRpaTi2b4L2zF8J9TGk80wjThykzY5AAW/2O1C2mIbgAAAABJRU5ErkJggg=='],
|
ferongr: ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///9zBQC/AADpDAP/gID/q6voCwJJTwpOAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxUlEQVR42q1TOwrCQBB9s0FRtJI0WoqFtSLYegoP4gVSeJsUHsHSI3iFeIqRXXgwrhlXwYHHhLwPTB7B36abBCV+0pA4DUBQUNZYQptGtW3jtoKyxgoe0yrBCoyZfL/5ioQ3URZOXW9I341l3oo+NXEZiW4CEuIzvPECopED4OaZ3RNmeAm4u+a8Jr5f17VyVoL8fr8qcltzwlyyj2iqcgPOQ9ExkHAITgD75bYBe0A5S4H/P9htuWMF3QXoQpwaKeT+lnsC6JE5I6aq6fEAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///8AcH4AtswA2PJ55fKi6fIA1/FtpPADAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxElEQVQ4y2NgoBq4/vE/HJOsBiRQUIfA2AzBqQYqUfn00/9FLz+BaQxDCKqBmX7jExijKEDSDJPHrnnbGQhGV4RmOFwdVkNwhQMheYwQxhaIi7b9Z9A3gWAQm2BUoQOgRhgA8o7j1ozLC4LCyAZcx6kZI5qg4kLKqggDFFWxJySsUQVzlb4pwgAJaTRvokcVNgOqOv8zcHBCsL07DgNg8YsczzA5MxtUL+DMD8g0slxI/H8GQ/P/DJKyeKIRpglXZsIiBwBhP5O+VbI/JgAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///8oeQBJ3ABV/wHM/7Lu/+ZU/gAqUP3dAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAx0lEQVQ4y2NgoBYI+cfwH4ZJVgMS0KhEYGyG4FQDkzjzf9P/d/+fgWl0QwiqgSkI/c8IxsgKkDXD5LFq9rwDweiK0A2HqcNqCK5wICSPEcLYAtH+AMN/IXMIBrEJRie6OEgjDAC5x3FqxuUFNiEUA67j1IweTTBxBQ1puAG86jgSEraogskJWSBcwCGF5k30qMJmgMFEhv/MXBAs5oLDAFj8IsczTE7UEeECbhU8+QGZRpaTi2b4L2zF8J9TGk80wjThykzY5AAW/2O1C2mIbgAAAABJRU5ErkJggg=='],
|
||||||
@ -12862,7 +12876,7 @@
|
|||||||
|
|
||||||
ThreadExcerpt = {
|
ThreadExcerpt = {
|
||||||
init: function() {
|
init: function() {
|
||||||
if (g.VIEW !== 'thread' || !Conf['Thread Excerpt']) {
|
if ((g.BOARD.ID !== 'f' && g.BOARD.ID !== 'pol') || g.VIEW !== 'thread' || !Conf['Thread Excerpt'] || Conf['Remove Thread Excerpt']) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return Thread.callbacks.push({
|
return Thread.callbacks.push({
|
||||||
@ -12897,7 +12911,10 @@
|
|||||||
});
|
});
|
||||||
Header.addShortcut(sc);
|
Header.addShortcut(sc);
|
||||||
} else {
|
} else {
|
||||||
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "<div class=move title='" + title + "'>" + html + "</div>");
|
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', {
|
||||||
|
innerHTML: "<div class=\"move\" title=\"" + E(statsTitle) + "\">" + statsHTML.innerHTML + "</div>"
|
||||||
|
});
|
||||||
|
$.addClass(doc, 'float');
|
||||||
$.ready(function() {
|
$.ready(function() {
|
||||||
return $.add(d.body, sc);
|
return $.add(d.body, sc);
|
||||||
});
|
});
|
||||||
@ -12918,7 +12935,10 @@
|
|||||||
this.posts.forEach(function(post) {
|
this.posts.forEach(function(post) {
|
||||||
postCount++;
|
postCount++;
|
||||||
if (post.file) {
|
if (post.file) {
|
||||||
return fileCount++;
|
fileCount++;
|
||||||
|
}
|
||||||
|
if (Conf["Page Count in Stats"]) {
|
||||||
|
return ThreadStats.lastPost = post.info.date;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ThreadStats.thread = this;
|
ThreadStats.thread = this;
|
||||||
@ -12977,6 +12997,7 @@
|
|||||||
if (!Conf["Page Count in Stats"]) {
|
if (!Conf["Page Count in Stats"]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
clearTimeout(ThreadStats.timeout);
|
||||||
if (ThreadStats.thread.isDead) {
|
if (ThreadStats.thread.isDead) {
|
||||||
ThreadStats.pageCountEl.textContent = 'Dead';
|
ThreadStats.pageCountEl.textContent = 'Dead';
|
||||||
$.addClass(ThreadStats.pageCountEl, 'warning');
|
$.addClass(ThreadStats.pageCountEl, 'warning');
|
||||||
@ -13005,6 +13026,7 @@
|
|||||||
}
|
}
|
||||||
ThreadStats.pageCountEl.textContent = page.page;
|
ThreadStats.pageCountEl.textContent = page.page;
|
||||||
(page.page === this.response.length ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning');
|
(page.page === this.response.length ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning');
|
||||||
|
ThreadStats.lastPageUpdate = new Date(thread.last_modified * $.SECOND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -13019,15 +13041,18 @@
|
|||||||
}
|
}
|
||||||
if (Conf['Updater and Stats in Header']) {
|
if (Conf['Updater and Stats in Header']) {
|
||||||
this.dialog = sc = $.el('span', {
|
this.dialog = sc = $.el('span', {
|
||||||
innerHTML: "[<span id=update-status></span><span id=update-timer title='Update now'></span>]\u00A0",
|
|
||||||
id: 'updater'
|
id: 'updater'
|
||||||
});
|
});
|
||||||
|
$.extend(sc, {
|
||||||
|
innerHTML: "[<span id=\"update-status\"></span><span id=\"update-timer\" title=\"Update now\"></span>]"
|
||||||
|
});
|
||||||
$.ready(function() {
|
$.ready(function() {
|
||||||
return Header.addShortcut(sc);
|
return Header.addShortcut(sc);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.dialog = sc = UI.dialog('updater', 'bottom: 0px; left: 0px;', "<div class=move><span id=update-status></span><span id=update-timer title='Update now'></span></div>");
|
this.dialog = sc = UI.dialog('updater', 'bottom: 0px; left: 0px;', {
|
||||||
$.addClass(doc, 'float');
|
innerHTML: "<div class=\"move\"></div><span id=\"update-status\"></span><span id=\"update-timer\" title=\"Update now\"></span>"
|
||||||
|
});
|
||||||
$.ready(function() {
|
$.ready(function() {
|
||||||
$.addClass(doc, 'float');
|
$.addClass(doc, 'float');
|
||||||
return $.add(d.body, sc);
|
return $.add(d.body, sc);
|
||||||
@ -13044,24 +13069,21 @@
|
|||||||
for (name in _ref) {
|
for (name in _ref) {
|
||||||
conf = _ref[name];
|
conf = _ref[name];
|
||||||
checked = Conf[name] ? 'checked' : '';
|
checked = Conf[name] ? 'checked' : '';
|
||||||
el = $.el('label', {
|
el = UI.checkbox(name, " " + name);
|
||||||
title: "" + conf[1],
|
|
||||||
innerHTML: "<input name='" + name + "' type=checkbox " + checked + "> " + name
|
|
||||||
});
|
|
||||||
input = el.firstElementChild;
|
input = el.firstElementChild;
|
||||||
$.on(input, 'change', $.cb.checked);
|
$.on(input, 'change', $.cb.checked);
|
||||||
if (input.name === 'Scroll BG') {
|
if (input.name === 'Scroll BG') {
|
||||||
$.on(input, 'change', this.cb.scrollBG);
|
$.on(input, 'change', this.cb.scrollBG);
|
||||||
this.cb.scrollBG();
|
this.cb.scrollBG();
|
||||||
} else if (input.name === 'Auto Update') {
|
} else if (input.name === 'Auto Update') {
|
||||||
$.on(input, 'change', this.cb.update);
|
$.on(input, 'change', this.cb.autoUpdate);
|
||||||
}
|
}
|
||||||
subEntries.push({
|
subEntries.push({
|
||||||
el: el
|
el: el
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.settings = $.el('span', {
|
this.settings = $.el('span', {
|
||||||
innerHTML: '<a href=javascript:;>Interval</a>'
|
innerHTML: "<a href=\"javascript:;\">Interval</a>"
|
||||||
});
|
});
|
||||||
$.on(this.settings, 'click', this.intervalShortcut);
|
$.on(this.settings, 'click', this.intervalShortcut);
|
||||||
subEntries.push({
|
subEntries.push({
|
||||||
@ -13122,6 +13144,7 @@
|
|||||||
ThreadUpdater.thread = this;
|
ThreadUpdater.thread = this;
|
||||||
ThreadUpdater.root = this.OP.nodes.root.parentNode;
|
ThreadUpdater.root = this.OP.nodes.root.parentNode;
|
||||||
ThreadUpdater.lastPost = +this.posts.keys[this.posts.keys.length - 1];
|
ThreadUpdater.lastPost = +this.posts.keys[this.posts.keys.length - 1];
|
||||||
|
ThreadUpdater.outdateCount = 0;
|
||||||
ThreadUpdater.cb.interval.call($.el('input', {
|
ThreadUpdater.cb.interval.call($.el('input', {
|
||||||
value: Conf['Interval'],
|
value: Conf['Interval'],
|
||||||
name: 'Interval'
|
name: 'Interval'
|
||||||
@ -13134,7 +13157,7 @@
|
|||||||
} else {
|
} else {
|
||||||
ThreadUpdater.cb.online();
|
ThreadUpdater.cb.online();
|
||||||
}
|
}
|
||||||
Rice.nodes(ThreadUpdater.dialog);
|
return Rice.nodes(ThreadUpdater.dialog);
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -13144,14 +13167,18 @@
|
|||||||
beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA',
|
beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA',
|
||||||
cb: {
|
cb: {
|
||||||
online: function() {
|
online: function() {
|
||||||
|
if (ThreadUpdater.thread.isDead) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (ThreadUpdater.online = navigator.onLine) {
|
if (ThreadUpdater.online = navigator.onLine) {
|
||||||
ThreadUpdater.outdateCount = 0;
|
ThreadUpdater.outdateCount = 0;
|
||||||
ThreadUpdater.setInterval();
|
ThreadUpdater.setInterval();
|
||||||
ThreadUpdater.set('status', null, null);
|
ThreadUpdater.set('status', '', '');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ThreadUpdater.set('timer', null);
|
ThreadUpdater.set('timer', '');
|
||||||
return ThreadUpdater.set('status', 'Offline', 'warning');
|
ThreadUpdater.set('status', 'Offline', 'warning');
|
||||||
|
return clearTimeout(ThreadUpdater.timeoutID);
|
||||||
},
|
},
|
||||||
post: function(e) {
|
post: function(e) {
|
||||||
if (!(ThreadUpdater.isUpdating && e.detail.threadID === ThreadUpdater.thread.ID)) {
|
if (!(ThreadUpdater.isUpdating && e.detail.threadID === ThreadUpdater.thread.ID)) {
|
||||||
@ -13164,14 +13191,14 @@
|
|||||||
},
|
},
|
||||||
checkpost: function(e) {
|
checkpost: function(e) {
|
||||||
if (!ThreadUpdater.checkPostCount) {
|
if (!ThreadUpdater.checkPostCount) {
|
||||||
if (e.detail.threadID !== ThreadUpdater.thread.ID) {
|
if (e && e.detail.threadID !== ThreadUpdater.thread.ID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ThreadUpdater.seconds = 0;
|
ThreadUpdater.seconds = 0;
|
||||||
ThreadUpdater.outdateCount = 0;
|
ThreadUpdater.outdateCount = 0;
|
||||||
ThreadUpdater.set('timer', '...');
|
ThreadUpdater.set('timer', '...');
|
||||||
}
|
}
|
||||||
if (!(g.DEAD || ThreadUpdater.foundPost || ThreadUpdater.checkPostCount >= 5)) {
|
if (!(ThreadUpdater.thread.isDead || ThreadUpdater.foundPost || ThreadUpdater.checkPostCount >= 5)) {
|
||||||
return setTimeout(ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND);
|
return setTimeout(ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND);
|
||||||
}
|
}
|
||||||
ThreadUpdater.setInterval();
|
ThreadUpdater.setInterval();
|
||||||
@ -13195,6 +13222,9 @@
|
|||||||
return !d.hidden;
|
return !d.hidden;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
autoUpdate: function(e) {
|
||||||
|
return ThreadUpdater.count(ThreadUpdater.isUpdating = this.checked);
|
||||||
|
},
|
||||||
interval: function(e) {
|
interval: function(e) {
|
||||||
var val;
|
var val;
|
||||||
val = parseInt(this.value, 10);
|
val = parseInt(this.value, 10);
|
||||||
@ -13211,7 +13241,6 @@
|
|||||||
req = ThreadUpdater.req;
|
req = ThreadUpdater.req;
|
||||||
switch (req.status) {
|
switch (req.status) {
|
||||||
case 200:
|
case 200:
|
||||||
g.DEAD = false;
|
|
||||||
ThreadUpdater.parse(req.response.posts);
|
ThreadUpdater.parse(req.response.posts);
|
||||||
if (ThreadUpdater.thread.isArchived) {
|
if (ThreadUpdater.thread.isArchived) {
|
||||||
ThreadUpdater.set('status', 'Archived', 'warning');
|
ThreadUpdater.set('status', 'Archived', 'warning');
|
||||||
@ -13316,9 +13345,10 @@
|
|||||||
var n;
|
var n;
|
||||||
ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000);
|
ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000);
|
||||||
if (!(n = --ThreadUpdater.seconds)) {
|
if (!(n = --ThreadUpdater.seconds)) {
|
||||||
|
ThreadUpdater.outdateCount++;
|
||||||
return ThreadUpdater.update();
|
return ThreadUpdater.update();
|
||||||
} else if (n <= -60) {
|
} else if (n <= -60) {
|
||||||
ThreadUpdater.set('status', 'Retrying', null);
|
ThreadUpdater.set('status', 'Retrying', '');
|
||||||
return ThreadUpdater.update();
|
return ThreadUpdater.update();
|
||||||
} else if (n > 0) {
|
} else if (n > 0) {
|
||||||
return ThreadUpdater.set('timer', n);
|
return ThreadUpdater.set('timer', n);
|
||||||
@ -13393,18 +13423,27 @@
|
|||||||
ThreadUpdater.thread.posts.forEach(function(post) {
|
ThreadUpdater.thread.posts.forEach(function(post) {
|
||||||
var ID;
|
var ID;
|
||||||
ID = +post.ID;
|
ID = +post.ID;
|
||||||
if (__indexOf.call(index, ID) < 0) {
|
if (!(post.info.date > Date.now() - 60 * $.SECOND)) {
|
||||||
post.kill();
|
if (__indexOf.call(index, ID) < 0) {
|
||||||
} else if (post.isDead) {
|
post.kill();
|
||||||
post.resurrect();
|
} else if (post.isDead) {
|
||||||
} else if (post.file && !post.file.isDead && __indexOf.call(files, ID) < 0) {
|
post.resurrect();
|
||||||
post.kill(true);
|
} else if (post.file && !(post.file.isDead || __indexOf.call(files, ID) >= 0)) {
|
||||||
|
post.kill(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ThreadUpdater.postID && ThreadUpdater.postID === ID) {
|
if (ThreadUpdater.postID && ThreadUpdater.postID === ID) {
|
||||||
return ThreadUpdater.foundPost = true;
|
return ThreadUpdater.foundPost = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
sendEvent = function() {
|
sendEvent = function() {
|
||||||
|
var ipCountEl;
|
||||||
|
if ((OP.unique_ips != null) && (ipCountEl = $.id('unique-ips'))) {
|
||||||
|
ipCountEl.textContent = OP.unique_ips;
|
||||||
|
ipCountEl.previousSibling.textContent = ipCountEl.previousSibling.textContent.replace(/\b(?:is|are)\b/, OP.unique_ips === 1 ? 'is' : 'are');
|
||||||
|
ipCountEl.nextSibling.textContent = ipCountEl.nextSibling.textContent.replace(/\bposters?\b/, OP.unique_ips === 1 ? 'poster' : 'posters');
|
||||||
|
}
|
||||||
|
ThreadUpdater.postIDs = index;
|
||||||
return $.event('ThreadUpdate', {
|
return $.event('ThreadUpdate', {
|
||||||
404: false,
|
404: false,
|
||||||
threadID: ThreadUpdater.thread.fullID,
|
threadID: ThreadUpdater.thread.fullID,
|
||||||
@ -13417,7 +13456,7 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
if (!count) {
|
if (!count) {
|
||||||
ThreadUpdater.set('status', null, null);
|
ThreadUpdater.set('status', '', '');
|
||||||
ThreadUpdater.outdateCount++;
|
ThreadUpdater.outdateCount++;
|
||||||
sendEvent();
|
sendEvent();
|
||||||
return;
|
return;
|
||||||
@ -13464,13 +13503,17 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.db = new DataBoard('watchedThreads', this.refresh, true);
|
this.db = new DataBoard('watchedThreads', this.refresh, true);
|
||||||
this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', "<div><span class=\"move\">Thread Watcher <span id=\"watcher-status\"></span></span><a class=\"menu-button\" href=\"javascript:;\"><i class=\"fa\">\uf107</i></a></div><div id=\"watched-threads\"></div>");
|
this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', {
|
||||||
|
innerHTML: "<div>\r<span class=\"move\">\rThread Watcher \r<a class=\"refresh fa\" title=\"Check threads\" href=\"javascript:;\">\\uf021</a>\r<span id=\"watcher-status\"></span>\r</span>\r<a class=\"menu-button\" href=\"javascript:;\"><i class=\"fa\">\\uf107</i></a>\r</div>\r<div id=\"watched-threads\"></div>"
|
||||||
|
});
|
||||||
this.status = $('#watcher-status', this.dialog);
|
this.status = $('#watcher-status', this.dialog);
|
||||||
this.list = this.dialog.lastElementChild;
|
this.list = this.dialog.lastElementChild;
|
||||||
|
this.refreshButton = $('.refresh', this.dialog);
|
||||||
$.on(d, 'QRPostSuccessful', this.cb.post);
|
$.on(d, 'QRPostSuccessful', this.cb.post);
|
||||||
if (g.VIEW === 'thread') {
|
if (g.VIEW === 'thread') {
|
||||||
$.on(d, 'ThreadUpdate', this.cb.threadUpdate);
|
$.on(d, 'ThreadUpdate', this.cb.threadUpdate);
|
||||||
}
|
}
|
||||||
|
$.on(this.refreshButton, 'click', this.fetchAllStatus);
|
||||||
$.on(d, '4chanXInitFinished', this.ready);
|
$.on(d, '4chanXInitFinished', this.ready);
|
||||||
switch (g.VIEW) {
|
switch (g.VIEW) {
|
||||||
case 'index':
|
case 'index':
|
||||||
@ -13485,18 +13528,75 @@
|
|||||||
ThreadWatcher.fetchAllStatus();
|
ThreadWatcher.fetchAllStatus();
|
||||||
this.db.save();
|
this.db.save();
|
||||||
}
|
}
|
||||||
return Thread.callbacks.push({
|
if (Conf['JSON Navigation'] && Conf['Menu'] && g.BOARD.ID !== 'f') {
|
||||||
|
Menu.menu.addEntry({
|
||||||
|
el: $.el('a', {
|
||||||
|
href: 'javascript:;'
|
||||||
|
}),
|
||||||
|
order: 6,
|
||||||
|
open: function(_arg) {
|
||||||
|
var thread;
|
||||||
|
thread = _arg.thread;
|
||||||
|
if (!(Conf['Index Mode'] === 'catalog' && g.VIEW === 'index')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.el.textContent = ThreadWatcher.isWatched(thread) ? 'Unwatch thread' : 'Watch thread';
|
||||||
|
if (this.cb) {
|
||||||
|
$.off(this.el, 'click', this.cb);
|
||||||
|
}
|
||||||
|
this.cb = function() {
|
||||||
|
$.event('CloseMenu');
|
||||||
|
return ThreadWatcher.toggle(thread);
|
||||||
|
};
|
||||||
|
$.on(this.el, 'click', this.cb);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Post.callbacks.push({
|
||||||
name: 'Thread Watcher',
|
name: 'Thread Watcher',
|
||||||
cb: this.node
|
cb: this.node
|
||||||
});
|
});
|
||||||
|
return CatalogThread.callbacks.push({
|
||||||
|
name: 'Thread Watcher',
|
||||||
|
cb: this.catalogNode
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isWatched: function(thread) {
|
||||||
|
var _ref;
|
||||||
|
return (_ref = ThreadWatcher.db) != null ? _ref.get({
|
||||||
|
boardID: thread.board.ID,
|
||||||
|
threadID: thread.ID
|
||||||
|
}) : void 0;
|
||||||
},
|
},
|
||||||
node: function() {
|
node: function() {
|
||||||
var toggler;
|
var toggler;
|
||||||
toggler = $.el('img', {
|
if (this.isReply) {
|
||||||
className: 'watch-thread-link'
|
return;
|
||||||
});
|
}
|
||||||
$.on(toggler, 'click', ThreadWatcher.cb.toggle);
|
if (this.isClone) {
|
||||||
return $.before($('input', this.OP.nodes.post), toggler);
|
toggler = $('.watch-thread-link', this.nodes.post);
|
||||||
|
} else {
|
||||||
|
toggler = $.el('img', {
|
||||||
|
className: 'watch-thread-link'
|
||||||
|
});
|
||||||
|
$.before($('input', this.nodes.post), toggler);
|
||||||
|
}
|
||||||
|
return $.on(toggler, 'click', ThreadWatcher.cb.toggle);
|
||||||
|
},
|
||||||
|
catalogNode: function() {
|
||||||
|
if (ThreadWatcher.isWatched(this.thread)) {
|
||||||
|
$.addClass(this.nodes.root, 'watched');
|
||||||
|
}
|
||||||
|
return $.on(this.nodes.thumb.parentNode, 'click', (function(_this) {
|
||||||
|
return function(e) {
|
||||||
|
if (!(e.button === 0 && e.altKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ThreadWatcher.toggle(_this.thread);
|
||||||
|
return e.preventDefault();
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
},
|
},
|
||||||
ready: function() {
|
ready: function() {
|
||||||
var el;
|
var el;
|
||||||
@ -13541,12 +13641,6 @@
|
|||||||
}
|
}
|
||||||
return $.event('CloseMenu');
|
return $.event('CloseMenu');
|
||||||
},
|
},
|
||||||
checkThreads: function() {
|
|
||||||
if ($.hasClass(this, 'disabled')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return ThreadWatcher.fetchAllStatus();
|
|
||||||
},
|
|
||||||
pruneDeads: function() {
|
pruneDeads: function() {
|
||||||
var boardID, data, threadID, _i, _len, _ref, _ref1;
|
var boardID, data, threadID, _i, _len, _ref, _ref1;
|
||||||
if ($.hasClass(this, 'disabled')) {
|
if ($.hasClass(this, 'disabled')) {
|
||||||
@ -13568,7 +13662,10 @@
|
|||||||
return $.event('CloseMenu');
|
return $.event('CloseMenu');
|
||||||
},
|
},
|
||||||
toggle: function() {
|
toggle: function() {
|
||||||
return ThreadWatcher.toggle(Get.threadFromNode(this));
|
ThreadWatcher.toggle(Get.threadFromNode(this));
|
||||||
|
Index.followedThreadID = thread.ID;
|
||||||
|
ThreadWatcher.toggle(thread);
|
||||||
|
return delete Index.followedThreadID;
|
||||||
},
|
},
|
||||||
rm: function() {
|
rm: function() {
|
||||||
var boardID, threadID, _ref;
|
var boardID, threadID, _ref;
|
||||||
@ -13587,9 +13684,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onIndexRefresh: function() {
|
onIndexRefresh: function() {
|
||||||
var boardID, data, threadID, _ref;
|
var boardID, data, db, threadID, _ref;
|
||||||
|
db = ThreadWatcher.db;
|
||||||
boardID = g.BOARD.ID;
|
boardID = g.BOARD.ID;
|
||||||
_ref = ThreadWatcher.db.data.boards[boardID];
|
db.forceSync();
|
||||||
|
_ref = db.data.boards[boardID];
|
||||||
for (threadID in _ref) {
|
for (threadID in _ref) {
|
||||||
data = _ref[threadID];
|
data = _ref[threadID];
|
||||||
if (!data.isDead && !(threadID in g.BOARD.threads)) {
|
if (!data.isDead && !(threadID in g.BOARD.threads)) {
|
||||||
@ -13628,10 +13727,12 @@
|
|||||||
},
|
},
|
||||||
fetchAllStatus: function() {
|
fetchAllStatus: function() {
|
||||||
var thread, threads, _i, _len;
|
var thread, threads, _i, _len;
|
||||||
|
ThreadWatcher.db.forceSync();
|
||||||
|
ThreadWatcher.unreaddb.forceSync();
|
||||||
|
QR.db.forceSync();
|
||||||
if (!(threads = ThreadWatcher.getAll()).length) {
|
if (!(threads = ThreadWatcher.getAll()).length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ThreadWatcher.status.textContent = '...';
|
|
||||||
for (_i = 0, _len = threads.length; _i < _len; _i++) {
|
for (_i = 0, _len = threads.length; _i < _len; _i++) {
|
||||||
thread = threads[_i];
|
thread = threads[_i];
|
||||||
ThreadWatcher.fetchStatus(thread);
|
ThreadWatcher.fetchStatus(thread);
|
||||||
@ -13640,43 +13741,103 @@
|
|||||||
fetchStatus: function(_arg) {
|
fetchStatus: function(_arg) {
|
||||||
var boardID, data, fetchCount, threadID;
|
var boardID, data, fetchCount, threadID;
|
||||||
boardID = _arg.boardID, threadID = _arg.threadID, data = _arg.data;
|
boardID = _arg.boardID, threadID = _arg.threadID, data = _arg.data;
|
||||||
if (data.isDead) {
|
if (data.isDead && !Conf['Show Unread Count']) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetchCount = ThreadWatcher.fetchCount;
|
fetchCount = ThreadWatcher.fetchCount;
|
||||||
|
if (fetchCount.fetching === 0) {
|
||||||
|
ThreadWatcher.status.textContent = '...';
|
||||||
|
$.addClass(ThreadWatcher.refreshButton, 'fa-spin');
|
||||||
|
}
|
||||||
fetchCount.fetching++;
|
fetchCount.fetching++;
|
||||||
return $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", {
|
return $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", {
|
||||||
onloadend: function() {
|
onloadend: function() {
|
||||||
var status;
|
var isDead, lastReadPost, match, postObj, quotingYou, regexp, status, unread, _i, _len, _ref, _ref1;
|
||||||
fetchCount.fetched++;
|
fetchCount.fetched++;
|
||||||
if (fetchCount.fetched === fetchCount.fetching) {
|
if (fetchCount.fetched === fetchCount.fetching) {
|
||||||
fetchCount.fetched = 0;
|
fetchCount.fetched = 0;
|
||||||
fetchCount.fetching = 0;
|
fetchCount.fetching = 0;
|
||||||
status = '';
|
status = '';
|
||||||
|
$.rmClass(ThreadWatcher.refreshButton, 'fa-spin');
|
||||||
} else {
|
} else {
|
||||||
status = "" + (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%";
|
status = "" + (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%";
|
||||||
}
|
}
|
||||||
ThreadWatcher.status.textContent = status;
|
ThreadWatcher.status.textContent = status;
|
||||||
if (this.status !== 404) {
|
if (this.status === 200 && this.response) {
|
||||||
return;
|
isDead = !!this.response.posts[0].archived;
|
||||||
}
|
if (isDead && Conf['Auto Prune']) {
|
||||||
if (Conf['Auto Prune']) {
|
ThreadWatcher.db["delete"]({
|
||||||
ThreadWatcher.db["delete"]({
|
boardID: boardID,
|
||||||
boardID: boardID,
|
threadID: threadID
|
||||||
threadID: threadID
|
});
|
||||||
});
|
ThreadWatcher.refresh();
|
||||||
} else {
|
return;
|
||||||
data.isDead = true;
|
}
|
||||||
ThreadWatcher.db.set({
|
lastReadPost = ThreadWatcher.unreaddb.get({
|
||||||
boardID: boardID,
|
boardID: boardID,
|
||||||
threadID: threadID,
|
threadID: threadID,
|
||||||
val: data
|
defaultValue: 0
|
||||||
});
|
});
|
||||||
|
unread = quotingYou = 0;
|
||||||
|
_ref = this.response.posts;
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
postObj = _ref[_i];
|
||||||
|
if (!(postObj.no > lastReadPost)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((_ref1 = QR.db) != null ? _ref1.get({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID,
|
||||||
|
postID: postObj.no
|
||||||
|
}) : void 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unread++;
|
||||||
|
if (!(QR.db && postObj.com)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
regexp = /<a [^>]*\bhref="(?:\/([^\/]+)\/thread\/(\d+))?(?:#p(\d+))?"/g;
|
||||||
|
while (match = regexp.exec(postObj.com)) {
|
||||||
|
if (QR.db.get({
|
||||||
|
boardID: match[1] || boardID,
|
||||||
|
threadID: match[2] || threadID,
|
||||||
|
postID: match[3] || match[2] || threadID
|
||||||
|
})) {
|
||||||
|
quotingYou++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isDead !== data.isDead || unread !== data.unread || quotingYou !== data.quotingYou) {
|
||||||
|
data.isDead = isDead;
|
||||||
|
data.unread = unread;
|
||||||
|
data.quotingYou = quotingYou;
|
||||||
|
ThreadWatcher.db.set({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID,
|
||||||
|
val: data
|
||||||
|
});
|
||||||
|
return ThreadWatcher.refresh();
|
||||||
|
}
|
||||||
|
} else if (this.status === 404) {
|
||||||
|
if (Conf['Auto Prune']) {
|
||||||
|
ThreadWatcher.db["delete"]({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data.isDead = true;
|
||||||
|
delete data.unread;
|
||||||
|
delete data.quotingYou;
|
||||||
|
ThreadWatcher.db.set({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID,
|
||||||
|
val: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return ThreadWatcher.refresh();
|
||||||
}
|
}
|
||||||
return ThreadWatcher.refresh();
|
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
type: 'head'
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getAll: function() {
|
getAll: function() {
|
||||||
@ -13700,24 +13861,31 @@
|
|||||||
return all;
|
return all;
|
||||||
},
|
},
|
||||||
makeLine: function(boardID, threadID, data) {
|
makeLine: function(boardID, threadID, data) {
|
||||||
var div, fullID, href, link, x;
|
var count, div, fullID, link, title, x;
|
||||||
x = $.el('a', {
|
x = $.el('a', {
|
||||||
className: 'fa',
|
className: 'fa',
|
||||||
href: 'javascript:;',
|
href: 'javascript:;',
|
||||||
textContent: '\uf00d'
|
textContent: '\uf00d'
|
||||||
});
|
});
|
||||||
$.on(x, 'click', ThreadWatcher.cb.rm);
|
$.on(x, 'click', ThreadWatcher.cb.rm);
|
||||||
if (data.isDead) {
|
|
||||||
href = Redirect.to('thread', {
|
|
||||||
boardID: boardID,
|
|
||||||
threadID: threadID
|
|
||||||
});
|
|
||||||
}
|
|
||||||
link = $.el('a', {
|
link = $.el('a', {
|
||||||
href: href || ("/" + boardID + "/thread/" + threadID),
|
href: "/" + boardID + "/thread/" + threadID,
|
||||||
textContent: data.excerpt,
|
textContent: data.excerpt,
|
||||||
title: data.excerpt
|
title: data.excerpt,
|
||||||
|
className: 'watcher-link'
|
||||||
});
|
});
|
||||||
|
if (Conf['Show Unread Count'] && (data.unread != null)) {
|
||||||
|
count = $.el('span', {
|
||||||
|
textContent: "(" + data.unread + ")",
|
||||||
|
className: 'watcher-unread'
|
||||||
|
});
|
||||||
|
$.add(link, count);
|
||||||
|
}
|
||||||
|
title = $.el('span', {
|
||||||
|
textContent: data.excerpt,
|
||||||
|
className: 'watcher-title'
|
||||||
|
});
|
||||||
|
$.add(link, title);
|
||||||
div = $.el('div');
|
div = $.el('div');
|
||||||
fullID = "" + boardID + "." + threadID;
|
fullID = "" + boardID + "." + threadID;
|
||||||
div.dataset.fullID = fullID;
|
div.dataset.fullID = fullID;
|
||||||
@ -13727,11 +13895,19 @@
|
|||||||
if (data.isDead) {
|
if (data.isDead) {
|
||||||
$.addClass(div, 'dead-thread');
|
$.addClass(div, 'dead-thread');
|
||||||
}
|
}
|
||||||
|
if (Conf['Show Unread Count']) {
|
||||||
|
if (data.unread) {
|
||||||
|
$.addClass(div, 'replies-unread');
|
||||||
|
}
|
||||||
|
if (data.quotingYou) {
|
||||||
|
$.addClass(div, 'replies-quoting-you');
|
||||||
|
}
|
||||||
|
}
|
||||||
$.add(div, [x, $.tn(' '), link]);
|
$.add(div, [x, $.tn(' '), link]);
|
||||||
return div;
|
return div;
|
||||||
},
|
},
|
||||||
refresh: function() {
|
refresh: function() {
|
||||||
var boardID, data, helper, list, nodes, refresher, thread, threadID, threads, toggler, watched, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3;
|
var boardID, data, list, nodes, refresher, threadID, _i, _j, _len, _len1, _ref, _ref1, _ref2;
|
||||||
nodes = [];
|
nodes = [];
|
||||||
_ref = ThreadWatcher.getAll();
|
_ref = ThreadWatcher.getAll();
|
||||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
@ -13741,24 +13917,76 @@
|
|||||||
list = ThreadWatcher.list;
|
list = ThreadWatcher.list;
|
||||||
$.rmAll(list);
|
$.rmAll(list);
|
||||||
$.add(list, nodes);
|
$.add(list, nodes);
|
||||||
threads = g.BOARD.threads;
|
g.threads.forEach(function(thread) {
|
||||||
_ref2 = threads.keys;
|
var helper, post, toggler, _j, _len1, _ref2;
|
||||||
|
helper = ThreadWatcher.isWatched(thread) ? ['addClass', 'Unwatch'] : ['rmClass', 'Watch'];
|
||||||
|
if (thread.OP) {
|
||||||
|
_ref2 = [thread.OP].concat(__slice.call(thread.OP.clones));
|
||||||
|
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
||||||
|
post = _ref2[_j];
|
||||||
|
toggler = $('.watch-thread-link', post.nodes.post);
|
||||||
|
$[helper[0]](toggler, 'watched');
|
||||||
|
toggler.title = "" + helper[1] + " Thread";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (thread.catalogView) {
|
||||||
|
return $[helper[0]](thread.catalogView.nodes.root, 'watched');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_ref2 = ThreadWatcher.menu.refreshers;
|
||||||
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
||||||
threadID = _ref2[_j];
|
refresher = _ref2[_j];
|
||||||
thread = threads[threadID];
|
refresher();
|
||||||
toggler = $('.watch-thread-link', thread.OP.nodes.post);
|
}
|
||||||
watched = ThreadWatcher.db.get({
|
if (Index.nodes && Conf['Pin Watched Threads']) {
|
||||||
boardID: thread.board.ID,
|
Index.sort();
|
||||||
|
return Index.buildIndex();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
update: function(boardID, threadID, newData) {
|
||||||
|
var data, key, line, n, newLine, val, _ref;
|
||||||
|
if (!(data = (_ref = ThreadWatcher.db) != null ? _ref.get({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID
|
||||||
|
}) : void 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newData.isDead && Conf['Auto Prune']) {
|
||||||
|
ThreadWatcher.db["delete"]({
|
||||||
|
boardID: boardID,
|
||||||
threadID: threadID
|
threadID: threadID
|
||||||
});
|
});
|
||||||
helper = watched ? ['addClass', 'Unwatch'] : ['rmClass', 'Watch'];
|
ThreadWatcher.refresh();
|
||||||
$[helper[0]](toggler, 'watched');
|
return;
|
||||||
toggler.title = "" + helper[1] + " Thread";
|
|
||||||
}
|
}
|
||||||
_ref3 = ThreadWatcher.menu.refreshers;
|
n = 0;
|
||||||
for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) {
|
for (key in newData) {
|
||||||
refresher = _ref3[_k];
|
val = newData[key];
|
||||||
refresher();
|
if (data[key] !== val) {
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!n) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ThreadWatcher.db.forceSync();
|
||||||
|
if (!(data = ThreadWatcher.db.get({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID
|
||||||
|
}))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$.extend(data, newData);
|
||||||
|
ThreadWatcher.db.set({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID,
|
||||||
|
val: data
|
||||||
|
});
|
||||||
|
if (line = $("#watched-threads > [data-full-i-d='" + boardID + "." + threadID + "']", ThreadWatcher.dialog)) {
|
||||||
|
newLine = ThreadWatcher.makeLine(boardID, threadID, data);
|
||||||
|
return $.replace(line, newLine);
|
||||||
|
} else {
|
||||||
|
return ThreadWatcher.refresh();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggle: function(thread) {
|
toggle: function(thread) {
|
||||||
@ -13795,7 +14023,14 @@
|
|||||||
threadID: threadID,
|
threadID: threadID,
|
||||||
val: data
|
val: data
|
||||||
});
|
});
|
||||||
return ThreadWatcher.refresh();
|
ThreadWatcher.refresh();
|
||||||
|
if (Conf['Show Unread Count']) {
|
||||||
|
return ThreadWatcher.fetchStatus({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID,
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
rm: function(boardID, threadID) {
|
rm: function(boardID, threadID) {
|
||||||
ThreadWatcher.db["delete"]({
|
ThreadWatcher.db["delete"]({
|
||||||
@ -13825,12 +14060,12 @@
|
|||||||
if (!Conf['Thread Watcher']) {
|
if (!Conf['Thread Watcher']) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
menu = new UI.Menu();
|
menu = this.menu = new UI.Menu('thread watcher');
|
||||||
$.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) {
|
$.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) {
|
||||||
return menu.toggle(e, this, ThreadWatcher);
|
return menu.toggle(e, this, ThreadWatcher);
|
||||||
});
|
});
|
||||||
this.addHeaderMenuEntry();
|
this.addHeaderMenuEntry();
|
||||||
return this.addMenuEntries(menu);
|
return this.addMenuEntries;
|
||||||
},
|
},
|
||||||
addHeaderMenuEntry: function() {
|
addHeaderMenuEntry: function() {
|
||||||
var entryEl;
|
var entryEl;
|
||||||
@ -13855,7 +14090,7 @@
|
|||||||
return entryEl.textContent = text;
|
return entryEl.textContent = text;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addMenuEntries: function(menu) {
|
addMenuEntries: function() {
|
||||||
var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1;
|
var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1;
|
||||||
entries = [];
|
entries = [];
|
||||||
entries.push({
|
entries.push({
|
||||||
@ -13869,22 +14104,11 @@
|
|||||||
return (ThreadWatcher.list.firstElementChild ? $.rmClass : $.addClass)(this.el, 'disabled');
|
return (ThreadWatcher.list.firstElementChild ? $.rmClass : $.addClass)(this.el, 'disabled');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
entries.push({
|
|
||||||
cb: ThreadWatcher.cb.checkThreads,
|
|
||||||
entry: {
|
|
||||||
el: $.el('a', {
|
|
||||||
textContent: 'Check 404\'d threads'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
refresh: function() {
|
|
||||||
return ($('div:not(.dead-thread)', ThreadWatcher.list) ? $.rmClass : $.addClass)(this.el, 'disabled');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
entries.push({
|
entries.push({
|
||||||
cb: ThreadWatcher.cb.pruneDeads,
|
cb: ThreadWatcher.cb.pruneDeads,
|
||||||
entry: {
|
entry: {
|
||||||
el: $.el('a', {
|
el: $.el('a', {
|
||||||
textContent: 'Prune 404\'d threads'
|
textContent: 'Prune dead threads'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
refresh: function() {
|
refresh: function() {
|
||||||
@ -13916,22 +14140,18 @@
|
|||||||
if (refresh) {
|
if (refresh) {
|
||||||
this.refreshers.push(refresh.bind(entry));
|
this.refreshers.push(refresh.bind(entry));
|
||||||
}
|
}
|
||||||
menu.addEntry(entry);
|
this.menu.addEntry(entry);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createSubEntry: function(name, desc) {
|
createSubEntry: function(name, desc) {
|
||||||
var entry, input;
|
var entry, input;
|
||||||
entry = {
|
entry = {
|
||||||
type: 'thread watcher',
|
type: 'thread watcher',
|
||||||
el: $.el('label', {
|
el: UI.checkbox(name, " " + name)
|
||||||
innerHTML: "<input type=checkbox name='" + name + "'> " + name,
|
|
||||||
title: desc
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
input = entry.el.firstElementChild;
|
input = entry.el.firstElementChild;
|
||||||
input.checked = Conf[name];
|
|
||||||
$.on(input, 'change', $.cb.checked);
|
$.on(input, 'change', $.cb.checked);
|
||||||
if (name === 'Current Board') {
|
if (name === 'Current Board' || name === 'Show Unread Count') {
|
||||||
$.on(input, 'change', ThreadWatcher.refresh);
|
$.on(input, 'change', ThreadWatcher.refresh);
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
@ -15757,10 +15977,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getThread: function() {
|
getThread: function() {
|
||||||
var threadRoot, _i, _len, _ref;
|
var thread, threadRoot, _i, _len, _ref;
|
||||||
_ref = $$('.thread');
|
_ref = $$('.thread');
|
||||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
threadRoot = _ref[_i];
|
threadRoot = _ref[_i];
|
||||||
|
thread = Get.threadFromRoot(threadRoot);
|
||||||
|
if (thread.isHidden && !thread.stub) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height) {
|
if (Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height) {
|
||||||
return threadRoot;
|
return threadRoot;
|
||||||
}
|
}
|
||||||
@ -15768,7 +15992,10 @@
|
|||||||
return $('.board');
|
return $('.board');
|
||||||
},
|
},
|
||||||
scroll: function(delta) {
|
scroll: function(delta) {
|
||||||
var axis, next, thread, top;
|
var axis, extra, next, thread, top, _ref;
|
||||||
|
if ((_ref = d.activeElement) != null) {
|
||||||
|
_ref.blur();
|
||||||
|
}
|
||||||
thread = Nav.getThread();
|
thread = Nav.getThread();
|
||||||
axis = delta === +1 ? 'following' : 'preceding';
|
axis = delta === +1 ? 'following' : 'preceding';
|
||||||
if (next = $.x("" + axis + "-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread)) {
|
if (next = $.x("" + axis + "-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread)) {
|
||||||
@ -15777,48 +16004,64 @@
|
|||||||
thread = next;
|
thread = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Header.scrollTo(thread);
|
extra = Header.getTopOf(thread) + doc.clientHeight - d.body.getBoundingClientRect().bottom;
|
||||||
|
if (extra > 0) {
|
||||||
|
d.body.style.marginBottom = "" + extra + "px";
|
||||||
|
}
|
||||||
|
Header.scrollTo(thread);
|
||||||
|
if (extra > 0 && !Nav.haveExtra) {
|
||||||
|
Nav.haveExtra = true;
|
||||||
|
return $.on(d, 'scroll', Nav.removeExtra);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeExtra: function() {
|
||||||
|
var extra;
|
||||||
|
extra = doc.clientHeight - d.body.getBoundingClientRect().bottom;
|
||||||
|
if (extra > 0) {
|
||||||
|
return d.body.style.marginBottom = "" + extra + "px";
|
||||||
|
} else {
|
||||||
|
d.body.style.marginBottom = null;
|
||||||
|
delete Nav.haveExtra;
|
||||||
|
return $.off(d, 'scroll', Nav.removeExtra);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
RelativeDates = {
|
RelativeDates = {
|
||||||
INTERVAL: $.MINUTE / 2,
|
INTERVAL: $.MINUTE / 2,
|
||||||
init: function() {
|
init: function() {
|
||||||
switch (g.VIEW) {
|
var _ref;
|
||||||
case 'index':
|
if (((_ref = g.VIEW) === 'index' || _ref === 'thread') && Conf['Relative Post Dates'] && !Conf['Relative Date Title'] || g.VIEW === 'index' && Conf['JSON Navigation'] && g.BOARD.ID !== 'f') {
|
||||||
this.flush();
|
this.flush();
|
||||||
$.on(d, 'visibilitychange', this.flush);
|
$.on(d, 'visibilitychange ThreadUpdate', this.flush);
|
||||||
if (!Conf['Relative Post Dates']) {
|
}
|
||||||
return;
|
if (Conf['Relative Post Dates']) {
|
||||||
}
|
return Post.callbacks.push({
|
||||||
break;
|
name: 'Relative Post Dates',
|
||||||
case 'thread':
|
cb: this.node
|
||||||
if (!Conf['Relative Post Dates']) {
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.flush();
|
|
||||||
$.on(d, 'visibilitychange ThreadUpdate', this.flush);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
return Post.callbacks.push({
|
|
||||||
name: 'Relative Post Dates',
|
|
||||||
cb: this.node
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
node: function() {
|
node: function() {
|
||||||
var dateEl;
|
var dateEl;
|
||||||
|
dateEl = this.nodes.date;
|
||||||
|
if (Conf['Relative Date Title']) {
|
||||||
|
$.on(dateEl, 'mouseover', (function(_this) {
|
||||||
|
return function() {
|
||||||
|
return RelativeDates.hover(_this);
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.isClone) {
|
if (this.isClone) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dateEl = this.nodes.date;
|
|
||||||
dateEl.title = dateEl.textContent;
|
dateEl.title = dateEl.textContent;
|
||||||
return RelativeDates.update(this);
|
return RelativeDates.update(this);
|
||||||
},
|
},
|
||||||
relative: function(diff, now, date) {
|
relative: function(diff, now, date) {
|
||||||
var days, months, number, rounded, unit, years;
|
var days, months, number, rounded, unit, years;
|
||||||
unit = (number = diff / $.DAY) >= 1 ? (years = now.getYear() - date.getYear(), months = now.getMonth() - date.getMonth(), days = now.getDate() - date.getDate(), years > 1 ? (number = years - (months < 0 || months === 0 && days < 0), 'year') : years === 1 && (months > 0 || months === 0 && days >= 0) ? (number = years, 'year') : (months = (months + 12) % 12) > 1 ? (number = months - (days < 0), 'month') : months === 1 && days >= 0 ? (number = months, 'month') : 'day') : (number = diff / $.HOUR) >= 1 ? 'hour' : (number = diff / $.MINUTE) >= 1 ? 'minute' : (number = Math.max(0, diff) / $.SECOND, 'second');
|
unit = (number = diff / $.DAY) >= 1 ? (years = now.getYear() - date.getYear(), months = now.getMonth() - date.getMonth(), days = now.getDate() - date.getDate(), years > 1 ? (number = years - (months < 0 || months === 0 && days < 0), 'year') : years === 1 && (months > 0 || months === 0 && days >= 0) ? (number = years, 'year') : (months = months + 12 * years) > 1 ? (number = months - (days < 0), 'month') : months === 1 && days >= 0 ? (number = months, 'month') : 'day') : (number = diff / $.HOUR) >= 1 ? 'hour' : (number = diff / $.MINUTE) >= 1 ? 'minute' : (number = Math.max(0, diff) / $.SECOND, 'second');
|
||||||
rounded = Math.round(number);
|
rounded = Math.round(number);
|
||||||
if (rounded !== 1) {
|
if (rounded !== 1) {
|
||||||
unit += 's';
|
unit += 's';
|
||||||
@ -15841,6 +16084,13 @@
|
|||||||
clearTimeout(RelativeDates.timeout);
|
clearTimeout(RelativeDates.timeout);
|
||||||
return RelativeDates.timeout = setTimeout(RelativeDates.flush, RelativeDates.INTERVAL);
|
return RelativeDates.timeout = setTimeout(RelativeDates.flush, RelativeDates.INTERVAL);
|
||||||
},
|
},
|
||||||
|
hover: function(post) {
|
||||||
|
var date, diff, now;
|
||||||
|
date = post.info.date;
|
||||||
|
now = new Date();
|
||||||
|
diff = now - date;
|
||||||
|
return post.nodes.date.title = RelativeDates.relative(diff, now, date);
|
||||||
|
},
|
||||||
update: function(data, now) {
|
update: function(data, now) {
|
||||||
var date, diff, isPost, relative, singlePost, _i, _len, _ref;
|
var date, diff, isPost, relative, singlePost, _i, _len, _ref;
|
||||||
isPost = data instanceof Post;
|
isPost = data instanceof Post;
|
||||||
@ -15880,44 +16130,45 @@
|
|||||||
if (Conf['Reveal Spoilers']) {
|
if (Conf['Reveal Spoilers']) {
|
||||||
$.addClass(doc, 'reveal-spoilers');
|
$.addClass(doc, 'reveal-spoilers');
|
||||||
}
|
}
|
||||||
if (Conf['Remove Spoilers']) {
|
if (!Conf['Remove Spoilers']) {
|
||||||
return $.addClass(doc, 'remove-spoilers');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Report = {
|
|
||||||
init: function() {
|
|
||||||
if (!/report/.test(location.search)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return $.asap((function() {
|
$.addClass(doc, 'remove-spoilers');
|
||||||
return $.id('recaptcha_response_field');
|
Post.callbacks.push({
|
||||||
}), Report.ready);
|
name: 'Reveal Spoilers',
|
||||||
|
cb: this.node
|
||||||
|
});
|
||||||
|
CatalogThread.callbacks.push({
|
||||||
|
name: 'Reveal Spoilers',
|
||||||
|
cb: this.node
|
||||||
|
});
|
||||||
|
if (g.VIEW === 'archive') {
|
||||||
|
return $.ready(function() {
|
||||||
|
return RemoveSpoilers.unspoiler($.id('arc-list'));
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ready: function() {
|
node: function(post) {
|
||||||
var field;
|
return RemoveSpoilers.unspoiler(this.nodes.comment);
|
||||||
field = $.id('recaptcha_response_field');
|
},
|
||||||
$.on(field, 'keydown', function(e) {
|
unspoiler: function(el) {
|
||||||
if (e.keyCode === 8 && !field.value) {
|
var span, spoiler, spoilers, _i, _len;
|
||||||
return $.globalEval('Recaptcha.reload("t")');
|
spoilers = $$('s', el);
|
||||||
}
|
for (_i = 0, _len = spoilers.length; _i < _len; _i++) {
|
||||||
});
|
spoiler = spoilers[_i];
|
||||||
return $.on($('form'), 'submit', function(e) {
|
span = $.el('span', {
|
||||||
var response;
|
className: 'removed-spoiler'
|
||||||
e.preventDefault();
|
});
|
||||||
response = field.value.trim();
|
$.replace(spoiler, span);
|
||||||
if (!/\s|^\d+$/.test(response)) {
|
$.add(span, __slice.call(spoiler.childNodes));
|
||||||
field.value = "" + response + " " + response;
|
}
|
||||||
}
|
|
||||||
return this.submit();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Time = {
|
Time = {
|
||||||
init: function() {
|
init: function() {
|
||||||
if (!Conf['Time Formatting']) {
|
var _ref;
|
||||||
|
if (!(((_ref = g.VIEW) === 'index' || _ref === 'thread') && Conf['Time Formatting'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return Post.callbacks.push({
|
return Post.callbacks.push({
|
||||||
@ -15932,7 +16183,7 @@
|
|||||||
return this.nodes.date.textContent = Time.format(Conf['time'], this.info.date);
|
return this.nodes.date.textContent = Time.format(Conf['time'], this.info.date);
|
||||||
},
|
},
|
||||||
format: function(formatString, date) {
|
format: function(formatString, date) {
|
||||||
return formatString.replace(/%([A-Za-z])/g, function(s, c) {
|
return formatString.replace(/%(.)/g, function(s, c) {
|
||||||
if (c in Time.formatters) {
|
if (c in Time.formatters) {
|
||||||
return Time.formatters[c].call(date);
|
return Time.formatters[c].call(date);
|
||||||
} else {
|
} else {
|
||||||
@ -16008,6 +16259,9 @@
|
|||||||
},
|
},
|
||||||
Y: function() {
|
Y: function() {
|
||||||
return this.getFullYear();
|
return this.getFullYear();
|
||||||
|
},
|
||||||
|
'%': function() {
|
||||||
|
return '%';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -88,7 +88,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, Build, Callbacks, CatalogLinks, CatalogThread, Clone, Color, Conf, Config, CrossOrigin, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, E, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Flash, Fourchan, Gallery, Get, GlobalMessage, Header, IDColor, IDHighlight, ImageCommon, ImageExpand, ImageHover, ImageLoader, Index, JSColor, Keybinds, Linkify, Main, MarkNewIPs, MascotTools, Mascots, Menu, Nav, Navigate, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteInline, QuoteMarkers, QuotePreview, QuoteStrikeThrough, QuoteThreading, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Rice, Sauce, Settings, SimpleDict, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, editMascot, editTheme, g, userNavigation,
|
var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, Build, Callbacks, CatalogLinks, CatalogThread, Clone, Color, Conf, Config, CrossOrigin, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, E, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Flash, Fourchan, Gallery, Get, GlobalMessage, Header, IDColor, IDHighlight, ImageCommon, ImageExpand, ImageHover, ImageLoader, Index, JSColor, Keybinds, Linkify, Main, MarkNewIPs, MascotTools, Mascots, Menu, Nav, Navigate, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteInline, QuoteMarkers, QuotePreview, QuoteStrikeThrough, QuoteThreading, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReportLink, RevealSpoilers, Rice, Sauce, Settings, SimpleDict, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, editMascot, editTheme, g, userNavigation,
|
||||||
__slice = [].slice,
|
__slice = [].slice,
|
||||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||||
__hasProp = {}.hasOwnProperty,
|
__hasProp = {}.hasOwnProperty,
|
||||||
@ -3233,6 +3233,7 @@
|
|||||||
this.isPinned = false;
|
this.isPinned = false;
|
||||||
this.isSticky = false;
|
this.isSticky = false;
|
||||||
this.isClosed = false;
|
this.isClosed = false;
|
||||||
|
this.isArchived = false;
|
||||||
this.postLimit = false;
|
this.postLimit = false;
|
||||||
this.fileLimit = false;
|
this.fileLimit = false;
|
||||||
this.ipCount = void 0;
|
this.ipCount = void 0;
|
||||||
@ -12769,6 +12770,19 @@
|
|||||||
|
|
||||||
Favicon = {
|
Favicon = {
|
||||||
init: function() {
|
init: function() {
|
||||||
|
return $.asap((function() {
|
||||||
|
return d.head && (Favicon.el = $('link[rel="shortcut icon"]', d.head));
|
||||||
|
}), Favicon.initAsap);
|
||||||
|
},
|
||||||
|
initAsap: function() {
|
||||||
|
var href;
|
||||||
|
Favicon.el.type = 'image/x-icon';
|
||||||
|
href = Favicon.el.href;
|
||||||
|
Favicon.SFW = /ws\.ico$/.test(href);
|
||||||
|
Favicon["default"] = href;
|
||||||
|
return Favicon["switch"]();
|
||||||
|
},
|
||||||
|
"switch": function() {
|
||||||
var f, funreadDeadY, i, items, t;
|
var f, funreadDeadY, i, items, t;
|
||||||
items = {
|
items = {
|
||||||
ferongr: ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///9zBQC/AADpDAP/gID/q6voCwJJTwpOAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxUlEQVR42q1TOwrCQBB9s0FRtJI0WoqFtSLYegoP4gVSeJsUHsHSI3iFeIqRXXgwrhlXwYHHhLwPTB7B36abBCV+0pA4DUBQUNZYQptGtW3jtoKyxgoe0yrBCoyZfL/5ioQ3URZOXW9I341l3oo+NXEZiW4CEuIzvPECopED4OaZ3RNmeAm4u+a8Jr5f17VyVoL8fr8qcltzwlyyj2iqcgPOQ9ExkHAITgD75bYBe0A5S4H/P9htuWMF3QXoQpwaKeT+lnsC6JE5I6aq6fEAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///8AcH4AtswA2PJ55fKi6fIA1/FtpPADAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxElEQVQ4y2NgoBq4/vE/HJOsBiRQUIfA2AzBqQYqUfn00/9FLz+BaQxDCKqBmX7jExijKEDSDJPHrnnbGQhGV4RmOFwdVkNwhQMheYwQxhaIi7b9Z9A3gWAQm2BUoQOgRhgA8o7j1ozLC4LCyAZcx6kZI5qg4kLKqggDFFWxJySsUQVzlb4pwgAJaTRvokcVNgOqOv8zcHBCsL07DgNg8YsczzA5MxtUL+DMD8g0slxI/H8GQ/P/DJKyeKIRpglXZsIiBwBhP5O+VbI/JgAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///8oeQBJ3ABV/wHM/7Lu/+ZU/gAqUP3dAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAx0lEQVQ4y2NgoBYI+cfwH4ZJVgMS0KhEYGyG4FQDkzjzf9P/d/+fgWl0QwiqgSkI/c8IxsgKkDXD5LFq9rwDweiK0A2HqcNqCK5wICSPEcLYAtH+AMN/IXMIBrEJRie6OEgjDAC5x3FqxuUFNiEUA67j1IweTTBxBQ1puAG86jgSEraogskJWSBcwCGF5k30qMJmgMFEhv/MXBAs5oLDAFj8IsczTE7UEeECbhU8+QGZRpaTi2b4L2zF8J9TGk80wjThykzY5AAW/2O1C2mIbgAAAABJRU5ErkJggg=='],
|
ferongr: ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///9zBQC/AADpDAP/gID/q6voCwJJTwpOAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxUlEQVR42q1TOwrCQBB9s0FRtJI0WoqFtSLYegoP4gVSeJsUHsHSI3iFeIqRXXgwrhlXwYHHhLwPTB7B36abBCV+0pA4DUBQUNZYQptGtW3jtoKyxgoe0yrBCoyZfL/5ioQ3URZOXW9I341l3oo+NXEZiW4CEuIzvPECopED4OaZ3RNmeAm4u+a8Jr5f17VyVoL8fr8qcltzwlyyj2iqcgPOQ9ExkHAITgD75bYBe0A5S4H/P9htuWMF3QXoQpwaKeT+lnsC6JE5I6aq6fEAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///8AcH4AtswA2PJ55fKi6fIA1/FtpPADAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxElEQVQ4y2NgoBq4/vE/HJOsBiRQUIfA2AzBqQYqUfn00/9FLz+BaQxDCKqBmX7jExijKEDSDJPHrnnbGQhGV4RmOFwdVkNwhQMheYwQxhaIi7b9Z9A3gWAQm2BUoQOgRhgA8o7j1ozLC4LCyAZcx6kZI5qg4kLKqggDFFWxJySsUQVzlb4pwgAJaTRvokcVNgOqOv8zcHBCsL07DgNg8YsczzA5MxtUL+DMD8g0slxI/H8GQ/P/DJKyeKIRpglXZsIiBwBhP5O+VbI/JgAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///8oeQBJ3ABV/wHM/7Lu/+ZU/gAqUP3dAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAx0lEQVQ4y2NgoBYI+cfwH4ZJVgMS0KhEYGyG4FQDkzjzf9P/d/+fgWl0QwiqgSkI/c8IxsgKkDXD5LFq9rwDweiK0A2HqcNqCK5wICSPEcLYAtH+AMN/IXMIBrEJRie6OEgjDAC5x3FqxuUFNiEUA67j1IweTTBxBQ1puAG86jgSEraogskJWSBcwCGF5k30qMJmgMFEhv/MXBAs5oLDAFj8IsczTE7UEeECbhU8+QGZRpaTi2b4L2zF8J9TGk80wjThykzY5AAW/2O1C2mIbgAAAABJRU5ErkJggg=='],
|
||||||
@ -12884,7 +12898,7 @@
|
|||||||
|
|
||||||
ThreadExcerpt = {
|
ThreadExcerpt = {
|
||||||
init: function() {
|
init: function() {
|
||||||
if (g.VIEW !== 'thread' || !Conf['Thread Excerpt']) {
|
if ((g.BOARD.ID !== 'f' && g.BOARD.ID !== 'pol') || g.VIEW !== 'thread' || !Conf['Thread Excerpt'] || Conf['Remove Thread Excerpt']) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return Thread.callbacks.push({
|
return Thread.callbacks.push({
|
||||||
@ -12919,7 +12933,10 @@
|
|||||||
});
|
});
|
||||||
Header.addShortcut(sc);
|
Header.addShortcut(sc);
|
||||||
} else {
|
} else {
|
||||||
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "<div class=move title='" + title + "'>" + html + "</div>");
|
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', {
|
||||||
|
innerHTML: "<div class=\"move\" title=\"" + E(statsTitle) + "\">" + statsHTML.innerHTML + "</div>"
|
||||||
|
});
|
||||||
|
$.addClass(doc, 'float');
|
||||||
$.ready(function() {
|
$.ready(function() {
|
||||||
return $.add(d.body, sc);
|
return $.add(d.body, sc);
|
||||||
});
|
});
|
||||||
@ -12940,7 +12957,10 @@
|
|||||||
this.posts.forEach(function(post) {
|
this.posts.forEach(function(post) {
|
||||||
postCount++;
|
postCount++;
|
||||||
if (post.file) {
|
if (post.file) {
|
||||||
return fileCount++;
|
fileCount++;
|
||||||
|
}
|
||||||
|
if (Conf["Page Count in Stats"]) {
|
||||||
|
return ThreadStats.lastPost = post.info.date;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ThreadStats.thread = this;
|
ThreadStats.thread = this;
|
||||||
@ -12999,6 +13019,7 @@
|
|||||||
if (!Conf["Page Count in Stats"]) {
|
if (!Conf["Page Count in Stats"]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
clearTimeout(ThreadStats.timeout);
|
||||||
if (ThreadStats.thread.isDead) {
|
if (ThreadStats.thread.isDead) {
|
||||||
ThreadStats.pageCountEl.textContent = 'Dead';
|
ThreadStats.pageCountEl.textContent = 'Dead';
|
||||||
$.addClass(ThreadStats.pageCountEl, 'warning');
|
$.addClass(ThreadStats.pageCountEl, 'warning');
|
||||||
@ -13027,6 +13048,7 @@
|
|||||||
}
|
}
|
||||||
ThreadStats.pageCountEl.textContent = page.page;
|
ThreadStats.pageCountEl.textContent = page.page;
|
||||||
(page.page === this.response.length ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning');
|
(page.page === this.response.length ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning');
|
||||||
|
ThreadStats.lastPageUpdate = new Date(thread.last_modified * $.SECOND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -13041,15 +13063,18 @@
|
|||||||
}
|
}
|
||||||
if (Conf['Updater and Stats in Header']) {
|
if (Conf['Updater and Stats in Header']) {
|
||||||
this.dialog = sc = $.el('span', {
|
this.dialog = sc = $.el('span', {
|
||||||
innerHTML: "[<span id=update-status></span><span id=update-timer title='Update now'></span>]\u00A0",
|
|
||||||
id: 'updater'
|
id: 'updater'
|
||||||
});
|
});
|
||||||
|
$.extend(sc, {
|
||||||
|
innerHTML: "[<span id=\"update-status\"></span><span id=\"update-timer\" title=\"Update now\"></span>]"
|
||||||
|
});
|
||||||
$.ready(function() {
|
$.ready(function() {
|
||||||
return Header.addShortcut(sc);
|
return Header.addShortcut(sc);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.dialog = sc = UI.dialog('updater', 'bottom: 0px; left: 0px;', "<div class=move><span id=update-status></span><span id=update-timer title='Update now'></span></div>");
|
this.dialog = sc = UI.dialog('updater', 'bottom: 0px; left: 0px;', {
|
||||||
$.addClass(doc, 'float');
|
innerHTML: "<div class=\"move\"></div><span id=\"update-status\"></span><span id=\"update-timer\" title=\"Update now\"></span>"
|
||||||
|
});
|
||||||
$.ready(function() {
|
$.ready(function() {
|
||||||
$.addClass(doc, 'float');
|
$.addClass(doc, 'float');
|
||||||
return $.add(d.body, sc);
|
return $.add(d.body, sc);
|
||||||
@ -13066,24 +13091,21 @@
|
|||||||
for (name in _ref) {
|
for (name in _ref) {
|
||||||
conf = _ref[name];
|
conf = _ref[name];
|
||||||
checked = Conf[name] ? 'checked' : '';
|
checked = Conf[name] ? 'checked' : '';
|
||||||
el = $.el('label', {
|
el = UI.checkbox(name, " " + name);
|
||||||
title: "" + conf[1],
|
|
||||||
innerHTML: "<input name='" + name + "' type=checkbox " + checked + "> " + name
|
|
||||||
});
|
|
||||||
input = el.firstElementChild;
|
input = el.firstElementChild;
|
||||||
$.on(input, 'change', $.cb.checked);
|
$.on(input, 'change', $.cb.checked);
|
||||||
if (input.name === 'Scroll BG') {
|
if (input.name === 'Scroll BG') {
|
||||||
$.on(input, 'change', this.cb.scrollBG);
|
$.on(input, 'change', this.cb.scrollBG);
|
||||||
this.cb.scrollBG();
|
this.cb.scrollBG();
|
||||||
} else if (input.name === 'Auto Update') {
|
} else if (input.name === 'Auto Update') {
|
||||||
$.on(input, 'change', this.cb.update);
|
$.on(input, 'change', this.cb.autoUpdate);
|
||||||
}
|
}
|
||||||
subEntries.push({
|
subEntries.push({
|
||||||
el: el
|
el: el
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.settings = $.el('span', {
|
this.settings = $.el('span', {
|
||||||
innerHTML: '<a href=javascript:;>Interval</a>'
|
innerHTML: "<a href=\"javascript:;\">Interval</a>"
|
||||||
});
|
});
|
||||||
$.on(this.settings, 'click', this.intervalShortcut);
|
$.on(this.settings, 'click', this.intervalShortcut);
|
||||||
subEntries.push({
|
subEntries.push({
|
||||||
@ -13144,6 +13166,7 @@
|
|||||||
ThreadUpdater.thread = this;
|
ThreadUpdater.thread = this;
|
||||||
ThreadUpdater.root = this.OP.nodes.root.parentNode;
|
ThreadUpdater.root = this.OP.nodes.root.parentNode;
|
||||||
ThreadUpdater.lastPost = +this.posts.keys[this.posts.keys.length - 1];
|
ThreadUpdater.lastPost = +this.posts.keys[this.posts.keys.length - 1];
|
||||||
|
ThreadUpdater.outdateCount = 0;
|
||||||
ThreadUpdater.cb.interval.call($.el('input', {
|
ThreadUpdater.cb.interval.call($.el('input', {
|
||||||
value: Conf['Interval'],
|
value: Conf['Interval'],
|
||||||
name: 'Interval'
|
name: 'Interval'
|
||||||
@ -13156,7 +13179,7 @@
|
|||||||
} else {
|
} else {
|
||||||
ThreadUpdater.cb.online();
|
ThreadUpdater.cb.online();
|
||||||
}
|
}
|
||||||
Rice.nodes(ThreadUpdater.dialog);
|
return Rice.nodes(ThreadUpdater.dialog);
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -13166,14 +13189,18 @@
|
|||||||
beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA',
|
beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA',
|
||||||
cb: {
|
cb: {
|
||||||
online: function() {
|
online: function() {
|
||||||
|
if (ThreadUpdater.thread.isDead) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (ThreadUpdater.online = navigator.onLine) {
|
if (ThreadUpdater.online = navigator.onLine) {
|
||||||
ThreadUpdater.outdateCount = 0;
|
ThreadUpdater.outdateCount = 0;
|
||||||
ThreadUpdater.setInterval();
|
ThreadUpdater.setInterval();
|
||||||
ThreadUpdater.set('status', null, null);
|
ThreadUpdater.set('status', '', '');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ThreadUpdater.set('timer', null);
|
ThreadUpdater.set('timer', '');
|
||||||
return ThreadUpdater.set('status', 'Offline', 'warning');
|
ThreadUpdater.set('status', 'Offline', 'warning');
|
||||||
|
return clearTimeout(ThreadUpdater.timeoutID);
|
||||||
},
|
},
|
||||||
post: function(e) {
|
post: function(e) {
|
||||||
if (!(ThreadUpdater.isUpdating && e.detail.threadID === ThreadUpdater.thread.ID)) {
|
if (!(ThreadUpdater.isUpdating && e.detail.threadID === ThreadUpdater.thread.ID)) {
|
||||||
@ -13186,14 +13213,14 @@
|
|||||||
},
|
},
|
||||||
checkpost: function(e) {
|
checkpost: function(e) {
|
||||||
if (!ThreadUpdater.checkPostCount) {
|
if (!ThreadUpdater.checkPostCount) {
|
||||||
if (e.detail.threadID !== ThreadUpdater.thread.ID) {
|
if (e && e.detail.threadID !== ThreadUpdater.thread.ID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ThreadUpdater.seconds = 0;
|
ThreadUpdater.seconds = 0;
|
||||||
ThreadUpdater.outdateCount = 0;
|
ThreadUpdater.outdateCount = 0;
|
||||||
ThreadUpdater.set('timer', '...');
|
ThreadUpdater.set('timer', '...');
|
||||||
}
|
}
|
||||||
if (!(g.DEAD || ThreadUpdater.foundPost || ThreadUpdater.checkPostCount >= 5)) {
|
if (!(ThreadUpdater.thread.isDead || ThreadUpdater.foundPost || ThreadUpdater.checkPostCount >= 5)) {
|
||||||
return setTimeout(ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND);
|
return setTimeout(ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND);
|
||||||
}
|
}
|
||||||
ThreadUpdater.setInterval();
|
ThreadUpdater.setInterval();
|
||||||
@ -13217,6 +13244,9 @@
|
|||||||
return !d.hidden;
|
return !d.hidden;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
autoUpdate: function(e) {
|
||||||
|
return ThreadUpdater.count(ThreadUpdater.isUpdating = this.checked);
|
||||||
|
},
|
||||||
interval: function(e) {
|
interval: function(e) {
|
||||||
var val;
|
var val;
|
||||||
val = parseInt(this.value, 10);
|
val = parseInt(this.value, 10);
|
||||||
@ -13233,7 +13263,6 @@
|
|||||||
req = ThreadUpdater.req;
|
req = ThreadUpdater.req;
|
||||||
switch (req.status) {
|
switch (req.status) {
|
||||||
case 200:
|
case 200:
|
||||||
g.DEAD = false;
|
|
||||||
ThreadUpdater.parse(req.response.posts);
|
ThreadUpdater.parse(req.response.posts);
|
||||||
if (ThreadUpdater.thread.isArchived) {
|
if (ThreadUpdater.thread.isArchived) {
|
||||||
ThreadUpdater.set('status', 'Archived', 'warning');
|
ThreadUpdater.set('status', 'Archived', 'warning');
|
||||||
@ -13338,9 +13367,10 @@
|
|||||||
var n;
|
var n;
|
||||||
ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000);
|
ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000);
|
||||||
if (!(n = --ThreadUpdater.seconds)) {
|
if (!(n = --ThreadUpdater.seconds)) {
|
||||||
|
ThreadUpdater.outdateCount++;
|
||||||
return ThreadUpdater.update();
|
return ThreadUpdater.update();
|
||||||
} else if (n <= -60) {
|
} else if (n <= -60) {
|
||||||
ThreadUpdater.set('status', 'Retrying', null);
|
ThreadUpdater.set('status', 'Retrying', '');
|
||||||
return ThreadUpdater.update();
|
return ThreadUpdater.update();
|
||||||
} else if (n > 0) {
|
} else if (n > 0) {
|
||||||
return ThreadUpdater.set('timer', n);
|
return ThreadUpdater.set('timer', n);
|
||||||
@ -13415,18 +13445,27 @@
|
|||||||
ThreadUpdater.thread.posts.forEach(function(post) {
|
ThreadUpdater.thread.posts.forEach(function(post) {
|
||||||
var ID;
|
var ID;
|
||||||
ID = +post.ID;
|
ID = +post.ID;
|
||||||
if (__indexOf.call(index, ID) < 0) {
|
if (!(post.info.date > Date.now() - 60 * $.SECOND)) {
|
||||||
post.kill();
|
if (__indexOf.call(index, ID) < 0) {
|
||||||
} else if (post.isDead) {
|
post.kill();
|
||||||
post.resurrect();
|
} else if (post.isDead) {
|
||||||
} else if (post.file && !post.file.isDead && __indexOf.call(files, ID) < 0) {
|
post.resurrect();
|
||||||
post.kill(true);
|
} else if (post.file && !(post.file.isDead || __indexOf.call(files, ID) >= 0)) {
|
||||||
|
post.kill(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ThreadUpdater.postID && ThreadUpdater.postID === ID) {
|
if (ThreadUpdater.postID && ThreadUpdater.postID === ID) {
|
||||||
return ThreadUpdater.foundPost = true;
|
return ThreadUpdater.foundPost = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
sendEvent = function() {
|
sendEvent = function() {
|
||||||
|
var ipCountEl;
|
||||||
|
if ((OP.unique_ips != null) && (ipCountEl = $.id('unique-ips'))) {
|
||||||
|
ipCountEl.textContent = OP.unique_ips;
|
||||||
|
ipCountEl.previousSibling.textContent = ipCountEl.previousSibling.textContent.replace(/\b(?:is|are)\b/, OP.unique_ips === 1 ? 'is' : 'are');
|
||||||
|
ipCountEl.nextSibling.textContent = ipCountEl.nextSibling.textContent.replace(/\bposters?\b/, OP.unique_ips === 1 ? 'poster' : 'posters');
|
||||||
|
}
|
||||||
|
ThreadUpdater.postIDs = index;
|
||||||
return $.event('ThreadUpdate', {
|
return $.event('ThreadUpdate', {
|
||||||
404: false,
|
404: false,
|
||||||
threadID: ThreadUpdater.thread.fullID,
|
threadID: ThreadUpdater.thread.fullID,
|
||||||
@ -13439,7 +13478,7 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
if (!count) {
|
if (!count) {
|
||||||
ThreadUpdater.set('status', null, null);
|
ThreadUpdater.set('status', '', '');
|
||||||
ThreadUpdater.outdateCount++;
|
ThreadUpdater.outdateCount++;
|
||||||
sendEvent();
|
sendEvent();
|
||||||
return;
|
return;
|
||||||
@ -13486,13 +13525,17 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.db = new DataBoard('watchedThreads', this.refresh, true);
|
this.db = new DataBoard('watchedThreads', this.refresh, true);
|
||||||
this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', "<div><span class=\"move\">Thread Watcher <span id=\"watcher-status\"></span></span><a class=\"menu-button\" href=\"javascript:;\"><i class=\"fa\">\uf107</i></a></div><div id=\"watched-threads\"></div>");
|
this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', {
|
||||||
|
innerHTML: "<div>\r<span class=\"move\">\rThread Watcher \r<a class=\"refresh fa\" title=\"Check threads\" href=\"javascript:;\">\\uf021</a>\r<span id=\"watcher-status\"></span>\r</span>\r<a class=\"menu-button\" href=\"javascript:;\"><i class=\"fa\">\\uf107</i></a>\r</div>\r<div id=\"watched-threads\"></div>"
|
||||||
|
});
|
||||||
this.status = $('#watcher-status', this.dialog);
|
this.status = $('#watcher-status', this.dialog);
|
||||||
this.list = this.dialog.lastElementChild;
|
this.list = this.dialog.lastElementChild;
|
||||||
|
this.refreshButton = $('.refresh', this.dialog);
|
||||||
$.on(d, 'QRPostSuccessful', this.cb.post);
|
$.on(d, 'QRPostSuccessful', this.cb.post);
|
||||||
if (g.VIEW === 'thread') {
|
if (g.VIEW === 'thread') {
|
||||||
$.on(d, 'ThreadUpdate', this.cb.threadUpdate);
|
$.on(d, 'ThreadUpdate', this.cb.threadUpdate);
|
||||||
}
|
}
|
||||||
|
$.on(this.refreshButton, 'click', this.fetchAllStatus);
|
||||||
$.on(d, '4chanXInitFinished', this.ready);
|
$.on(d, '4chanXInitFinished', this.ready);
|
||||||
switch (g.VIEW) {
|
switch (g.VIEW) {
|
||||||
case 'index':
|
case 'index':
|
||||||
@ -13507,18 +13550,75 @@
|
|||||||
ThreadWatcher.fetchAllStatus();
|
ThreadWatcher.fetchAllStatus();
|
||||||
this.db.save();
|
this.db.save();
|
||||||
}
|
}
|
||||||
return Thread.callbacks.push({
|
if (Conf['JSON Navigation'] && Conf['Menu'] && g.BOARD.ID !== 'f') {
|
||||||
|
Menu.menu.addEntry({
|
||||||
|
el: $.el('a', {
|
||||||
|
href: 'javascript:;'
|
||||||
|
}),
|
||||||
|
order: 6,
|
||||||
|
open: function(_arg) {
|
||||||
|
var thread;
|
||||||
|
thread = _arg.thread;
|
||||||
|
if (!(Conf['Index Mode'] === 'catalog' && g.VIEW === 'index')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.el.textContent = ThreadWatcher.isWatched(thread) ? 'Unwatch thread' : 'Watch thread';
|
||||||
|
if (this.cb) {
|
||||||
|
$.off(this.el, 'click', this.cb);
|
||||||
|
}
|
||||||
|
this.cb = function() {
|
||||||
|
$.event('CloseMenu');
|
||||||
|
return ThreadWatcher.toggle(thread);
|
||||||
|
};
|
||||||
|
$.on(this.el, 'click', this.cb);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Post.callbacks.push({
|
||||||
name: 'Thread Watcher',
|
name: 'Thread Watcher',
|
||||||
cb: this.node
|
cb: this.node
|
||||||
});
|
});
|
||||||
|
return CatalogThread.callbacks.push({
|
||||||
|
name: 'Thread Watcher',
|
||||||
|
cb: this.catalogNode
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isWatched: function(thread) {
|
||||||
|
var _ref;
|
||||||
|
return (_ref = ThreadWatcher.db) != null ? _ref.get({
|
||||||
|
boardID: thread.board.ID,
|
||||||
|
threadID: thread.ID
|
||||||
|
}) : void 0;
|
||||||
},
|
},
|
||||||
node: function() {
|
node: function() {
|
||||||
var toggler;
|
var toggler;
|
||||||
toggler = $.el('img', {
|
if (this.isReply) {
|
||||||
className: 'watch-thread-link'
|
return;
|
||||||
});
|
}
|
||||||
$.on(toggler, 'click', ThreadWatcher.cb.toggle);
|
if (this.isClone) {
|
||||||
return $.before($('input', this.OP.nodes.post), toggler);
|
toggler = $('.watch-thread-link', this.nodes.post);
|
||||||
|
} else {
|
||||||
|
toggler = $.el('img', {
|
||||||
|
className: 'watch-thread-link'
|
||||||
|
});
|
||||||
|
$.before($('input', this.nodes.post), toggler);
|
||||||
|
}
|
||||||
|
return $.on(toggler, 'click', ThreadWatcher.cb.toggle);
|
||||||
|
},
|
||||||
|
catalogNode: function() {
|
||||||
|
if (ThreadWatcher.isWatched(this.thread)) {
|
||||||
|
$.addClass(this.nodes.root, 'watched');
|
||||||
|
}
|
||||||
|
return $.on(this.nodes.thumb.parentNode, 'click', (function(_this) {
|
||||||
|
return function(e) {
|
||||||
|
if (!(e.button === 0 && e.altKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ThreadWatcher.toggle(_this.thread);
|
||||||
|
return e.preventDefault();
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
},
|
},
|
||||||
ready: function() {
|
ready: function() {
|
||||||
var el;
|
var el;
|
||||||
@ -13563,12 +13663,6 @@
|
|||||||
}
|
}
|
||||||
return $.event('CloseMenu');
|
return $.event('CloseMenu');
|
||||||
},
|
},
|
||||||
checkThreads: function() {
|
|
||||||
if ($.hasClass(this, 'disabled')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return ThreadWatcher.fetchAllStatus();
|
|
||||||
},
|
|
||||||
pruneDeads: function() {
|
pruneDeads: function() {
|
||||||
var boardID, data, threadID, _i, _len, _ref, _ref1;
|
var boardID, data, threadID, _i, _len, _ref, _ref1;
|
||||||
if ($.hasClass(this, 'disabled')) {
|
if ($.hasClass(this, 'disabled')) {
|
||||||
@ -13590,7 +13684,10 @@
|
|||||||
return $.event('CloseMenu');
|
return $.event('CloseMenu');
|
||||||
},
|
},
|
||||||
toggle: function() {
|
toggle: function() {
|
||||||
return ThreadWatcher.toggle(Get.threadFromNode(this));
|
ThreadWatcher.toggle(Get.threadFromNode(this));
|
||||||
|
Index.followedThreadID = thread.ID;
|
||||||
|
ThreadWatcher.toggle(thread);
|
||||||
|
return delete Index.followedThreadID;
|
||||||
},
|
},
|
||||||
rm: function() {
|
rm: function() {
|
||||||
var boardID, threadID, _ref;
|
var boardID, threadID, _ref;
|
||||||
@ -13609,9 +13706,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onIndexRefresh: function() {
|
onIndexRefresh: function() {
|
||||||
var boardID, data, threadID, _ref;
|
var boardID, data, db, threadID, _ref;
|
||||||
|
db = ThreadWatcher.db;
|
||||||
boardID = g.BOARD.ID;
|
boardID = g.BOARD.ID;
|
||||||
_ref = ThreadWatcher.db.data.boards[boardID];
|
db.forceSync();
|
||||||
|
_ref = db.data.boards[boardID];
|
||||||
for (threadID in _ref) {
|
for (threadID in _ref) {
|
||||||
data = _ref[threadID];
|
data = _ref[threadID];
|
||||||
if (!data.isDead && !(threadID in g.BOARD.threads)) {
|
if (!data.isDead && !(threadID in g.BOARD.threads)) {
|
||||||
@ -13650,10 +13749,12 @@
|
|||||||
},
|
},
|
||||||
fetchAllStatus: function() {
|
fetchAllStatus: function() {
|
||||||
var thread, threads, _i, _len;
|
var thread, threads, _i, _len;
|
||||||
|
ThreadWatcher.db.forceSync();
|
||||||
|
ThreadWatcher.unreaddb.forceSync();
|
||||||
|
QR.db.forceSync();
|
||||||
if (!(threads = ThreadWatcher.getAll()).length) {
|
if (!(threads = ThreadWatcher.getAll()).length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ThreadWatcher.status.textContent = '...';
|
|
||||||
for (_i = 0, _len = threads.length; _i < _len; _i++) {
|
for (_i = 0, _len = threads.length; _i < _len; _i++) {
|
||||||
thread = threads[_i];
|
thread = threads[_i];
|
||||||
ThreadWatcher.fetchStatus(thread);
|
ThreadWatcher.fetchStatus(thread);
|
||||||
@ -13662,43 +13763,103 @@
|
|||||||
fetchStatus: function(_arg) {
|
fetchStatus: function(_arg) {
|
||||||
var boardID, data, fetchCount, threadID;
|
var boardID, data, fetchCount, threadID;
|
||||||
boardID = _arg.boardID, threadID = _arg.threadID, data = _arg.data;
|
boardID = _arg.boardID, threadID = _arg.threadID, data = _arg.data;
|
||||||
if (data.isDead) {
|
if (data.isDead && !Conf['Show Unread Count']) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetchCount = ThreadWatcher.fetchCount;
|
fetchCount = ThreadWatcher.fetchCount;
|
||||||
|
if (fetchCount.fetching === 0) {
|
||||||
|
ThreadWatcher.status.textContent = '...';
|
||||||
|
$.addClass(ThreadWatcher.refreshButton, 'fa-spin');
|
||||||
|
}
|
||||||
fetchCount.fetching++;
|
fetchCount.fetching++;
|
||||||
return $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", {
|
return $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", {
|
||||||
onloadend: function() {
|
onloadend: function() {
|
||||||
var status;
|
var isDead, lastReadPost, match, postObj, quotingYou, regexp, status, unread, _i, _len, _ref, _ref1;
|
||||||
fetchCount.fetched++;
|
fetchCount.fetched++;
|
||||||
if (fetchCount.fetched === fetchCount.fetching) {
|
if (fetchCount.fetched === fetchCount.fetching) {
|
||||||
fetchCount.fetched = 0;
|
fetchCount.fetched = 0;
|
||||||
fetchCount.fetching = 0;
|
fetchCount.fetching = 0;
|
||||||
status = '';
|
status = '';
|
||||||
|
$.rmClass(ThreadWatcher.refreshButton, 'fa-spin');
|
||||||
} else {
|
} else {
|
||||||
status = "" + (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%";
|
status = "" + (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%";
|
||||||
}
|
}
|
||||||
ThreadWatcher.status.textContent = status;
|
ThreadWatcher.status.textContent = status;
|
||||||
if (this.status !== 404) {
|
if (this.status === 200 && this.response) {
|
||||||
return;
|
isDead = !!this.response.posts[0].archived;
|
||||||
}
|
if (isDead && Conf['Auto Prune']) {
|
||||||
if (Conf['Auto Prune']) {
|
ThreadWatcher.db["delete"]({
|
||||||
ThreadWatcher.db["delete"]({
|
boardID: boardID,
|
||||||
boardID: boardID,
|
threadID: threadID
|
||||||
threadID: threadID
|
});
|
||||||
});
|
ThreadWatcher.refresh();
|
||||||
} else {
|
return;
|
||||||
data.isDead = true;
|
}
|
||||||
ThreadWatcher.db.set({
|
lastReadPost = ThreadWatcher.unreaddb.get({
|
||||||
boardID: boardID,
|
boardID: boardID,
|
||||||
threadID: threadID,
|
threadID: threadID,
|
||||||
val: data
|
defaultValue: 0
|
||||||
});
|
});
|
||||||
|
unread = quotingYou = 0;
|
||||||
|
_ref = this.response.posts;
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
postObj = _ref[_i];
|
||||||
|
if (!(postObj.no > lastReadPost)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((_ref1 = QR.db) != null ? _ref1.get({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID,
|
||||||
|
postID: postObj.no
|
||||||
|
}) : void 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unread++;
|
||||||
|
if (!(QR.db && postObj.com)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
regexp = /<a [^>]*\bhref="(?:\/([^\/]+)\/thread\/(\d+))?(?:#p(\d+))?"/g;
|
||||||
|
while (match = regexp.exec(postObj.com)) {
|
||||||
|
if (QR.db.get({
|
||||||
|
boardID: match[1] || boardID,
|
||||||
|
threadID: match[2] || threadID,
|
||||||
|
postID: match[3] || match[2] || threadID
|
||||||
|
})) {
|
||||||
|
quotingYou++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isDead !== data.isDead || unread !== data.unread || quotingYou !== data.quotingYou) {
|
||||||
|
data.isDead = isDead;
|
||||||
|
data.unread = unread;
|
||||||
|
data.quotingYou = quotingYou;
|
||||||
|
ThreadWatcher.db.set({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID,
|
||||||
|
val: data
|
||||||
|
});
|
||||||
|
return ThreadWatcher.refresh();
|
||||||
|
}
|
||||||
|
} else if (this.status === 404) {
|
||||||
|
if (Conf['Auto Prune']) {
|
||||||
|
ThreadWatcher.db["delete"]({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data.isDead = true;
|
||||||
|
delete data.unread;
|
||||||
|
delete data.quotingYou;
|
||||||
|
ThreadWatcher.db.set({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID,
|
||||||
|
val: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return ThreadWatcher.refresh();
|
||||||
}
|
}
|
||||||
return ThreadWatcher.refresh();
|
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
type: 'head'
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getAll: function() {
|
getAll: function() {
|
||||||
@ -13722,24 +13883,31 @@
|
|||||||
return all;
|
return all;
|
||||||
},
|
},
|
||||||
makeLine: function(boardID, threadID, data) {
|
makeLine: function(boardID, threadID, data) {
|
||||||
var div, fullID, href, link, x;
|
var count, div, fullID, link, title, x;
|
||||||
x = $.el('a', {
|
x = $.el('a', {
|
||||||
className: 'fa',
|
className: 'fa',
|
||||||
href: 'javascript:;',
|
href: 'javascript:;',
|
||||||
textContent: '\uf00d'
|
textContent: '\uf00d'
|
||||||
});
|
});
|
||||||
$.on(x, 'click', ThreadWatcher.cb.rm);
|
$.on(x, 'click', ThreadWatcher.cb.rm);
|
||||||
if (data.isDead) {
|
|
||||||
href = Redirect.to('thread', {
|
|
||||||
boardID: boardID,
|
|
||||||
threadID: threadID
|
|
||||||
});
|
|
||||||
}
|
|
||||||
link = $.el('a', {
|
link = $.el('a', {
|
||||||
href: href || ("/" + boardID + "/thread/" + threadID),
|
href: "/" + boardID + "/thread/" + threadID,
|
||||||
textContent: data.excerpt,
|
textContent: data.excerpt,
|
||||||
title: data.excerpt
|
title: data.excerpt,
|
||||||
|
className: 'watcher-link'
|
||||||
});
|
});
|
||||||
|
if (Conf['Show Unread Count'] && (data.unread != null)) {
|
||||||
|
count = $.el('span', {
|
||||||
|
textContent: "(" + data.unread + ")",
|
||||||
|
className: 'watcher-unread'
|
||||||
|
});
|
||||||
|
$.add(link, count);
|
||||||
|
}
|
||||||
|
title = $.el('span', {
|
||||||
|
textContent: data.excerpt,
|
||||||
|
className: 'watcher-title'
|
||||||
|
});
|
||||||
|
$.add(link, title);
|
||||||
div = $.el('div');
|
div = $.el('div');
|
||||||
fullID = "" + boardID + "." + threadID;
|
fullID = "" + boardID + "." + threadID;
|
||||||
div.dataset.fullID = fullID;
|
div.dataset.fullID = fullID;
|
||||||
@ -13749,11 +13917,19 @@
|
|||||||
if (data.isDead) {
|
if (data.isDead) {
|
||||||
$.addClass(div, 'dead-thread');
|
$.addClass(div, 'dead-thread');
|
||||||
}
|
}
|
||||||
|
if (Conf['Show Unread Count']) {
|
||||||
|
if (data.unread) {
|
||||||
|
$.addClass(div, 'replies-unread');
|
||||||
|
}
|
||||||
|
if (data.quotingYou) {
|
||||||
|
$.addClass(div, 'replies-quoting-you');
|
||||||
|
}
|
||||||
|
}
|
||||||
$.add(div, [x, $.tn(' '), link]);
|
$.add(div, [x, $.tn(' '), link]);
|
||||||
return div;
|
return div;
|
||||||
},
|
},
|
||||||
refresh: function() {
|
refresh: function() {
|
||||||
var boardID, data, helper, list, nodes, refresher, thread, threadID, threads, toggler, watched, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3;
|
var boardID, data, list, nodes, refresher, threadID, _i, _j, _len, _len1, _ref, _ref1, _ref2;
|
||||||
nodes = [];
|
nodes = [];
|
||||||
_ref = ThreadWatcher.getAll();
|
_ref = ThreadWatcher.getAll();
|
||||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
@ -13763,24 +13939,76 @@
|
|||||||
list = ThreadWatcher.list;
|
list = ThreadWatcher.list;
|
||||||
$.rmAll(list);
|
$.rmAll(list);
|
||||||
$.add(list, nodes);
|
$.add(list, nodes);
|
||||||
threads = g.BOARD.threads;
|
g.threads.forEach(function(thread) {
|
||||||
_ref2 = threads.keys;
|
var helper, post, toggler, _j, _len1, _ref2;
|
||||||
|
helper = ThreadWatcher.isWatched(thread) ? ['addClass', 'Unwatch'] : ['rmClass', 'Watch'];
|
||||||
|
if (thread.OP) {
|
||||||
|
_ref2 = [thread.OP].concat(__slice.call(thread.OP.clones));
|
||||||
|
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
||||||
|
post = _ref2[_j];
|
||||||
|
toggler = $('.watch-thread-link', post.nodes.post);
|
||||||
|
$[helper[0]](toggler, 'watched');
|
||||||
|
toggler.title = "" + helper[1] + " Thread";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (thread.catalogView) {
|
||||||
|
return $[helper[0]](thread.catalogView.nodes.root, 'watched');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_ref2 = ThreadWatcher.menu.refreshers;
|
||||||
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
||||||
threadID = _ref2[_j];
|
refresher = _ref2[_j];
|
||||||
thread = threads[threadID];
|
refresher();
|
||||||
toggler = $('.watch-thread-link', thread.OP.nodes.post);
|
}
|
||||||
watched = ThreadWatcher.db.get({
|
if (Index.nodes && Conf['Pin Watched Threads']) {
|
||||||
boardID: thread.board.ID,
|
Index.sort();
|
||||||
|
return Index.buildIndex();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
update: function(boardID, threadID, newData) {
|
||||||
|
var data, key, line, n, newLine, val, _ref;
|
||||||
|
if (!(data = (_ref = ThreadWatcher.db) != null ? _ref.get({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID
|
||||||
|
}) : void 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newData.isDead && Conf['Auto Prune']) {
|
||||||
|
ThreadWatcher.db["delete"]({
|
||||||
|
boardID: boardID,
|
||||||
threadID: threadID
|
threadID: threadID
|
||||||
});
|
});
|
||||||
helper = watched ? ['addClass', 'Unwatch'] : ['rmClass', 'Watch'];
|
ThreadWatcher.refresh();
|
||||||
$[helper[0]](toggler, 'watched');
|
return;
|
||||||
toggler.title = "" + helper[1] + " Thread";
|
|
||||||
}
|
}
|
||||||
_ref3 = ThreadWatcher.menu.refreshers;
|
n = 0;
|
||||||
for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) {
|
for (key in newData) {
|
||||||
refresher = _ref3[_k];
|
val = newData[key];
|
||||||
refresher();
|
if (data[key] !== val) {
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!n) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ThreadWatcher.db.forceSync();
|
||||||
|
if (!(data = ThreadWatcher.db.get({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID
|
||||||
|
}))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$.extend(data, newData);
|
||||||
|
ThreadWatcher.db.set({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID,
|
||||||
|
val: data
|
||||||
|
});
|
||||||
|
if (line = $("#watched-threads > [data-full-i-d='" + boardID + "." + threadID + "']", ThreadWatcher.dialog)) {
|
||||||
|
newLine = ThreadWatcher.makeLine(boardID, threadID, data);
|
||||||
|
return $.replace(line, newLine);
|
||||||
|
} else {
|
||||||
|
return ThreadWatcher.refresh();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggle: function(thread) {
|
toggle: function(thread) {
|
||||||
@ -13817,7 +14045,14 @@
|
|||||||
threadID: threadID,
|
threadID: threadID,
|
||||||
val: data
|
val: data
|
||||||
});
|
});
|
||||||
return ThreadWatcher.refresh();
|
ThreadWatcher.refresh();
|
||||||
|
if (Conf['Show Unread Count']) {
|
||||||
|
return ThreadWatcher.fetchStatus({
|
||||||
|
boardID: boardID,
|
||||||
|
threadID: threadID,
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
rm: function(boardID, threadID) {
|
rm: function(boardID, threadID) {
|
||||||
ThreadWatcher.db["delete"]({
|
ThreadWatcher.db["delete"]({
|
||||||
@ -13847,12 +14082,12 @@
|
|||||||
if (!Conf['Thread Watcher']) {
|
if (!Conf['Thread Watcher']) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
menu = new UI.Menu();
|
menu = this.menu = new UI.Menu('thread watcher');
|
||||||
$.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) {
|
$.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) {
|
||||||
return menu.toggle(e, this, ThreadWatcher);
|
return menu.toggle(e, this, ThreadWatcher);
|
||||||
});
|
});
|
||||||
this.addHeaderMenuEntry();
|
this.addHeaderMenuEntry();
|
||||||
return this.addMenuEntries(menu);
|
return this.addMenuEntries;
|
||||||
},
|
},
|
||||||
addHeaderMenuEntry: function() {
|
addHeaderMenuEntry: function() {
|
||||||
var entryEl;
|
var entryEl;
|
||||||
@ -13877,7 +14112,7 @@
|
|||||||
return entryEl.textContent = text;
|
return entryEl.textContent = text;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addMenuEntries: function(menu) {
|
addMenuEntries: function() {
|
||||||
var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1;
|
var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1;
|
||||||
entries = [];
|
entries = [];
|
||||||
entries.push({
|
entries.push({
|
||||||
@ -13891,22 +14126,11 @@
|
|||||||
return (ThreadWatcher.list.firstElementChild ? $.rmClass : $.addClass)(this.el, 'disabled');
|
return (ThreadWatcher.list.firstElementChild ? $.rmClass : $.addClass)(this.el, 'disabled');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
entries.push({
|
|
||||||
cb: ThreadWatcher.cb.checkThreads,
|
|
||||||
entry: {
|
|
||||||
el: $.el('a', {
|
|
||||||
textContent: 'Check 404\'d threads'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
refresh: function() {
|
|
||||||
return ($('div:not(.dead-thread)', ThreadWatcher.list) ? $.rmClass : $.addClass)(this.el, 'disabled');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
entries.push({
|
entries.push({
|
||||||
cb: ThreadWatcher.cb.pruneDeads,
|
cb: ThreadWatcher.cb.pruneDeads,
|
||||||
entry: {
|
entry: {
|
||||||
el: $.el('a', {
|
el: $.el('a', {
|
||||||
textContent: 'Prune 404\'d threads'
|
textContent: 'Prune dead threads'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
refresh: function() {
|
refresh: function() {
|
||||||
@ -13938,22 +14162,18 @@
|
|||||||
if (refresh) {
|
if (refresh) {
|
||||||
this.refreshers.push(refresh.bind(entry));
|
this.refreshers.push(refresh.bind(entry));
|
||||||
}
|
}
|
||||||
menu.addEntry(entry);
|
this.menu.addEntry(entry);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createSubEntry: function(name, desc) {
|
createSubEntry: function(name, desc) {
|
||||||
var entry, input;
|
var entry, input;
|
||||||
entry = {
|
entry = {
|
||||||
type: 'thread watcher',
|
type: 'thread watcher',
|
||||||
el: $.el('label', {
|
el: UI.checkbox(name, " " + name)
|
||||||
innerHTML: "<input type=checkbox name='" + name + "'> " + name,
|
|
||||||
title: desc
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
input = entry.el.firstElementChild;
|
input = entry.el.firstElementChild;
|
||||||
input.checked = Conf[name];
|
|
||||||
$.on(input, 'change', $.cb.checked);
|
$.on(input, 'change', $.cb.checked);
|
||||||
if (name === 'Current Board') {
|
if (name === 'Current Board' || name === 'Show Unread Count') {
|
||||||
$.on(input, 'change', ThreadWatcher.refresh);
|
$.on(input, 'change', ThreadWatcher.refresh);
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
@ -15778,10 +15998,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getThread: function() {
|
getThread: function() {
|
||||||
var threadRoot, _i, _len, _ref;
|
var thread, threadRoot, _i, _len, _ref;
|
||||||
_ref = $$('.thread');
|
_ref = $$('.thread');
|
||||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
threadRoot = _ref[_i];
|
threadRoot = _ref[_i];
|
||||||
|
thread = Get.threadFromRoot(threadRoot);
|
||||||
|
if (thread.isHidden && !thread.stub) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height) {
|
if (Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height) {
|
||||||
return threadRoot;
|
return threadRoot;
|
||||||
}
|
}
|
||||||
@ -15789,7 +16013,10 @@
|
|||||||
return $('.board');
|
return $('.board');
|
||||||
},
|
},
|
||||||
scroll: function(delta) {
|
scroll: function(delta) {
|
||||||
var axis, next, thread, top;
|
var axis, extra, next, thread, top, _ref;
|
||||||
|
if ((_ref = d.activeElement) != null) {
|
||||||
|
_ref.blur();
|
||||||
|
}
|
||||||
thread = Nav.getThread();
|
thread = Nav.getThread();
|
||||||
axis = delta === +1 ? 'following' : 'preceding';
|
axis = delta === +1 ? 'following' : 'preceding';
|
||||||
if (next = $.x("" + axis + "-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread)) {
|
if (next = $.x("" + axis + "-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread)) {
|
||||||
@ -15798,48 +16025,64 @@
|
|||||||
thread = next;
|
thread = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Header.scrollTo(thread);
|
extra = Header.getTopOf(thread) + doc.clientHeight - d.body.getBoundingClientRect().bottom;
|
||||||
|
if (extra > 0) {
|
||||||
|
d.body.style.marginBottom = "" + extra + "px";
|
||||||
|
}
|
||||||
|
Header.scrollTo(thread);
|
||||||
|
if (extra > 0 && !Nav.haveExtra) {
|
||||||
|
Nav.haveExtra = true;
|
||||||
|
return $.on(d, 'scroll', Nav.removeExtra);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeExtra: function() {
|
||||||
|
var extra;
|
||||||
|
extra = doc.clientHeight - d.body.getBoundingClientRect().bottom;
|
||||||
|
if (extra > 0) {
|
||||||
|
return d.body.style.marginBottom = "" + extra + "px";
|
||||||
|
} else {
|
||||||
|
d.body.style.marginBottom = null;
|
||||||
|
delete Nav.haveExtra;
|
||||||
|
return $.off(d, 'scroll', Nav.removeExtra);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
RelativeDates = {
|
RelativeDates = {
|
||||||
INTERVAL: $.MINUTE / 2,
|
INTERVAL: $.MINUTE / 2,
|
||||||
init: function() {
|
init: function() {
|
||||||
switch (g.VIEW) {
|
var _ref;
|
||||||
case 'index':
|
if (((_ref = g.VIEW) === 'index' || _ref === 'thread') && Conf['Relative Post Dates'] && !Conf['Relative Date Title'] || g.VIEW === 'index' && Conf['JSON Navigation'] && g.BOARD.ID !== 'f') {
|
||||||
this.flush();
|
this.flush();
|
||||||
$.on(d, 'visibilitychange', this.flush);
|
$.on(d, 'visibilitychange ThreadUpdate', this.flush);
|
||||||
if (!Conf['Relative Post Dates']) {
|
}
|
||||||
return;
|
if (Conf['Relative Post Dates']) {
|
||||||
}
|
return Post.callbacks.push({
|
||||||
break;
|
name: 'Relative Post Dates',
|
||||||
case 'thread':
|
cb: this.node
|
||||||
if (!Conf['Relative Post Dates']) {
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.flush();
|
|
||||||
$.on(d, 'visibilitychange ThreadUpdate', this.flush);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
return Post.callbacks.push({
|
|
||||||
name: 'Relative Post Dates',
|
|
||||||
cb: this.node
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
node: function() {
|
node: function() {
|
||||||
var dateEl;
|
var dateEl;
|
||||||
|
dateEl = this.nodes.date;
|
||||||
|
if (Conf['Relative Date Title']) {
|
||||||
|
$.on(dateEl, 'mouseover', (function(_this) {
|
||||||
|
return function() {
|
||||||
|
return RelativeDates.hover(_this);
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.isClone) {
|
if (this.isClone) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dateEl = this.nodes.date;
|
|
||||||
dateEl.title = dateEl.textContent;
|
dateEl.title = dateEl.textContent;
|
||||||
return RelativeDates.update(this);
|
return RelativeDates.update(this);
|
||||||
},
|
},
|
||||||
relative: function(diff, now, date) {
|
relative: function(diff, now, date) {
|
||||||
var days, months, number, rounded, unit, years;
|
var days, months, number, rounded, unit, years;
|
||||||
unit = (number = diff / $.DAY) >= 1 ? (years = now.getYear() - date.getYear(), months = now.getMonth() - date.getMonth(), days = now.getDate() - date.getDate(), years > 1 ? (number = years - (months < 0 || months === 0 && days < 0), 'year') : years === 1 && (months > 0 || months === 0 && days >= 0) ? (number = years, 'year') : (months = (months + 12) % 12) > 1 ? (number = months - (days < 0), 'month') : months === 1 && days >= 0 ? (number = months, 'month') : 'day') : (number = diff / $.HOUR) >= 1 ? 'hour' : (number = diff / $.MINUTE) >= 1 ? 'minute' : (number = Math.max(0, diff) / $.SECOND, 'second');
|
unit = (number = diff / $.DAY) >= 1 ? (years = now.getYear() - date.getYear(), months = now.getMonth() - date.getMonth(), days = now.getDate() - date.getDate(), years > 1 ? (number = years - (months < 0 || months === 0 && days < 0), 'year') : years === 1 && (months > 0 || months === 0 && days >= 0) ? (number = years, 'year') : (months = months + 12 * years) > 1 ? (number = months - (days < 0), 'month') : months === 1 && days >= 0 ? (number = months, 'month') : 'day') : (number = diff / $.HOUR) >= 1 ? 'hour' : (number = diff / $.MINUTE) >= 1 ? 'minute' : (number = Math.max(0, diff) / $.SECOND, 'second');
|
||||||
rounded = Math.round(number);
|
rounded = Math.round(number);
|
||||||
if (rounded !== 1) {
|
if (rounded !== 1) {
|
||||||
unit += 's';
|
unit += 's';
|
||||||
@ -15862,6 +16105,13 @@
|
|||||||
clearTimeout(RelativeDates.timeout);
|
clearTimeout(RelativeDates.timeout);
|
||||||
return RelativeDates.timeout = setTimeout(RelativeDates.flush, RelativeDates.INTERVAL);
|
return RelativeDates.timeout = setTimeout(RelativeDates.flush, RelativeDates.INTERVAL);
|
||||||
},
|
},
|
||||||
|
hover: function(post) {
|
||||||
|
var date, diff, now;
|
||||||
|
date = post.info.date;
|
||||||
|
now = new Date();
|
||||||
|
diff = now - date;
|
||||||
|
return post.nodes.date.title = RelativeDates.relative(diff, now, date);
|
||||||
|
},
|
||||||
update: function(data, now) {
|
update: function(data, now) {
|
||||||
var date, diff, isPost, relative, singlePost, _i, _len, _ref;
|
var date, diff, isPost, relative, singlePost, _i, _len, _ref;
|
||||||
isPost = data instanceof Post;
|
isPost = data instanceof Post;
|
||||||
@ -15901,44 +16151,45 @@
|
|||||||
if (Conf['Reveal Spoilers']) {
|
if (Conf['Reveal Spoilers']) {
|
||||||
$.addClass(doc, 'reveal-spoilers');
|
$.addClass(doc, 'reveal-spoilers');
|
||||||
}
|
}
|
||||||
if (Conf['Remove Spoilers']) {
|
if (!Conf['Remove Spoilers']) {
|
||||||
return $.addClass(doc, 'remove-spoilers');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Report = {
|
|
||||||
init: function() {
|
|
||||||
if (!/report/.test(location.search)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return $.asap((function() {
|
$.addClass(doc, 'remove-spoilers');
|
||||||
return $.id('recaptcha_response_field');
|
Post.callbacks.push({
|
||||||
}), Report.ready);
|
name: 'Reveal Spoilers',
|
||||||
|
cb: this.node
|
||||||
|
});
|
||||||
|
CatalogThread.callbacks.push({
|
||||||
|
name: 'Reveal Spoilers',
|
||||||
|
cb: this.node
|
||||||
|
});
|
||||||
|
if (g.VIEW === 'archive') {
|
||||||
|
return $.ready(function() {
|
||||||
|
return RemoveSpoilers.unspoiler($.id('arc-list'));
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ready: function() {
|
node: function(post) {
|
||||||
var field;
|
return RemoveSpoilers.unspoiler(this.nodes.comment);
|
||||||
field = $.id('recaptcha_response_field');
|
},
|
||||||
$.on(field, 'keydown', function(e) {
|
unspoiler: function(el) {
|
||||||
if (e.keyCode === 8 && !field.value) {
|
var span, spoiler, spoilers, _i, _len;
|
||||||
return $.globalEval('Recaptcha.reload("t")');
|
spoilers = $$('s', el);
|
||||||
}
|
for (_i = 0, _len = spoilers.length; _i < _len; _i++) {
|
||||||
});
|
spoiler = spoilers[_i];
|
||||||
return $.on($('form'), 'submit', function(e) {
|
span = $.el('span', {
|
||||||
var response;
|
className: 'removed-spoiler'
|
||||||
e.preventDefault();
|
});
|
||||||
response = field.value.trim();
|
$.replace(spoiler, span);
|
||||||
if (!/\s|^\d+$/.test(response)) {
|
$.add(span, __slice.call(spoiler.childNodes));
|
||||||
field.value = "" + response + " " + response;
|
}
|
||||||
}
|
|
||||||
return this.submit();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Time = {
|
Time = {
|
||||||
init: function() {
|
init: function() {
|
||||||
if (!Conf['Time Formatting']) {
|
var _ref;
|
||||||
|
if (!(((_ref = g.VIEW) === 'index' || _ref === 'thread') && Conf['Time Formatting'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return Post.callbacks.push({
|
return Post.callbacks.push({
|
||||||
@ -15953,7 +16204,7 @@
|
|||||||
return this.nodes.date.textContent = Time.format(Conf['time'], this.info.date);
|
return this.nodes.date.textContent = Time.format(Conf['time'], this.info.date);
|
||||||
},
|
},
|
||||||
format: function(formatString, date) {
|
format: function(formatString, date) {
|
||||||
return formatString.replace(/%([A-Za-z])/g, function(s, c) {
|
return formatString.replace(/%(.)/g, function(s, c) {
|
||||||
if (c in Time.formatters) {
|
if (c in Time.formatters) {
|
||||||
return Time.formatters[c].call(date);
|
return Time.formatters[c].call(date);
|
||||||
} else {
|
} else {
|
||||||
@ -16029,6 +16280,9 @@
|
|||||||
},
|
},
|
||||||
Y: function() {
|
Y: function() {
|
||||||
return this.getFullYear();
|
return this.getFullYear();
|
||||||
|
},
|
||||||
|
'%': function() {
|
||||||
|
return '%';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
<div>
|
<div>
|
||||||
<span class="move">Thread Watcher <span id="watcher-status"></span></span>
|
<span class="move">
|
||||||
|
Thread Watcher
|
||||||
|
<a class="refresh fa" title="Check threads" href="javascript:;">\uf021</a>
|
||||||
|
<span id="watcher-status"></span>
|
||||||
|
</span>
|
||||||
<a class="menu-button" href="javascript:;"><i class="fa">\uf107</i></a>
|
<a class="menu-button" href="javascript:;"><i class="fa">\uf107</i></a>
|
||||||
</div>
|
</div>
|
||||||
<div id="watched-threads"></div>
|
<div id="watched-threads"></div>
|
||||||
@ -3,16 +3,17 @@ class Thread
|
|||||||
toString: -> @ID
|
toString: -> @ID
|
||||||
|
|
||||||
constructor: (@ID, @board) ->
|
constructor: (@ID, @board) ->
|
||||||
@fullID = "#{@board}.#{@ID}"
|
@fullID = "#{@board}.#{@ID}"
|
||||||
@posts = new SimpleDict
|
@posts = new SimpleDict
|
||||||
@isDead = false
|
@isDead = false
|
||||||
@isHidden = false
|
@isHidden = false
|
||||||
@isOnTop = false
|
@isOnTop = false
|
||||||
@isPinned = false
|
@isPinned = false
|
||||||
@isSticky = false
|
@isSticky = false
|
||||||
@isClosed = false
|
@isClosed = false
|
||||||
@postLimit = false
|
@isArchived = false
|
||||||
@fileLimit = false
|
@postLimit = false
|
||||||
|
@fileLimit = false
|
||||||
@ipCount = undefined
|
@ipCount = undefined
|
||||||
|
|
||||||
@OP = null
|
@OP = null
|
||||||
|
|||||||
@ -33,11 +33,14 @@ Nav =
|
|||||||
|
|
||||||
getThread: ->
|
getThread: ->
|
||||||
for threadRoot in $$ '.thread'
|
for threadRoot in $$ '.thread'
|
||||||
|
thread = Get.threadFromRoot threadRoot
|
||||||
|
continue if thread.isHidden and !thread.stub
|
||||||
if Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height # not scrolled past
|
if Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height # not scrolled past
|
||||||
return threadRoot
|
return threadRoot
|
||||||
return $ '.board'
|
return $ '.board'
|
||||||
|
|
||||||
scroll: (delta) ->
|
scroll: (delta) ->
|
||||||
|
d.activeElement?.blur()
|
||||||
thread = Nav.getThread()
|
thread = Nav.getThread()
|
||||||
axis = if delta is +1
|
axis = if delta is +1
|
||||||
'following'
|
'following'
|
||||||
@ -49,4 +52,21 @@ Nav =
|
|||||||
# or we're above the first thread and don't want to skip it.
|
# or we're above the first thread and don't want to skip it.
|
||||||
top = Header.getTopOf thread
|
top = Header.getTopOf thread
|
||||||
thread = next if delta is +1 and top < 5 or delta is -1 and top > -5
|
thread = next if delta is +1 and top < 5 or delta is -1 and top > -5
|
||||||
|
# Add extra space to the end of the page if necessary so that all threads can be selected by keybinds.
|
||||||
|
extra = Header.getTopOf(thread) + doc.clientHeight - d.body.getBoundingClientRect().bottom
|
||||||
|
d.body.style.marginBottom = "#{extra}px" if extra > 0
|
||||||
|
|
||||||
Header.scrollTo thread
|
Header.scrollTo thread
|
||||||
|
|
||||||
|
if extra > 0 and !Nav.haveExtra
|
||||||
|
Nav.haveExtra = true
|
||||||
|
$.on d, 'scroll', Nav.removeExtra
|
||||||
|
|
||||||
|
removeExtra: ->
|
||||||
|
extra = doc.clientHeight - d.body.getBoundingClientRect().bottom
|
||||||
|
if extra > 0
|
||||||
|
d.body.style.marginBottom = "#{extra}px"
|
||||||
|
else
|
||||||
|
d.body.style.marginBottom = null
|
||||||
|
delete Nav.haveExtra
|
||||||
|
$.off d, 'scroll', Nav.removeExtra
|
||||||
|
|||||||
@ -1,28 +1,28 @@
|
|||||||
RelativeDates =
|
RelativeDates =
|
||||||
INTERVAL: $.MINUTE / 2
|
INTERVAL: $.MINUTE / 2
|
||||||
init: ->
|
init: ->
|
||||||
switch g.VIEW
|
if (
|
||||||
when 'index'
|
g.VIEW in ['index', 'thread'] and Conf['Relative Post Dates'] and !Conf['Relative Date Title'] or
|
||||||
@flush()
|
g.VIEW is 'index' and Conf['JSON Navigation'] and g.BOARD.ID isnt 'f'
|
||||||
$.on d, 'visibilitychange', @flush
|
)
|
||||||
return unless Conf['Relative Post Dates']
|
@flush()
|
||||||
when 'thread'
|
$.on d, 'visibilitychange ThreadUpdate', @flush
|
||||||
return unless Conf['Relative Post Dates']
|
|
||||||
@flush()
|
if Conf['Relative Post Dates']
|
||||||
$.on d, 'visibilitychange ThreadUpdate', @flush
|
Post.callbacks.push
|
||||||
else
|
name: 'Relative Post Dates'
|
||||||
return
|
cb: @node
|
||||||
|
|
||||||
Post.callbacks.push
|
|
||||||
name: 'Relative Post Dates'
|
|
||||||
cb: @node
|
|
||||||
node: ->
|
node: ->
|
||||||
|
dateEl = @nodes.date
|
||||||
|
if Conf['Relative Date Title']
|
||||||
|
$.on dateEl, 'mouseover', => RelativeDates.hover @
|
||||||
|
return
|
||||||
return if @isClone
|
return if @isClone
|
||||||
|
|
||||||
# Show original absolute time as tooltip so users can still know exact times
|
# Show original absolute time as tooltip so users can still know exact times
|
||||||
# Since "Time Formatting" runs its `node` before us, the title tooltip will
|
# Since "Time Formatting" runs its `node` before us, the title tooltip will
|
||||||
# pick up the user-formatted time instead of 4chan time when enabled.
|
# pick up the user-formatted time instead of 4chan time when enabled.
|
||||||
dateEl = @nodes.date
|
|
||||||
dateEl.title = dateEl.textContent
|
dateEl.title = dateEl.textContent
|
||||||
|
|
||||||
RelativeDates.update @
|
RelativeDates.update @
|
||||||
@ -39,7 +39,7 @@ RelativeDates =
|
|||||||
else if years is 1 and (months > 0 or months is 0 and days >= 0)
|
else if years is 1 and (months > 0 or months is 0 and days >= 0)
|
||||||
number = years
|
number = years
|
||||||
'year'
|
'year'
|
||||||
else if (months = (months+12)%12 ) > 1
|
else if (months = months + 12*years) > 1
|
||||||
number = months - (days < 0)
|
number = months - (days < 0)
|
||||||
'month'
|
'month'
|
||||||
else if months is 1 and days >= 0
|
else if months is 1 and days >= 0
|
||||||
@ -82,6 +82,12 @@ RelativeDates =
|
|||||||
clearTimeout RelativeDates.timeout
|
clearTimeout RelativeDates.timeout
|
||||||
RelativeDates.timeout = setTimeout RelativeDates.flush, RelativeDates.INTERVAL
|
RelativeDates.timeout = setTimeout RelativeDates.flush, RelativeDates.INTERVAL
|
||||||
|
|
||||||
|
hover: (post) ->
|
||||||
|
date = post.info.date
|
||||||
|
now = new Date()
|
||||||
|
diff = now - date
|
||||||
|
post.nodes.date.title = RelativeDates.relative diff, now, date
|
||||||
|
|
||||||
# `update()`, when called from `flush()`, updates the elements,
|
# `update()`, when called from `flush()`, updates the elements,
|
||||||
# and re-calls `setOwnTimeout()` to re-add `data` to the stale list later.
|
# and re-calls `setOwnTimeout()` to re-add `data` to the stale list later.
|
||||||
update: (data, now) ->
|
update: (data, now) ->
|
||||||
|
|||||||
@ -3,5 +3,28 @@ RemoveSpoilers =
|
|||||||
if Conf['Reveal Spoilers']
|
if Conf['Reveal Spoilers']
|
||||||
$.addClass doc, 'reveal-spoilers'
|
$.addClass doc, 'reveal-spoilers'
|
||||||
|
|
||||||
if Conf['Remove Spoilers']
|
return unless Conf['Remove Spoilers']
|
||||||
$.addClass doc, 'remove-spoilers'
|
|
||||||
|
$.addClass doc, 'remove-spoilers'
|
||||||
|
|
||||||
|
Post.callbacks.push
|
||||||
|
name: 'Reveal Spoilers'
|
||||||
|
cb: @node
|
||||||
|
|
||||||
|
CatalogThread.callbacks.push
|
||||||
|
name: 'Reveal Spoilers'
|
||||||
|
cb: @node
|
||||||
|
|
||||||
|
if g.VIEW is 'archive'
|
||||||
|
$.ready -> RemoveSpoilers.unspoiler $.id 'arc-list'
|
||||||
|
|
||||||
|
node: (post) ->
|
||||||
|
RemoveSpoilers.unspoiler @nodes.comment
|
||||||
|
|
||||||
|
unspoiler: (el) ->
|
||||||
|
spoilers = $$ 's', el
|
||||||
|
for spoiler in spoilers
|
||||||
|
span = $.el 'span', className: 'removed-spoiler'
|
||||||
|
$.replace spoiler, span
|
||||||
|
$.add span, [spoiler.childNodes...]
|
||||||
|
return
|
||||||
@ -1,13 +0,0 @@
|
|||||||
Report =
|
|
||||||
init: ->
|
|
||||||
return unless /report/.test(location.search)
|
|
||||||
$.asap (-> $.id 'recaptcha_response_field'), Report.ready
|
|
||||||
ready: ->
|
|
||||||
field = $.id 'recaptcha_response_field'
|
|
||||||
$.on field, 'keydown', (e) ->
|
|
||||||
$.globalEval 'Recaptcha.reload("t")' if e.keyCode is 8 and not field.value
|
|
||||||
$.on $('form'), 'submit', (e) ->
|
|
||||||
e.preventDefault()
|
|
||||||
response = field.value.trim()
|
|
||||||
field.value = "#{response} #{response}" unless /\s|^\d+$/.test response
|
|
||||||
@submit()
|
|
||||||
@ -1,19 +1,21 @@
|
|||||||
Time =
|
Time =
|
||||||
init: ->
|
init: ->
|
||||||
return if !Conf['Time Formatting']
|
return unless g.VIEW in ['index', 'thread'] and Conf['Time Formatting']
|
||||||
|
|
||||||
Post.callbacks.push
|
Post.callbacks.push
|
||||||
name: 'Time Formatting'
|
name: 'Time Formatting'
|
||||||
cb: @node
|
cb: @node
|
||||||
|
|
||||||
node: ->
|
node: ->
|
||||||
return if @isClone
|
return if @isClone
|
||||||
@nodes.date.textContent = Time.format Conf['time'], @info.date
|
@nodes.date.textContent = Time.format Conf['time'], @info.date
|
||||||
format: (formatString, date) ->
|
format: (formatString, date) ->
|
||||||
formatString.replace /%([A-Za-z])/g, (s, c) ->
|
formatString.replace /%(.)/g, (s, c) ->
|
||||||
if c of Time.formatters
|
if c of Time.formatters
|
||||||
Time.formatters[c].call(date)
|
Time.formatters[c].call(date)
|
||||||
else
|
else
|
||||||
s
|
s
|
||||||
|
|
||||||
day: [
|
day: [
|
||||||
'Sunday'
|
'Sunday'
|
||||||
'Monday'
|
'Monday'
|
||||||
@ -23,6 +25,7 @@ Time =
|
|||||||
'Friday'
|
'Friday'
|
||||||
'Saturday'
|
'Saturday'
|
||||||
]
|
]
|
||||||
|
|
||||||
month: [
|
month: [
|
||||||
'January'
|
'January'
|
||||||
'February'
|
'February'
|
||||||
@ -37,7 +40,9 @@ Time =
|
|||||||
'November'
|
'November'
|
||||||
'December'
|
'December'
|
||||||
]
|
]
|
||||||
|
|
||||||
zeroPad: (n) -> if n < 10 then "0#{n}" else n
|
zeroPad: (n) -> if n < 10 then "0#{n}" else n
|
||||||
|
|
||||||
formatters:
|
formatters:
|
||||||
a: -> Time.day[@getDay()][...3]
|
a: -> Time.day[@getDay()][...3]
|
||||||
A: -> Time.day[@getDay()]
|
A: -> Time.day[@getDay()]
|
||||||
@ -56,3 +61,4 @@ Time =
|
|||||||
S: -> Time.zeroPad @getSeconds()
|
S: -> Time.zeroPad @getSeconds()
|
||||||
y: -> @getFullYear().toString()[2..]
|
y: -> @getFullYear().toString()[2..]
|
||||||
Y: -> @getFullYear()
|
Y: -> @getFullYear()
|
||||||
|
'%': -> '%'
|
||||||
|
|||||||
@ -1,5 +1,15 @@
|
|||||||
Favicon =
|
Favicon =
|
||||||
init: ->
|
init: ->
|
||||||
|
$.asap (-> d.head and Favicon.el = $ 'link[rel="shortcut icon"]', d.head), Favicon.initAsap
|
||||||
|
|
||||||
|
initAsap: ->
|
||||||
|
Favicon.el.type = 'image/x-icon'
|
||||||
|
{href} = Favicon.el
|
||||||
|
Favicon.SFW = /ws\.ico$/.test href
|
||||||
|
Favicon.default = href
|
||||||
|
Favicon.switch()
|
||||||
|
|
||||||
|
switch: ->
|
||||||
items = {
|
items = {
|
||||||
ferongr: [
|
ferongr: [
|
||||||
'<%= grunt.file.read("src/General/img/favicons/ferongr/unreadDead.png", {encoding: "base64"}) %>'
|
'<%= grunt.file.read("src/General/img/favicons/ferongr/unreadDead.png", {encoding: "base64"}) %>'
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
ThreadExcerpt =
|
ThreadExcerpt =
|
||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW isnt 'thread' or !Conf['Thread Excerpt']
|
return if (g.BOARD.ID isnt 'f' and g.BOARD.ID isnt 'pol') or g.VIEW isnt 'thread' or !Conf['Thread Excerpt'] or Conf['Remove Thread Excerpt']
|
||||||
|
|
||||||
Thread.callbacks.push
|
Thread.callbacks.push
|
||||||
name: 'Thread Excerpt'
|
name: 'Thread Excerpt'
|
||||||
|
|||||||
@ -21,16 +21,18 @@ ThreadStats =
|
|||||||
id: 'thread-stats'
|
id: 'thread-stats'
|
||||||
title: title
|
title: title
|
||||||
Header.addShortcut sc
|
Header.addShortcut sc
|
||||||
|
|
||||||
else
|
else
|
||||||
@dialog = sc = UI.dialog 'thread-stats', 'bottom: 0px; right: 0px;',
|
@dialog = sc = UI.dialog 'thread-stats', 'bottom: 0px; right: 0px;',
|
||||||
"<div class=move title='#{title}'>#{html}</div>"
|
<%= html('<div class="move" title="${statsTitle}">&{statsHTML}</div>') %>
|
||||||
|
$.addClass doc, 'float'
|
||||||
$.ready ->
|
$.ready ->
|
||||||
$.add d.body, sc
|
$.add d.body, sc
|
||||||
|
|
||||||
@postCountEl = $ '#post-count', sc
|
@postCountEl = $ '#post-count', sc
|
||||||
@ipCountEl = $ '#ip-count', sc
|
@ipCountEl = $ '#ip-count', sc
|
||||||
@fileCountEl = $ '#file-count', sc
|
@fileCountEl = $ '#file-count', sc
|
||||||
@pageCountEl = $ '#page-count', sc
|
@pageCountEl = $ '#page-count', sc
|
||||||
|
|
||||||
Thread.callbacks.push
|
Thread.callbacks.push
|
||||||
name: 'Thread Stats'
|
name: 'Thread Stats'
|
||||||
@ -42,6 +44,7 @@ ThreadStats =
|
|||||||
@posts.forEach (post) ->
|
@posts.forEach (post) ->
|
||||||
postCount++
|
postCount++
|
||||||
fileCount++ if post.file
|
fileCount++ if post.file
|
||||||
|
ThreadStats.lastPost = post.info.date if Conf["Page Count in Stats"]
|
||||||
ThreadStats.thread = @
|
ThreadStats.thread = @
|
||||||
ThreadStats.fetchPage()
|
ThreadStats.fetchPage()
|
||||||
ThreadStats.update postCount, fileCount, @ipCount
|
ThreadStats.update postCount, fileCount, @ipCount
|
||||||
@ -88,6 +91,7 @@ ThreadStats =
|
|||||||
|
|
||||||
fetchPage: ->
|
fetchPage: ->
|
||||||
return if !Conf["Page Count in Stats"]
|
return if !Conf["Page Count in Stats"]
|
||||||
|
clearTimeout ThreadStats.timeout
|
||||||
if ThreadStats.thread.isDead
|
if ThreadStats.thread.isDead
|
||||||
ThreadStats.pageCountEl.textContent = 'Dead'
|
ThreadStats.pageCountEl.textContent = 'Dead'
|
||||||
$.addClass ThreadStats.pageCountEl, 'warning'
|
$.addClass ThreadStats.pageCountEl, 'warning'
|
||||||
@ -102,4 +106,6 @@ ThreadStats =
|
|||||||
for thread in page.threads when thread.no is ThreadStats.thread.ID
|
for thread in page.threads when thread.no is ThreadStats.thread.ID
|
||||||
ThreadStats.pageCountEl.textContent = page.page
|
ThreadStats.pageCountEl.textContent = page.page
|
||||||
(if page.page is @response.length then $.addClass else $.rmClass) ThreadStats.pageCountEl, 'warning'
|
(if page.page is @response.length then $.addClass else $.rmClass) ThreadStats.pageCountEl, 'warning'
|
||||||
|
# Thread data may be stale (modification date given < time of last post). If so, try again on next thread update.
|
||||||
|
ThreadStats.lastPageUpdate = new Date thread.last_modified * $.SECOND
|
||||||
return
|
return
|
||||||
|
|||||||
@ -4,14 +4,13 @@ ThreadUpdater =
|
|||||||
|
|
||||||
if Conf['Updater and Stats in Header']
|
if Conf['Updater and Stats in Header']
|
||||||
@dialog = sc = $.el 'span',
|
@dialog = sc = $.el 'span',
|
||||||
innerHTML: "[<span id=update-status></span><span id=update-timer title='Update now'></span>]\u00A0"
|
|
||||||
id: 'updater'
|
id: 'updater'
|
||||||
|
$.extend sc, <%= html('[<span id="update-status"></span><span id="update-timer" title="Update now"></span>]') %>
|
||||||
$.ready ->
|
$.ready ->
|
||||||
Header.addShortcut sc
|
Header.addShortcut sc
|
||||||
else
|
else
|
||||||
@dialog = sc = UI.dialog 'updater', 'bottom: 0px; left: 0px;',
|
@dialog = sc = UI.dialog 'updater', 'bottom: 0px; left: 0px;',
|
||||||
"<div class=move><span id=update-status></span><span id=update-timer title='Update now'></span></div>"
|
<%= html('<div class="move"></div><span id="update-status"></span><span id="update-timer" title="Update now"></span>') %>
|
||||||
$.addClass doc, 'float'
|
|
||||||
$.ready ->
|
$.ready ->
|
||||||
$.addClass doc, 'float'
|
$.addClass doc, 'float'
|
||||||
$.add d.body, sc
|
$.add d.body, sc
|
||||||
@ -28,20 +27,18 @@ ThreadUpdater =
|
|||||||
subEntries = []
|
subEntries = []
|
||||||
for name, conf of Config.updater.checkbox
|
for name, conf of Config.updater.checkbox
|
||||||
checked = if Conf[name] then 'checked' else ''
|
checked = if Conf[name] then 'checked' else ''
|
||||||
el = $.el 'label',
|
el = UI.checkbox name, " #{name}"
|
||||||
title: "#{conf[1]}"
|
|
||||||
innerHTML: "<input name='#{name}' type=checkbox #{checked}> #{name}"
|
|
||||||
input = el.firstElementChild
|
input = el.firstElementChild
|
||||||
$.on input, 'change', $.cb.checked
|
$.on input, 'change', $.cb.checked
|
||||||
if input.name is 'Scroll BG'
|
if input.name is 'Scroll BG'
|
||||||
$.on input, 'change', @cb.scrollBG
|
$.on input, 'change', @cb.scrollBG
|
||||||
@cb.scrollBG()
|
@cb.scrollBG()
|
||||||
else if input.name is 'Auto Update'
|
else if input.name is 'Auto Update'
|
||||||
$.on input, 'change', @cb.update
|
$.on input, 'change', @cb.autoUpdate
|
||||||
subEntries.push el: el
|
subEntries.push el: el
|
||||||
|
|
||||||
@settings = $.el 'span',
|
@settings = $.el 'span',
|
||||||
innerHTML: '<a href=javascript:;>Interval</a>'
|
<%= html('<a href="javascript:;">Interval</a>') %>
|
||||||
|
|
||||||
$.on @settings, 'click', @intervalShortcut
|
$.on @settings, 'click', @intervalShortcut
|
||||||
|
|
||||||
@ -92,9 +89,10 @@ ThreadUpdater =
|
|||||||
Thread.callbacks.disconnect 'Thread Updater'
|
Thread.callbacks.disconnect 'Thread Updater'
|
||||||
|
|
||||||
node: ->
|
node: ->
|
||||||
ThreadUpdater.thread = @
|
ThreadUpdater.thread = @
|
||||||
ThreadUpdater.root = @OP.nodes.root.parentNode
|
ThreadUpdater.root = @OP.nodes.root.parentNode
|
||||||
ThreadUpdater.lastPost = +@posts.keys[@posts.keys.length - 1]
|
ThreadUpdater.lastPost = +@posts.keys[@posts.keys.length - 1]
|
||||||
|
ThreadUpdater.outdateCount = 0
|
||||||
|
|
||||||
ThreadUpdater.cb.interval.call $.el 'input',
|
ThreadUpdater.cb.interval.call $.el 'input',
|
||||||
value: Conf['Interval']
|
value: Conf['Interval']
|
||||||
@ -110,8 +108,6 @@ ThreadUpdater =
|
|||||||
ThreadUpdater.cb.online()
|
ThreadUpdater.cb.online()
|
||||||
|
|
||||||
Rice.nodes ThreadUpdater.dialog
|
Rice.nodes ThreadUpdater.dialog
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
###
|
###
|
||||||
http://freesound.org/people/pierrecartoons1979/sounds/90112/
|
http://freesound.org/people/pierrecartoons1979/sounds/90112/
|
||||||
@ -121,24 +117,26 @@ ThreadUpdater =
|
|||||||
|
|
||||||
cb:
|
cb:
|
||||||
online: ->
|
online: ->
|
||||||
|
return if ThreadUpdater.thread.isDead
|
||||||
if ThreadUpdater.online = navigator.onLine
|
if ThreadUpdater.online = navigator.onLine
|
||||||
ThreadUpdater.outdateCount = 0
|
ThreadUpdater.outdateCount = 0
|
||||||
ThreadUpdater.setInterval()
|
ThreadUpdater.setInterval()
|
||||||
ThreadUpdater.set 'status', null, null
|
ThreadUpdater.set 'status', '', ''
|
||||||
return
|
return
|
||||||
ThreadUpdater.set 'timer', null
|
ThreadUpdater.set 'timer', ''
|
||||||
ThreadUpdater.set 'status', 'Offline', 'warning'
|
ThreadUpdater.set 'status', 'Offline', 'warning'
|
||||||
|
clearTimeout ThreadUpdater.timeoutID
|
||||||
post: (e) ->
|
post: (e) ->
|
||||||
return unless ThreadUpdater.isUpdating and e.detail.threadID is ThreadUpdater.thread.ID
|
return unless ThreadUpdater.isUpdating and e.detail.threadID is ThreadUpdater.thread.ID
|
||||||
ThreadUpdater.outdateCount = 0
|
ThreadUpdater.outdateCount = 0
|
||||||
setTimeout ThreadUpdater.update, 1000 if ThreadUpdater.seconds > 2
|
setTimeout ThreadUpdater.update, 1000 if ThreadUpdater.seconds > 2
|
||||||
checkpost: (e) ->
|
checkpost: (e) ->
|
||||||
unless ThreadUpdater.checkPostCount
|
unless ThreadUpdater.checkPostCount
|
||||||
return unless e.detail.threadID is ThreadUpdater.thread.ID
|
return if e and e.detail.threadID isnt ThreadUpdater.thread.ID
|
||||||
ThreadUpdater.seconds = 0
|
ThreadUpdater.seconds = 0
|
||||||
ThreadUpdater.outdateCount = 0
|
ThreadUpdater.outdateCount = 0
|
||||||
ThreadUpdater.set 'timer', '...'
|
ThreadUpdater.set 'timer', '...'
|
||||||
unless g.DEAD or ThreadUpdater.foundPost or ThreadUpdater.checkPostCount >= 5
|
unless ThreadUpdater.thread.isDead or ThreadUpdater.foundPost or ThreadUpdater.checkPostCount >= 5
|
||||||
return setTimeout ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND
|
return setTimeout ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND
|
||||||
ThreadUpdater.setInterval()
|
ThreadUpdater.setInterval()
|
||||||
ThreadUpdater.checkPostCount = 0
|
ThreadUpdater.checkPostCount = 0
|
||||||
@ -155,6 +153,8 @@ ThreadUpdater =
|
|||||||
-> true
|
-> true
|
||||||
else
|
else
|
||||||
-> not d.hidden
|
-> not d.hidden
|
||||||
|
autoUpdate: (e) ->
|
||||||
|
ThreadUpdater.count ThreadUpdater.isUpdating = @checked
|
||||||
interval: (e) ->
|
interval: (e) ->
|
||||||
val = parseInt @value, 10
|
val = parseInt @value, 10
|
||||||
if val < 1 then val = 1
|
if val < 1 then val = 1
|
||||||
@ -164,7 +164,6 @@ ThreadUpdater =
|
|||||||
{req} = ThreadUpdater
|
{req} = ThreadUpdater
|
||||||
switch req.status
|
switch req.status
|
||||||
when 200
|
when 200
|
||||||
g.DEAD = false
|
|
||||||
ThreadUpdater.parse req.response.posts
|
ThreadUpdater.parse req.response.posts
|
||||||
if ThreadUpdater.thread.isArchived
|
if ThreadUpdater.thread.isArchived
|
||||||
ThreadUpdater.set 'status', 'Archived', 'warning'
|
ThreadUpdater.set 'status', 'Archived', 'warning'
|
||||||
@ -257,9 +256,10 @@ ThreadUpdater =
|
|||||||
timeout: ->
|
timeout: ->
|
||||||
ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000
|
ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000
|
||||||
unless n = --ThreadUpdater.seconds
|
unless n = --ThreadUpdater.seconds
|
||||||
|
ThreadUpdater.outdateCount++
|
||||||
ThreadUpdater.update()
|
ThreadUpdater.update()
|
||||||
else if n <= -60
|
else if n <= -60
|
||||||
ThreadUpdater.set 'status', 'Retrying', null
|
ThreadUpdater.set 'status', 'Retrying', ''
|
||||||
ThreadUpdater.update()
|
ThreadUpdater.update()
|
||||||
else if n > 0
|
else if n > 0
|
||||||
ThreadUpdater.set 'timer', n
|
ThreadUpdater.set 'timer', n
|
||||||
@ -307,7 +307,7 @@ ThreadUpdater =
|
|||||||
ThreadUpdater.updateThreadStatus 'Closed', !!OP.closed
|
ThreadUpdater.updateThreadStatus 'Closed', !!OP.closed
|
||||||
ThreadUpdater.thread.postLimit = !!OP.bumplimit
|
ThreadUpdater.thread.postLimit = !!OP.bumplimit
|
||||||
ThreadUpdater.thread.fileLimit = !!OP.imagelimit
|
ThreadUpdater.thread.fileLimit = !!OP.imagelimit
|
||||||
ThreadUpdater.thread.ipCount = OP.unique_ips if OP.unique_ips?
|
ThreadUpdater.thread.ipCount = OP.unique_ips if OP.unique_ips?
|
||||||
|
|
||||||
posts = [] # post objects
|
posts = [] # post objects
|
||||||
index = [] # existing posts
|
index = [] # existing posts
|
||||||
@ -331,18 +331,28 @@ ThreadUpdater =
|
|||||||
# continue if post.isDead
|
# continue if post.isDead
|
||||||
ID = +post.ID
|
ID = +post.ID
|
||||||
|
|
||||||
unless ID in index
|
# Assume deleted posts less than 60 seconds old are false positives.
|
||||||
post.kill()
|
unless post.info.date > Date.now() - 60 * $.SECOND
|
||||||
else if post.isDead
|
unless ID in index
|
||||||
post.resurrect()
|
post.kill()
|
||||||
else if post.file and !post.file.isDead and ID not in files
|
else if post.isDead
|
||||||
post.kill true
|
post.resurrect()
|
||||||
|
else if post.file and not (post.file.isDead or ID in files)
|
||||||
|
post.kill true
|
||||||
|
|
||||||
# Fetching your own posts after posting
|
# Fetching your own posts after posting
|
||||||
if ThreadUpdater.postID and ThreadUpdater.postID is ID
|
if ThreadUpdater.postID and ThreadUpdater.postID is ID
|
||||||
ThreadUpdater.foundPost = true
|
ThreadUpdater.foundPost = true
|
||||||
|
|
||||||
sendEvent = ->
|
sendEvent = ->
|
||||||
|
# Update IP count in original post form.
|
||||||
|
if OP.unique_ips? and ipCountEl = $.id('unique-ips')
|
||||||
|
ipCountEl.textContent = OP.unique_ips
|
||||||
|
ipCountEl.previousSibling.textContent = ipCountEl.previousSibling.textContent.replace(/\b(?:is|are)\b/, if OP.unique_ips is 1 then 'is' else 'are')
|
||||||
|
ipCountEl.nextSibling.textContent = ipCountEl.nextSibling.textContent.replace(/\bposters?\b/, if OP.unique_ips is 1 then 'poster' else 'posters')
|
||||||
|
|
||||||
|
ThreadUpdater.postIDs = index
|
||||||
|
|
||||||
$.event 'ThreadUpdate',
|
$.event 'ThreadUpdate',
|
||||||
404: false
|
404: false
|
||||||
threadID: ThreadUpdater.thread.fullID
|
threadID: ThreadUpdater.thread.fullID
|
||||||
@ -352,7 +362,7 @@ ThreadUpdater =
|
|||||||
ipCount: OP.unique_ips
|
ipCount: OP.unique_ips
|
||||||
|
|
||||||
unless count
|
unless count
|
||||||
ThreadUpdater.set 'status', null, null
|
ThreadUpdater.set 'status', '', ''
|
||||||
ThreadUpdater.outdateCount++
|
ThreadUpdater.outdateCount++
|
||||||
sendEvent()
|
sendEvent()
|
||||||
return
|
return
|
||||||
|
|||||||
@ -3,17 +3,18 @@ ThreadWatcher =
|
|||||||
return if !Conf['Thread Watcher']
|
return if !Conf['Thread Watcher']
|
||||||
|
|
||||||
@db = new DataBoard 'watchedThreads', @refresh, true
|
@db = new DataBoard 'watchedThreads', @refresh, true
|
||||||
@dialog = UI.dialog 'thread-watcher', 'top: 50px; left: 0px;', """<%=
|
@dialog = UI.dialog 'thread-watcher', 'top: 50px; left: 0px;', <%= importHTML('Monitoring/ThreadWatcher') %>
|
||||||
grunt.file.read('src/General/html/Monitoring/ThreadWatcher.html').replace(/>\s+</g, '><').trim()
|
|
||||||
%>"""
|
|
||||||
|
|
||||||
@status = $ '#watcher-status', @dialog
|
@status = $ '#watcher-status', @dialog
|
||||||
@list = @dialog.lastElementChild
|
@list = @dialog.lastElementChild
|
||||||
|
@refreshButton = $ '.refresh', @dialog
|
||||||
|
|
||||||
$.on d, 'QRPostSuccessful', @cb.post
|
$.on d, 'QRPostSuccessful', @cb.post
|
||||||
$.on d, 'ThreadUpdate', @cb.threadUpdate if g.VIEW is 'thread'
|
$.on d, 'ThreadUpdate', @cb.threadUpdate if g.VIEW is 'thread'
|
||||||
|
$.on @refreshButton, 'click', @fetchAllStatus
|
||||||
|
|
||||||
$.on d, '4chanXInitFinished', @ready
|
$.on d, '4chanXInitFinished', @ready
|
||||||
|
|
||||||
switch g.VIEW
|
switch g.VIEW
|
||||||
when 'index'
|
when 'index'
|
||||||
$.on d, 'IndexRefresh', @cb.onIndexRefresh
|
$.on d, 'IndexRefresh', @cb.onIndexRefresh
|
||||||
@ -26,15 +27,50 @@ ThreadWatcher =
|
|||||||
ThreadWatcher.fetchAllStatus()
|
ThreadWatcher.fetchAllStatus()
|
||||||
@db.save()
|
@db.save()
|
||||||
|
|
||||||
Thread.callbacks.push
|
if Conf['JSON Navigation'] and Conf['Menu'] and g.BOARD.ID isnt 'f'
|
||||||
|
Menu.menu.addEntry
|
||||||
|
el: $.el 'a', href: 'javascript:;'
|
||||||
|
order: 6
|
||||||
|
open: ({thread}) ->
|
||||||
|
return false unless Conf['Index Mode'] is 'catalog' and g.VIEW is 'index'
|
||||||
|
@el.textContent = if ThreadWatcher.isWatched thread
|
||||||
|
'Unwatch thread'
|
||||||
|
else
|
||||||
|
'Watch thread'
|
||||||
|
$.off @el, 'click', @cb if @cb
|
||||||
|
@cb = ->
|
||||||
|
$.event 'CloseMenu'
|
||||||
|
ThreadWatcher.toggle thread
|
||||||
|
$.on @el, 'click', @cb
|
||||||
|
true
|
||||||
|
|
||||||
|
Post.callbacks.push
|
||||||
name: 'Thread Watcher'
|
name: 'Thread Watcher'
|
||||||
cb: @node
|
cb: @node
|
||||||
|
|
||||||
|
CatalogThread.callbacks.push
|
||||||
|
name: 'Thread Watcher'
|
||||||
|
cb: @catalogNode
|
||||||
|
|
||||||
|
isWatched: (thread) ->
|
||||||
|
ThreadWatcher.db?.get {boardID: thread.board.ID, threadID: thread.ID}
|
||||||
|
|
||||||
node: ->
|
node: ->
|
||||||
toggler = $.el 'img',
|
return if @isReply
|
||||||
className: 'watch-thread-link'
|
if @isClone
|
||||||
|
toggler = $ '.watch-thread-link', @nodes.post
|
||||||
|
else
|
||||||
|
toggler = $.el 'img',
|
||||||
|
className: 'watch-thread-link'
|
||||||
|
$.before $('input', @nodes.post), toggler
|
||||||
$.on toggler, 'click', ThreadWatcher.cb.toggle
|
$.on toggler, 'click', ThreadWatcher.cb.toggle
|
||||||
$.before $('input', @OP.nodes.post), toggler
|
|
||||||
|
catalogNode: ->
|
||||||
|
$.addClass @nodes.root, 'watched' if ThreadWatcher.isWatched @thread
|
||||||
|
$.on @nodes.thumb.parentNode, 'click', (e) =>
|
||||||
|
return unless e.button is 0 and e.altKey
|
||||||
|
ThreadWatcher.toggle @thread
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
ready: ->
|
ready: ->
|
||||||
$.off d, '4chanXInitFinished', ThreadWatcher.ready
|
$.off d, '4chanXInitFinished', ThreadWatcher.ready
|
||||||
@ -63,9 +99,6 @@ ThreadWatcher =
|
|||||||
for a in $$ 'a[title]', ThreadWatcher.list
|
for a in $$ 'a[title]', ThreadWatcher.list
|
||||||
$.open a.href
|
$.open a.href
|
||||||
$.event 'CloseMenu'
|
$.event 'CloseMenu'
|
||||||
checkThreads: ->
|
|
||||||
return if $.hasClass @, 'disabled'
|
|
||||||
ThreadWatcher.fetchAllStatus()
|
|
||||||
pruneDeads: ->
|
pruneDeads: ->
|
||||||
return if $.hasClass @, 'disabled'
|
return if $.hasClass @, 'disabled'
|
||||||
for {boardID, threadID, data} in ThreadWatcher.getAll() when data.isDead
|
for {boardID, threadID, data} in ThreadWatcher.getAll() when data.isDead
|
||||||
@ -76,6 +109,9 @@ ThreadWatcher =
|
|||||||
$.event 'CloseMenu'
|
$.event 'CloseMenu'
|
||||||
toggle: ->
|
toggle: ->
|
||||||
ThreadWatcher.toggle Get.threadFromNode @
|
ThreadWatcher.toggle Get.threadFromNode @
|
||||||
|
Index.followedThreadID = thread.ID
|
||||||
|
ThreadWatcher.toggle thread
|
||||||
|
delete Index.followedThreadID
|
||||||
rm: ->
|
rm: ->
|
||||||
[boardID, threadID] = @parentNode.dataset.fullID.split '.'
|
[boardID, threadID] = @parentNode.dataset.fullID.split '.'
|
||||||
ThreadWatcher.rm boardID, +threadID
|
ThreadWatcher.rm boardID, +threadID
|
||||||
@ -87,8 +123,10 @@ ThreadWatcher =
|
|||||||
else if Conf['Auto Watch Reply']
|
else if Conf['Auto Watch Reply']
|
||||||
ThreadWatcher.add g.threads[boardID + '.' + threadID]
|
ThreadWatcher.add g.threads[boardID + '.' + threadID]
|
||||||
onIndexRefresh: ->
|
onIndexRefresh: ->
|
||||||
|
{db} = ThreadWatcher
|
||||||
boardID = g.BOARD.ID
|
boardID = g.BOARD.ID
|
||||||
for threadID, data of ThreadWatcher.db.data.boards[boardID] when not data.isDead and threadID not of g.BOARD.threads
|
db.forceSync()
|
||||||
|
for threadID, data of db.data.boards[boardID] when not data.isDead and threadID not of g.BOARD.threads
|
||||||
if Conf['Auto Prune']
|
if Conf['Auto Prune']
|
||||||
ThreadWatcher.db.delete {boardID, threadID}
|
ThreadWatcher.db.delete {boardID, threadID}
|
||||||
else
|
else
|
||||||
@ -98,21 +136,26 @@ ThreadWatcher =
|
|||||||
onThreadRefresh: (e) ->
|
onThreadRefresh: (e) ->
|
||||||
thread = g.threads[e.detail.threadID]
|
thread = g.threads[e.detail.threadID]
|
||||||
return unless e.detail[404] and ThreadWatcher.db.get {boardID: thread.board.ID, threadID: thread.ID}
|
return unless e.detail[404] and ThreadWatcher.db.get {boardID: thread.board.ID, threadID: thread.ID}
|
||||||
# Update 404 status.
|
# Update dead status.
|
||||||
ThreadWatcher.add thread
|
ThreadWatcher.add thread
|
||||||
|
|
||||||
fetchCount:
|
fetchCount:
|
||||||
fetched: 0
|
fetched: 0
|
||||||
fetching: 0
|
fetching: 0
|
||||||
fetchAllStatus: ->
|
fetchAllStatus: ->
|
||||||
|
ThreadWatcher.db.forceSync()
|
||||||
|
ThreadWatcher.unreaddb.forceSync()
|
||||||
|
QR.db.forceSync()
|
||||||
return unless (threads = ThreadWatcher.getAll()).length
|
return unless (threads = ThreadWatcher.getAll()).length
|
||||||
ThreadWatcher.status.textContent = '...'
|
|
||||||
for thread in threads
|
for thread in threads
|
||||||
ThreadWatcher.fetchStatus thread
|
ThreadWatcher.fetchStatus thread
|
||||||
return
|
return
|
||||||
fetchStatus: ({boardID, threadID, data}) ->
|
fetchStatus: ({boardID, threadID, data}) ->
|
||||||
return if data.isDead
|
return if data.isDead and !Conf['Show Unread Count']
|
||||||
{fetchCount} = ThreadWatcher
|
{fetchCount} = ThreadWatcher
|
||||||
|
if fetchCount.fetching is 0
|
||||||
|
ThreadWatcher.status.textContent = '...'
|
||||||
|
$.addClass ThreadWatcher.refreshButton, 'fa-spin'
|
||||||
fetchCount.fetching++
|
fetchCount.fetching++
|
||||||
$.ajax "//a.4cdn.org/#{boardID}/thread/#{threadID}.json",
|
$.ajax "//a.4cdn.org/#{boardID}/thread/#{threadID}.json",
|
||||||
onloadend: ->
|
onloadend: ->
|
||||||
@ -121,18 +164,56 @@ ThreadWatcher =
|
|||||||
fetchCount.fetched = 0
|
fetchCount.fetched = 0
|
||||||
fetchCount.fetching = 0
|
fetchCount.fetching = 0
|
||||||
status = ''
|
status = ''
|
||||||
|
$.rmClass ThreadWatcher.refreshButton, 'fa-spin'
|
||||||
else
|
else
|
||||||
status = "#{Math.round fetchCount.fetched / fetchCount.fetching * 100}%"
|
status = "#{Math.round fetchCount.fetched / fetchCount.fetching * 100}%"
|
||||||
ThreadWatcher.status.textContent = status
|
ThreadWatcher.status.textContent = status
|
||||||
return if @status isnt 404
|
|
||||||
if Conf['Auto Prune']
|
if @status is 200 and @response
|
||||||
ThreadWatcher.db.delete {boardID, threadID}
|
isDead = !!@response.posts[0].archived
|
||||||
else
|
if isDead and Conf['Auto Prune']
|
||||||
data.isDead = true
|
ThreadWatcher.db.delete {boardID, threadID}
|
||||||
ThreadWatcher.db.set {boardID, threadID, val: data}
|
ThreadWatcher.refresh()
|
||||||
ThreadWatcher.refresh()
|
return
|
||||||
,
|
|
||||||
type: 'head'
|
lastReadPost = ThreadWatcher.unreaddb.get
|
||||||
|
boardID: boardID
|
||||||
|
threadID: threadID
|
||||||
|
defaultValue: 0
|
||||||
|
|
||||||
|
unread = quotingYou = 0
|
||||||
|
|
||||||
|
for postObj in @response.posts
|
||||||
|
continue unless postObj.no > lastReadPost
|
||||||
|
continue if QR.db?.get {boardID, threadID, postID: postObj.no}
|
||||||
|
unread++
|
||||||
|
continue unless QR.db and postObj.com
|
||||||
|
regexp = /<a [^>]*\bhref="(?:\/([^\/]+)\/thread\/(\d+))?(?:#p(\d+))?"/g
|
||||||
|
while match = regexp.exec postObj.com
|
||||||
|
if QR.db.get {
|
||||||
|
boardID: match[1] or boardID
|
||||||
|
threadID: match[2] or threadID
|
||||||
|
postID: match[3] or match[2] or threadID
|
||||||
|
}
|
||||||
|
quotingYou++
|
||||||
|
continue
|
||||||
|
|
||||||
|
if isDead isnt data.isDead or unread isnt data.unread or quotingYou isnt data.quotingYou
|
||||||
|
data.isDead = isDead
|
||||||
|
data.unread = unread
|
||||||
|
data.quotingYou = quotingYou
|
||||||
|
ThreadWatcher.db.set {boardID, threadID, val: data}
|
||||||
|
ThreadWatcher.refresh()
|
||||||
|
|
||||||
|
else if @status is 404
|
||||||
|
if Conf['Auto Prune']
|
||||||
|
ThreadWatcher.db.delete {boardID, threadID}
|
||||||
|
else
|
||||||
|
data.isDead = true
|
||||||
|
delete data.unread
|
||||||
|
delete data.quotingYou
|
||||||
|
ThreadWatcher.db.set {boardID, threadID, val: data}
|
||||||
|
ThreadWatcher.refresh()
|
||||||
|
|
||||||
getAll: ->
|
getAll: ->
|
||||||
all = []
|
all = []
|
||||||
@ -150,18 +231,31 @@ ThreadWatcher =
|
|||||||
textContent: '\uf00d'
|
textContent: '\uf00d'
|
||||||
$.on x, 'click', ThreadWatcher.cb.rm
|
$.on x, 'click', ThreadWatcher.cb.rm
|
||||||
|
|
||||||
if data.isDead
|
|
||||||
href = Redirect.to 'thread', {boardID, threadID}
|
|
||||||
link = $.el 'a',
|
link = $.el 'a',
|
||||||
href: href or "/#{boardID}/thread/#{threadID}"
|
href: "/#{boardID}/thread/#{threadID}"
|
||||||
textContent: data.excerpt
|
textContent: data.excerpt
|
||||||
title: data.excerpt
|
title: data.excerpt
|
||||||
|
className: 'watcher-link'
|
||||||
|
|
||||||
|
if Conf['Show Unread Count'] and data.unread?
|
||||||
|
count = $.el 'span',
|
||||||
|
textContent: "(#{data.unread})"
|
||||||
|
className: 'watcher-unread'
|
||||||
|
$.add link, count
|
||||||
|
|
||||||
|
title = $.el 'span',
|
||||||
|
textContent: data.excerpt
|
||||||
|
className: 'watcher-title'
|
||||||
|
$.add link, title
|
||||||
|
|
||||||
div = $.el 'div'
|
div = $.el 'div'
|
||||||
fullID = "#{boardID}.#{threadID}"
|
fullID = "#{boardID}.#{threadID}"
|
||||||
div.dataset.fullID = fullID
|
div.dataset.fullID = fullID
|
||||||
$.addClass div, 'current' if g.VIEW is 'thread' and fullID is "#{g.BOARD}.#{g.THREADID}"
|
$.addClass div, 'current' if g.VIEW is 'thread' and fullID is "#{g.BOARD}.#{g.THREADID}"
|
||||||
$.addClass div, 'dead-thread' if data.isDead
|
$.addClass div, 'dead-thread' if data.isDead
|
||||||
|
if Conf['Show Unread Count']
|
||||||
|
$.addClass div, 'replies-unread' if data.unread
|
||||||
|
$.addClass div, 'replies-quoting-you' if data.quotingYou
|
||||||
$.add div, [x, $.tn(' '), link]
|
$.add div, [x, $.tn(' '), link]
|
||||||
div
|
div
|
||||||
refresh: ->
|
refresh: ->
|
||||||
@ -173,18 +267,40 @@ ThreadWatcher =
|
|||||||
$.rmAll list
|
$.rmAll list
|
||||||
$.add list, nodes
|
$.add list, nodes
|
||||||
|
|
||||||
{threads} = g.BOARD
|
g.threads.forEach (thread) ->
|
||||||
for threadID in threads.keys
|
helper = if ThreadWatcher.isWatched thread then ['addClass', 'Unwatch'] else ['rmClass', 'Watch']
|
||||||
thread = threads[threadID]
|
if thread.OP
|
||||||
toggler = $ '.watch-thread-link', thread.OP.nodes.post
|
for post in [thread.OP, thread.OP.clones...]
|
||||||
watched = ThreadWatcher.db.get {boardID: thread.board.ID, threadID}
|
toggler = $ '.watch-thread-link', post.nodes.post
|
||||||
helper = if watched then ['addClass', 'Unwatch'] else ['rmClass', 'Watch']
|
$[helper[0]] toggler, 'watched'
|
||||||
$[helper[0]] toggler, 'watched'
|
toggler.title = "#{helper[1]} Thread"
|
||||||
toggler.title = "#{helper[1]} Thread"
|
$[helper[0]] thread.catalogView.nodes.root, 'watched' if thread.catalogView
|
||||||
|
|
||||||
for refresher in ThreadWatcher.menu.refreshers
|
for refresher in ThreadWatcher.menu.refreshers
|
||||||
refresher()
|
refresher()
|
||||||
return
|
|
||||||
|
if Index.nodes and Conf['Pin Watched Threads']
|
||||||
|
Index.sort()
|
||||||
|
Index.buildIndex()
|
||||||
|
|
||||||
|
update: (boardID, threadID, newData) ->
|
||||||
|
return unless data = ThreadWatcher.db?.get {boardID, threadID}
|
||||||
|
if newData.isDead and Conf['Auto Prune']
|
||||||
|
ThreadWatcher.db.delete {boardID, threadID}
|
||||||
|
ThreadWatcher.refresh()
|
||||||
|
return
|
||||||
|
n = 0
|
||||||
|
n++ for key, val of newData when data[key] isnt val
|
||||||
|
return unless n
|
||||||
|
ThreadWatcher.db.forceSync()
|
||||||
|
return unless data = ThreadWatcher.db.get {boardID, threadID}
|
||||||
|
$.extend data, newData
|
||||||
|
ThreadWatcher.db.set {boardID, threadID, val: data}
|
||||||
|
if line = $ "#watched-threads > [data-full-i-d='#{boardID}.#{threadID}']", ThreadWatcher.dialog
|
||||||
|
newLine = ThreadWatcher.makeLine boardID, threadID, data
|
||||||
|
$.replace line, newLine
|
||||||
|
else
|
||||||
|
ThreadWatcher.refresh()
|
||||||
|
|
||||||
toggle: (thread) ->
|
toggle: (thread) ->
|
||||||
boardID = thread.board.ID
|
boardID = thread.board.ID
|
||||||
@ -205,6 +321,8 @@ ThreadWatcher =
|
|||||||
data.excerpt = Get.threadExcerpt thread
|
data.excerpt = Get.threadExcerpt thread
|
||||||
ThreadWatcher.db.set {boardID, threadID, val: data}
|
ThreadWatcher.db.set {boardID, threadID, val: data}
|
||||||
ThreadWatcher.refresh()
|
ThreadWatcher.refresh()
|
||||||
|
if Conf['Show Unread Count']
|
||||||
|
ThreadWatcher.fetchStatus {boardID, threadID, data}
|
||||||
rm: (boardID, threadID) ->
|
rm: (boardID, threadID) ->
|
||||||
ThreadWatcher.db.delete {boardID, threadID}
|
ThreadWatcher.db.delete {boardID, threadID}
|
||||||
ThreadWatcher.refresh()
|
ThreadWatcher.refresh()
|
||||||
@ -220,11 +338,11 @@ ThreadWatcher =
|
|||||||
refreshers: []
|
refreshers: []
|
||||||
init: ->
|
init: ->
|
||||||
return if !Conf['Thread Watcher']
|
return if !Conf['Thread Watcher']
|
||||||
menu = new UI.Menu()
|
menu = @menu = new UI.Menu 'thread watcher'
|
||||||
$.on $('.menu-button', ThreadWatcher.dialog), 'click', (e) ->
|
$.on $('.menu-button', ThreadWatcher.dialog), 'click', (e) ->
|
||||||
menu.toggle e, @, ThreadWatcher
|
menu.toggle e, @, ThreadWatcher
|
||||||
@addHeaderMenuEntry()
|
@addHeaderMenuEntry()
|
||||||
@addMenuEntries menu
|
@addMenuEntries
|
||||||
|
|
||||||
addHeaderMenuEntry: ->
|
addHeaderMenuEntry: ->
|
||||||
return if g.VIEW isnt 'thread'
|
return if g.VIEW isnt 'thread'
|
||||||
@ -243,7 +361,7 @@ ThreadWatcher =
|
|||||||
$.rmClass entryEl, rmClass
|
$.rmClass entryEl, rmClass
|
||||||
entryEl.textContent = text
|
entryEl.textContent = text
|
||||||
|
|
||||||
addMenuEntries: (menu) ->
|
addMenuEntries: ->
|
||||||
entries = []
|
entries = []
|
||||||
|
|
||||||
# `Open all` entry
|
# `Open all` entry
|
||||||
@ -254,20 +372,12 @@ ThreadWatcher =
|
|||||||
textContent: 'Open all threads'
|
textContent: 'Open all threads'
|
||||||
refresh: -> (if ThreadWatcher.list.firstElementChild then $.rmClass else $.addClass) @el, 'disabled'
|
refresh: -> (if ThreadWatcher.list.firstElementChild then $.rmClass else $.addClass) @el, 'disabled'
|
||||||
|
|
||||||
# `Check 404'd threads` entry
|
# `Prune dead threads` entry
|
||||||
entries.push
|
|
||||||
cb: ThreadWatcher.cb.checkThreads
|
|
||||||
entry:
|
|
||||||
el: $.el 'a',
|
|
||||||
textContent: 'Check 404\'d threads'
|
|
||||||
refresh: -> (if $('div:not(.dead-thread)', ThreadWatcher.list) then $.rmClass else $.addClass) @el, 'disabled'
|
|
||||||
|
|
||||||
# `Prune 404'd threads` entry
|
|
||||||
entries.push
|
entries.push
|
||||||
cb: ThreadWatcher.cb.pruneDeads
|
cb: ThreadWatcher.cb.pruneDeads
|
||||||
entry:
|
entry:
|
||||||
el: $.el 'a',
|
el: $.el 'a',
|
||||||
textContent: 'Prune 404\'d threads'
|
textContent: 'Prune dead threads'
|
||||||
refresh: -> (if $('.dead-thread', ThreadWatcher.list) then $.rmClass else $.addClass) @el, 'disabled'
|
refresh: -> (if $('.dead-thread', ThreadWatcher.list) then $.rmClass else $.addClass) @el, 'disabled'
|
||||||
|
|
||||||
# `Settings` entries:
|
# `Settings` entries:
|
||||||
@ -284,16 +394,14 @@ ThreadWatcher =
|
|||||||
entry.el.href = 'javascript:;' if entry.el.nodeName is 'A'
|
entry.el.href = 'javascript:;' if entry.el.nodeName is 'A'
|
||||||
$.on entry.el, 'click', cb if cb
|
$.on entry.el, 'click', cb if cb
|
||||||
@refreshers.push refresh.bind entry if refresh
|
@refreshers.push refresh.bind entry if refresh
|
||||||
menu.addEntry entry
|
@menu.addEntry entry
|
||||||
return
|
return
|
||||||
|
|
||||||
createSubEntry: (name, desc) ->
|
createSubEntry: (name, desc) ->
|
||||||
entry =
|
entry =
|
||||||
type: 'thread watcher'
|
type: 'thread watcher'
|
||||||
el: $.el 'label',
|
el: UI.checkbox name, " #{name}"
|
||||||
innerHTML: "<input type=checkbox name='#{name}'> #{name}"
|
|
||||||
title: desc
|
|
||||||
input = entry.el.firstElementChild
|
input = entry.el.firstElementChild
|
||||||
input.checked = Conf[name]
|
|
||||||
$.on input, 'change', $.cb.checked
|
$.on input, 'change', $.cb.checked
|
||||||
$.on input, 'change', ThreadWatcher.refresh if name is 'Current Board'
|
$.on input, 'change', ThreadWatcher.refresh if name in ['Current Board', 'Show Unread Count']
|
||||||
entry
|
entry
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user