Update to ccd0/4chan-x@31c262e531
This commit is contained in:
parent
bf17aab5fe
commit
20d936c1ca
@ -160,7 +160,7 @@
|
||||
'Color User IDs': [true, 'Assign unique colors to user IDs on boards that use them'],
|
||||
'Remove Spoilers': [false, 'Remove all spoilers in text.'],
|
||||
'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'],
|
||||
'Show Support Message': [true, 'Warn if your browser is unsupported. 4chan X may not operate correctly on unsupported browser versions.'],
|
||||
'Show Support Message': [true, 'Warn if your browser is unsupported. appchan x may not operate correctly on unsupported browser versions.'],
|
||||
'Normalize URL': [true, 'Rewrite the URL of the current page, removing stubs and changing /res/ to /thread/.'],
|
||||
'Announcement Hiding': [true, 'Enable announcements to be hidden.']
|
||||
},
|
||||
@ -216,7 +216,7 @@
|
||||
'Unread Favicon': [true, 'Show a different favicon when there are unread posts.'],
|
||||
'Unread Line': [true, 'Show a line to distinguish read posts from unread ones.'],
|
||||
'Scroll to Last Read Post': [true, 'Scroll back to the last read post when reopening a thread.'],
|
||||
'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title if not already present.'],
|
||||
'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title for threads in /f/.'],
|
||||
'Remove Thread Excerpt': [false, 'Replace the excerpt of the thread in the tab title with the board title.'],
|
||||
'Thread Stats': [true, 'Display reply and image count.'],
|
||||
'IP Count in Stats': [true, 'Display the unique IP count in the thread stats.', 1],
|
||||
@ -368,6 +368,7 @@
|
||||
},
|
||||
threadWatcher: {
|
||||
'Current Board': [false, 'Only show watched threads from the current board.'],
|
||||
'Auto Update Thread Watcher': [true, 'Periodically check status of watched threads.'],
|
||||
'Auto Watch': [false, 'Automatically watch threads you start.'],
|
||||
'Auto Watch Reply': [false, 'Automatically watch threads you reply to.'],
|
||||
'Auto Prune': [false, 'Automatically remove dead threads.']
|
||||
@ -8674,7 +8675,7 @@
|
||||
Quotify = {
|
||||
init: function() {
|
||||
var _ref;
|
||||
if (((_ref = g.VIEW) !== 'index' && _ref !== 'thread') || !Conf['Resurrect Quotes'] && g.BOARD.ID !== 'pol') {
|
||||
if (((_ref = g.VIEW) !== 'index' && _ref !== 'thread') || !Conf['Resurrect Quotes']) {
|
||||
return;
|
||||
}
|
||||
if (Conf['Comment Expansion']) {
|
||||
@ -8735,15 +8736,7 @@
|
||||
postID: postID
|
||||
});
|
||||
}
|
||||
} else if ((this.board.ID === boardID && boardID === 'pol') && postID.length === 9 && postID[-2] === postID[-1]) {
|
||||
postID = postID.slice(0, -1);
|
||||
quoteID = "" + boardID + "." + postID;
|
||||
a = $.el('a', {
|
||||
href: Build.postURL(boardID, this.thread.ID, postID),
|
||||
className: 'quotelink',
|
||||
textContent: quote
|
||||
});
|
||||
} else if (Conf['Resurrect Quotes']) {
|
||||
} else {
|
||||
redirect = Redirect.to('thread', {
|
||||
boardID: boardID,
|
||||
threadID: 0,
|
||||
@ -13210,7 +13203,7 @@
|
||||
},
|
||||
subEntries: []
|
||||
};
|
||||
_ref1 = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']];
|
||||
_ref1 = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['Unique ID', 'uniqueID'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']];
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
type = _ref1[_i];
|
||||
entry.subEntries.push(this.createSubEntry(type[0], type[1]));
|
||||
@ -13544,7 +13537,7 @@
|
||||
return Favicon["switch"]();
|
||||
},
|
||||
"switch": function() {
|
||||
var f, funreadDeadY, i, items, t;
|
||||
var f, i, items, t;
|
||||
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=='],
|
||||
'xat-': ['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'],
|
||||
@ -13559,7 +13552,7 @@
|
||||
while (items[i]) {
|
||||
items[i] = t + items[i++];
|
||||
}
|
||||
f.unreadDead = items[0], funreadDeadY = items[1], f.unreadSFW = items[2], f.unreadSFWY = items[3], f.unreadNSFW = items[4], f.unreadNSFWY = items[5];
|
||||
f.unreadDead = items[0], f.unreadDeadY = items[1], f.unreadSFW = items[2], f.unreadSFWY = items[3], f.unreadNSFW = items[4], f.unreadNSFWY = items[5];
|
||||
return f.update();
|
||||
},
|
||||
update: function() {
|
||||
@ -14288,7 +14281,6 @@
|
||||
|
||||
ThreadWatcher = {
|
||||
init: function() {
|
||||
var now;
|
||||
if (!Conf['Thread Watcher']) {
|
||||
return;
|
||||
}
|
||||
@ -14313,12 +14305,7 @@
|
||||
case 'thread':
|
||||
$.on(d, 'ThreadUpdate', this.cb.onThreadRefresh);
|
||||
}
|
||||
now = Date.now();
|
||||
if ((this.db.data.lastChecked || 0) < now - 2 * $.HOUR) {
|
||||
this.db.data.lastChecked = now;
|
||||
ThreadWatcher.fetchAllStatus();
|
||||
this.db.save();
|
||||
}
|
||||
ThreadWatcher.fetchAuto();
|
||||
if (Conf['JSON Navigation'] && Conf['Menu'] && g.BOARD.ID !== 'f') {
|
||||
Menu.menu.addEntry({
|
||||
el: $.el('a', {
|
||||
@ -14516,6 +14503,22 @@
|
||||
fetched: 0,
|
||||
fetching: 0
|
||||
},
|
||||
fetchAuto: function() {
|
||||
var db, interval, now;
|
||||
clearTimeout(ThreadWatcher.timeout);
|
||||
if (!Conf['Auto Update Thread Watcher']) {
|
||||
return;
|
||||
}
|
||||
db = ThreadWatcher.db;
|
||||
interval = Conf['Show Unread Count'] ? 5 * $.MINUTE : 2 * $.HOUR;
|
||||
now = Date.now();
|
||||
if (now >= (db.data.lastChecked || 0) + interval) {
|
||||
db.data.lastChecked = now;
|
||||
ThreadWatcher.fetchAllStatus();
|
||||
db.save();
|
||||
}
|
||||
return ThreadWatcher.timeout = setTimeout(ThreadWatcher.fetchAuto, interval);
|
||||
},
|
||||
fetchAllStatus: function() {
|
||||
var thread, threads, _i, _len;
|
||||
ThreadWatcher.db.forceSync();
|
||||
@ -14529,9 +14532,9 @@
|
||||
ThreadWatcher.fetchStatus(thread);
|
||||
}
|
||||
},
|
||||
fetchStatus: function(_arg) {
|
||||
fetchStatus: function(thread) {
|
||||
var boardID, data, fetchCount, threadID;
|
||||
boardID = _arg.boardID, threadID = _arg.threadID, data = _arg.data;
|
||||
boardID = thread.boardID, threadID = thread.threadID, data = thread.data;
|
||||
if (data.isDead && !Conf['Show Unread Count']) {
|
||||
return;
|
||||
}
|
||||
@ -14543,94 +14546,98 @@
|
||||
fetchCount.fetching++;
|
||||
return $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", {
|
||||
onloadend: function() {
|
||||
var isDead, lastReadPost, match, postObj, quotingYou, regexp, status, unread, _i, _len, _ref, _ref1;
|
||||
fetchCount.fetched++;
|
||||
if (fetchCount.fetched === fetchCount.fetching) {
|
||||
fetchCount.fetched = 0;
|
||||
fetchCount.fetching = 0;
|
||||
status = '';
|
||||
$.rmClass(ThreadWatcher.refreshButton, 'fa-spin');
|
||||
} else {
|
||||
status = "" + (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%";
|
||||
}
|
||||
ThreadWatcher.status.textContent = status;
|
||||
if (this.status === 200 && this.response) {
|
||||
isDead = !!this.response.posts[0].archived;
|
||||
if (isDead && Conf['Auto Prune']) {
|
||||
ThreadWatcher.db["delete"]({
|
||||
boardID: boardID,
|
||||
threadID: threadID
|
||||
});
|
||||
ThreadWatcher.refresh();
|
||||
return;
|
||||
}
|
||||
lastReadPost = ThreadWatcher.unreaddb.get({
|
||||
boardID: boardID,
|
||||
threadID: threadID,
|
||||
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.parseStatus.call(this, thread);
|
||||
}
|
||||
});
|
||||
},
|
||||
parseStatus: function(_arg) {
|
||||
var boardID, data, isDead, lastReadPost, match, postObj, quotingYou, regexp, status, threadID, unread, _i, _len, _ref, _ref1;
|
||||
boardID = _arg.boardID, threadID = _arg.threadID, data = _arg.data;
|
||||
fetchCount.fetched++;
|
||||
if (fetchCount.fetched === fetchCount.fetching) {
|
||||
fetchCount.fetched = 0;
|
||||
fetchCount.fetching = 0;
|
||||
status = '';
|
||||
$.rmClass(ThreadWatcher.refreshButton, 'fa-spin');
|
||||
} else {
|
||||
status = "" + (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%";
|
||||
}
|
||||
ThreadWatcher.status.textContent = status;
|
||||
if (this.status === 200 && this.response) {
|
||||
isDead = !!this.response.posts[0].archived;
|
||||
if (isDead && Conf['Auto Prune']) {
|
||||
ThreadWatcher.db["delete"]({
|
||||
boardID: boardID,
|
||||
threadID: threadID
|
||||
});
|
||||
ThreadWatcher.refresh();
|
||||
return;
|
||||
}
|
||||
lastReadPost = ThreadWatcher.unreaddb.get({
|
||||
boardID: boardID,
|
||||
threadID: threadID,
|
||||
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();
|
||||
}
|
||||
},
|
||||
getAll: function() {
|
||||
var all, boardID, data, threadID, threads, _ref;
|
||||
all = [];
|
||||
@ -14724,6 +14731,7 @@
|
||||
return $[helper[0]](thread.catalogView.nodes.root, 'watched');
|
||||
}
|
||||
});
|
||||
ThreadWatcher.refreshIcon();
|
||||
_ref2 = ThreadWatcher.menu.refreshers;
|
||||
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
||||
refresher = _ref2[_j];
|
||||
@ -14734,6 +14742,14 @@
|
||||
return Index.buildIndex();
|
||||
}
|
||||
},
|
||||
refreshIcon: function() {
|
||||
var className, _i, _len, _ref;
|
||||
_ref = ['replies-unread', 'replies-quoting-you'];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
className = _ref[_i];
|
||||
ThreadWatcher.shortcut.classList.toggle(className, !!$("." + className, ThreadWatcher.dialog));
|
||||
}
|
||||
},
|
||||
update: function(boardID, threadID, newData) {
|
||||
var data, key, line, n, newLine, val, _ref;
|
||||
if (!(data = (_ref = ThreadWatcher.db) != null ? _ref.get({
|
||||
@ -14775,7 +14791,8 @@
|
||||
});
|
||||
if (line = $("#watched-threads > [data-full-i-d='" + boardID + "." + threadID + "']", ThreadWatcher.dialog)) {
|
||||
newLine = ThreadWatcher.makeLine(boardID, threadID, data);
|
||||
return $.replace(line, newLine);
|
||||
$.replace(line, newLine);
|
||||
return ThreadWatcher.refreshIcon();
|
||||
} else {
|
||||
return ThreadWatcher.refresh();
|
||||
}
|
||||
@ -14938,13 +14955,15 @@
|
||||
var entry, input;
|
||||
entry = {
|
||||
type: 'thread watcher',
|
||||
el: UI.checkbox(name, name)
|
||||
el: UI.checkbox(name, " " + (name.replace(' Thread Watcher', '')))
|
||||
};
|
||||
input = entry.el.firstElementChild;
|
||||
$.on(input, 'change', $.cb.checked);
|
||||
if (name === 'Current Board' || name === 'Show Unread Count') {
|
||||
$.on(input, 'change', ThreadWatcher.refresh);
|
||||
}
|
||||
if (name === 'Show Unread Count' || name === 'Auto Update Thread Watcher') {
|
||||
$.on(input, 'change', ThreadWatcher.fetchAuto);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
@ -15352,7 +15371,7 @@
|
||||
search: function(archive, _arg) {
|
||||
var boardID, path, type, value;
|
||||
boardID = _arg.boardID, type = _arg.type, value = _arg.value;
|
||||
type = type === 'name' ? 'username' : type === 'MD5' ? 'image' : type;
|
||||
type = type === 'name' ? 'username' : type === 'uniqueID' ? 'uid' : type === 'MD5' ? 'image' : type;
|
||||
value = encodeURIComponent(value);
|
||||
path = archive.software === 'foolfuuka' ? "" + boardID + "/search/" + type + "/" + value : "" + boardID + "/?task=search2&search_" + (type === 'image' ? 'media_hash' : type) + "=" + value;
|
||||
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path;
|
||||
@ -18909,7 +18928,7 @@
|
||||
var add, check, el, settings;
|
||||
el = $.el('a', {
|
||||
className: 'settings-link',
|
||||
title: 'Appchan X Settings',
|
||||
title: 'appchan x Settings',
|
||||
href: 'javascript:;',
|
||||
textContent: 'Settings'
|
||||
});
|
||||
|
||||
@ -133,7 +133,7 @@
|
||||
'Color User IDs': [true, 'Assign unique colors to user IDs on boards that use them'],
|
||||
'Remove Spoilers': [false, 'Remove all spoilers in text.'],
|
||||
'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'],
|
||||
'Show Support Message': [true, 'Warn if your browser is unsupported. 4chan X may not operate correctly on unsupported browser versions.'],
|
||||
'Show Support Message': [true, 'Warn if your browser is unsupported. appchan x may not operate correctly on unsupported browser versions.'],
|
||||
'Normalize URL': [true, 'Rewrite the URL of the current page, removing stubs and changing /res/ to /thread/.'],
|
||||
'Announcement Hiding': [true, 'Enable announcements to be hidden.']
|
||||
},
|
||||
@ -189,7 +189,7 @@
|
||||
'Unread Favicon': [true, 'Show a different favicon when there are unread posts.'],
|
||||
'Unread Line': [true, 'Show a line to distinguish read posts from unread ones.'],
|
||||
'Scroll to Last Read Post': [true, 'Scroll back to the last read post when reopening a thread.'],
|
||||
'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title if not already present.'],
|
||||
'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title for threads in /f/.'],
|
||||
'Remove Thread Excerpt': [false, 'Replace the excerpt of the thread in the tab title with the board title.'],
|
||||
'Thread Stats': [true, 'Display reply and image count.'],
|
||||
'IP Count in Stats': [true, 'Display the unique IP count in the thread stats.', 1],
|
||||
@ -340,6 +340,7 @@
|
||||
},
|
||||
threadWatcher: {
|
||||
'Current Board': [false, 'Only show watched threads from the current board.'],
|
||||
'Auto Update Thread Watcher': [true, 'Periodically check status of watched threads.'],
|
||||
'Auto Watch': [false, 'Automatically watch threads you start.'],
|
||||
'Auto Watch Reply': [false, 'Automatically watch threads you reply to.'],
|
||||
'Auto Prune': [false, 'Automatically remove dead threads.']
|
||||
@ -8708,7 +8709,7 @@
|
||||
Quotify = {
|
||||
init: function() {
|
||||
var _ref;
|
||||
if (((_ref = g.VIEW) !== 'index' && _ref !== 'thread') || !Conf['Resurrect Quotes'] && g.BOARD.ID !== 'pol') {
|
||||
if (((_ref = g.VIEW) !== 'index' && _ref !== 'thread') || !Conf['Resurrect Quotes']) {
|
||||
return;
|
||||
}
|
||||
if (Conf['Comment Expansion']) {
|
||||
@ -8769,15 +8770,7 @@
|
||||
postID: postID
|
||||
});
|
||||
}
|
||||
} else if ((this.board.ID === boardID && boardID === 'pol') && postID.length === 9 && postID[-2] === postID[-1]) {
|
||||
postID = postID.slice(0, -1);
|
||||
quoteID = "" + boardID + "." + postID;
|
||||
a = $.el('a', {
|
||||
href: Build.postURL(boardID, this.thread.ID, postID),
|
||||
className: 'quotelink',
|
||||
textContent: quote
|
||||
});
|
||||
} else if (Conf['Resurrect Quotes']) {
|
||||
} else {
|
||||
redirect = Redirect.to('thread', {
|
||||
boardID: boardID,
|
||||
threadID: 0,
|
||||
@ -13226,7 +13219,7 @@
|
||||
},
|
||||
subEntries: []
|
||||
};
|
||||
_ref1 = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']];
|
||||
_ref1 = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['Unique ID', 'uniqueID'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']];
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
type = _ref1[_i];
|
||||
entry.subEntries.push(this.createSubEntry(type[0], type[1]));
|
||||
@ -13560,7 +13553,7 @@
|
||||
return Favicon["switch"]();
|
||||
},
|
||||
"switch": function() {
|
||||
var f, funreadDeadY, i, items, t;
|
||||
var f, i, items, t;
|
||||
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=='],
|
||||
'xat-': ['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'],
|
||||
@ -13575,7 +13568,7 @@
|
||||
while (items[i]) {
|
||||
items[i] = t + items[i++];
|
||||
}
|
||||
f.unreadDead = items[0], funreadDeadY = items[1], f.unreadSFW = items[2], f.unreadSFWY = items[3], f.unreadNSFW = items[4], f.unreadNSFWY = items[5];
|
||||
f.unreadDead = items[0], f.unreadDeadY = items[1], f.unreadSFW = items[2], f.unreadSFWY = items[3], f.unreadNSFW = items[4], f.unreadNSFWY = items[5];
|
||||
return f.update();
|
||||
},
|
||||
update: function() {
|
||||
@ -14304,7 +14297,6 @@
|
||||
|
||||
ThreadWatcher = {
|
||||
init: function() {
|
||||
var now;
|
||||
if (!Conf['Thread Watcher']) {
|
||||
return;
|
||||
}
|
||||
@ -14329,12 +14321,7 @@
|
||||
case 'thread':
|
||||
$.on(d, 'ThreadUpdate', this.cb.onThreadRefresh);
|
||||
}
|
||||
now = Date.now();
|
||||
if ((this.db.data.lastChecked || 0) < now - 2 * $.HOUR) {
|
||||
this.db.data.lastChecked = now;
|
||||
ThreadWatcher.fetchAllStatus();
|
||||
this.db.save();
|
||||
}
|
||||
ThreadWatcher.fetchAuto();
|
||||
if (Conf['JSON Navigation'] && Conf['Menu'] && g.BOARD.ID !== 'f') {
|
||||
Menu.menu.addEntry({
|
||||
el: $.el('a', {
|
||||
@ -14532,6 +14519,22 @@
|
||||
fetched: 0,
|
||||
fetching: 0
|
||||
},
|
||||
fetchAuto: function() {
|
||||
var db, interval, now;
|
||||
clearTimeout(ThreadWatcher.timeout);
|
||||
if (!Conf['Auto Update Thread Watcher']) {
|
||||
return;
|
||||
}
|
||||
db = ThreadWatcher.db;
|
||||
interval = Conf['Show Unread Count'] ? 5 * $.MINUTE : 2 * $.HOUR;
|
||||
now = Date.now();
|
||||
if (now >= (db.data.lastChecked || 0) + interval) {
|
||||
db.data.lastChecked = now;
|
||||
ThreadWatcher.fetchAllStatus();
|
||||
db.save();
|
||||
}
|
||||
return ThreadWatcher.timeout = setTimeout(ThreadWatcher.fetchAuto, interval);
|
||||
},
|
||||
fetchAllStatus: function() {
|
||||
var thread, threads, _i, _len;
|
||||
ThreadWatcher.db.forceSync();
|
||||
@ -14545,9 +14548,9 @@
|
||||
ThreadWatcher.fetchStatus(thread);
|
||||
}
|
||||
},
|
||||
fetchStatus: function(_arg) {
|
||||
fetchStatus: function(thread) {
|
||||
var boardID, data, fetchCount, threadID;
|
||||
boardID = _arg.boardID, threadID = _arg.threadID, data = _arg.data;
|
||||
boardID = thread.boardID, threadID = thread.threadID, data = thread.data;
|
||||
if (data.isDead && !Conf['Show Unread Count']) {
|
||||
return;
|
||||
}
|
||||
@ -14559,94 +14562,98 @@
|
||||
fetchCount.fetching++;
|
||||
return $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", {
|
||||
onloadend: function() {
|
||||
var isDead, lastReadPost, match, postObj, quotingYou, regexp, status, unread, _i, _len, _ref, _ref1;
|
||||
fetchCount.fetched++;
|
||||
if (fetchCount.fetched === fetchCount.fetching) {
|
||||
fetchCount.fetched = 0;
|
||||
fetchCount.fetching = 0;
|
||||
status = '';
|
||||
$.rmClass(ThreadWatcher.refreshButton, 'fa-spin');
|
||||
} else {
|
||||
status = "" + (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%";
|
||||
}
|
||||
ThreadWatcher.status.textContent = status;
|
||||
if (this.status === 200 && this.response) {
|
||||
isDead = !!this.response.posts[0].archived;
|
||||
if (isDead && Conf['Auto Prune']) {
|
||||
ThreadWatcher.db["delete"]({
|
||||
boardID: boardID,
|
||||
threadID: threadID
|
||||
});
|
||||
ThreadWatcher.refresh();
|
||||
return;
|
||||
}
|
||||
lastReadPost = ThreadWatcher.unreaddb.get({
|
||||
boardID: boardID,
|
||||
threadID: threadID,
|
||||
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.parseStatus.call(this, thread);
|
||||
}
|
||||
});
|
||||
},
|
||||
parseStatus: function(_arg) {
|
||||
var boardID, data, isDead, lastReadPost, match, postObj, quotingYou, regexp, status, threadID, unread, _i, _len, _ref, _ref1;
|
||||
boardID = _arg.boardID, threadID = _arg.threadID, data = _arg.data;
|
||||
fetchCount.fetched++;
|
||||
if (fetchCount.fetched === fetchCount.fetching) {
|
||||
fetchCount.fetched = 0;
|
||||
fetchCount.fetching = 0;
|
||||
status = '';
|
||||
$.rmClass(ThreadWatcher.refreshButton, 'fa-spin');
|
||||
} else {
|
||||
status = "" + (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%";
|
||||
}
|
||||
ThreadWatcher.status.textContent = status;
|
||||
if (this.status === 200 && this.response) {
|
||||
isDead = !!this.response.posts[0].archived;
|
||||
if (isDead && Conf['Auto Prune']) {
|
||||
ThreadWatcher.db["delete"]({
|
||||
boardID: boardID,
|
||||
threadID: threadID
|
||||
});
|
||||
ThreadWatcher.refresh();
|
||||
return;
|
||||
}
|
||||
lastReadPost = ThreadWatcher.unreaddb.get({
|
||||
boardID: boardID,
|
||||
threadID: threadID,
|
||||
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();
|
||||
}
|
||||
},
|
||||
getAll: function() {
|
||||
var all, boardID, data, threadID, threads, _ref;
|
||||
all = [];
|
||||
@ -14740,6 +14747,7 @@
|
||||
return $[helper[0]](thread.catalogView.nodes.root, 'watched');
|
||||
}
|
||||
});
|
||||
ThreadWatcher.refreshIcon();
|
||||
_ref2 = ThreadWatcher.menu.refreshers;
|
||||
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
||||
refresher = _ref2[_j];
|
||||
@ -14750,6 +14758,14 @@
|
||||
return Index.buildIndex();
|
||||
}
|
||||
},
|
||||
refreshIcon: function() {
|
||||
var className, _i, _len, _ref;
|
||||
_ref = ['replies-unread', 'replies-quoting-you'];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
className = _ref[_i];
|
||||
ThreadWatcher.shortcut.classList.toggle(className, !!$("." + className, ThreadWatcher.dialog));
|
||||
}
|
||||
},
|
||||
update: function(boardID, threadID, newData) {
|
||||
var data, key, line, n, newLine, val, _ref;
|
||||
if (!(data = (_ref = ThreadWatcher.db) != null ? _ref.get({
|
||||
@ -14791,7 +14807,8 @@
|
||||
});
|
||||
if (line = $("#watched-threads > [data-full-i-d='" + boardID + "." + threadID + "']", ThreadWatcher.dialog)) {
|
||||
newLine = ThreadWatcher.makeLine(boardID, threadID, data);
|
||||
return $.replace(line, newLine);
|
||||
$.replace(line, newLine);
|
||||
return ThreadWatcher.refreshIcon();
|
||||
} else {
|
||||
return ThreadWatcher.refresh();
|
||||
}
|
||||
@ -14954,13 +14971,15 @@
|
||||
var entry, input;
|
||||
entry = {
|
||||
type: 'thread watcher',
|
||||
el: UI.checkbox(name, name)
|
||||
el: UI.checkbox(name, " " + (name.replace(' Thread Watcher', '')))
|
||||
};
|
||||
input = entry.el.firstElementChild;
|
||||
$.on(input, 'change', $.cb.checked);
|
||||
if (name === 'Current Board' || name === 'Show Unread Count') {
|
||||
$.on(input, 'change', ThreadWatcher.refresh);
|
||||
}
|
||||
if (name === 'Show Unread Count' || name === 'Auto Update Thread Watcher') {
|
||||
$.on(input, 'change', ThreadWatcher.fetchAuto);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
@ -15367,7 +15386,7 @@
|
||||
search: function(archive, _arg) {
|
||||
var boardID, path, type, value;
|
||||
boardID = _arg.boardID, type = _arg.type, value = _arg.value;
|
||||
type = type === 'name' ? 'username' : type === 'MD5' ? 'image' : type;
|
||||
type = type === 'name' ? 'username' : type === 'uniqueID' ? 'uid' : type === 'MD5' ? 'image' : type;
|
||||
value = encodeURIComponent(value);
|
||||
path = archive.software === 'foolfuuka' ? "" + boardID + "/search/" + type + "/" + value : "" + boardID + "/?task=search2&search_" + (type === 'image' ? 'media_hash' : type) + "=" + value;
|
||||
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path;
|
||||
@ -18935,7 +18954,7 @@
|
||||
var add, check, el, settings;
|
||||
el = $.el('a', {
|
||||
className: 'settings-link',
|
||||
title: 'Appchan X Settings',
|
||||
title: 'appchan x Settings',
|
||||
href: 'javascript:;',
|
||||
textContent: 'Settings'
|
||||
});
|
||||
|
||||
@ -69,6 +69,8 @@ Redirect =
|
||||
search: (archive, {boardID, type, value}) ->
|
||||
type = if type is 'name'
|
||||
'username'
|
||||
else if type is 'uniqueID'
|
||||
'uid'
|
||||
else if type is 'MD5'
|
||||
'image'
|
||||
else
|
||||
|
||||
@ -84,7 +84,7 @@ Config =
|
||||
]
|
||||
'Show Support Message': [
|
||||
true
|
||||
'Warn if your browser is unsupported. 4chan X may not operate correctly on unsupported browser versions.'
|
||||
'Warn if your browser is unsupported. <%= meta.name %> may not operate correctly on unsupported browser versions.'
|
||||
]
|
||||
'Normalize URL': [
|
||||
true
|
||||
@ -291,7 +291,7 @@ Config =
|
||||
]
|
||||
'Thread Excerpt': [
|
||||
true
|
||||
'Show an excerpt of the thread in the tab title if not already present.'
|
||||
'Show an excerpt of the thread in the tab title for threads in /f/.'
|
||||
]
|
||||
'Remove Thread Excerpt': [
|
||||
false
|
||||
@ -856,6 +856,10 @@ Config =
|
||||
false
|
||||
'Only show watched threads from the current board.'
|
||||
]
|
||||
'Auto Update Thread Watcher': [
|
||||
true
|
||||
'Periodically check status of watched threads.'
|
||||
]
|
||||
'Auto Watch': [
|
||||
false
|
||||
'Automatically watch threads you start.'
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
Settings =
|
||||
init: ->
|
||||
# Appchan X settings link
|
||||
# Settings link
|
||||
el = $.el 'a',
|
||||
className: 'settings-link'
|
||||
title: 'Appchan X Settings'
|
||||
title: '<%= meta.name %> Settings'
|
||||
href: 'javascript:;'
|
||||
textContent: 'Settings'
|
||||
$.on el, 'click', @open
|
||||
|
||||
@ -16,6 +16,7 @@ ArchiveLink =
|
||||
['Post', 'post']
|
||||
['Name', 'name']
|
||||
['Tripcode', 'tripcode']
|
||||
['Unique ID', 'uniqueID']
|
||||
['Subject', 'subject']
|
||||
['Filename', 'filename']
|
||||
['Image MD5', 'MD5']
|
||||
|
||||
@ -67,7 +67,7 @@ Favicon =
|
||||
while items[i]
|
||||
items[i] = t + items[i++]
|
||||
|
||||
[f.unreadDead, funreadDeadY, f.unreadSFW, f.unreadSFWY, f.unreadNSFW, f.unreadNSFWY] = items
|
||||
[f.unreadDead, f.unreadDeadY, f.unreadSFW, f.unreadSFWY, f.unreadNSFW, f.unreadNSFWY] = items
|
||||
f.update()
|
||||
|
||||
update: ->
|
||||
|
||||
@ -22,11 +22,7 @@ ThreadWatcher =
|
||||
when 'thread'
|
||||
$.on d, 'ThreadUpdate', @cb.onThreadRefresh
|
||||
|
||||
now = Date.now()
|
||||
if (@db.data.lastChecked or 0) < now - 2 * $.HOUR
|
||||
@db.data.lastChecked = now
|
||||
ThreadWatcher.fetchAllStatus()
|
||||
@db.save()
|
||||
ThreadWatcher.fetchAuto()
|
||||
|
||||
if Conf['JSON Navigation'] and Conf['Menu'] and g.BOARD.ID isnt 'f'
|
||||
Menu.menu.addEntry
|
||||
@ -143,6 +139,17 @@ ThreadWatcher =
|
||||
fetchCount:
|
||||
fetched: 0
|
||||
fetching: 0
|
||||
fetchAuto: ->
|
||||
clearTimeout ThreadWatcher.timeout
|
||||
return unless Conf['Auto Update Thread Watcher']
|
||||
{db} = ThreadWatcher
|
||||
interval = if Conf['Show Unread Count'] then 5 * $.MINUTE else 2 * $.HOUR
|
||||
now = Date.now()
|
||||
if now >= (db.data.lastChecked or 0) + interval
|
||||
db.data.lastChecked = now
|
||||
ThreadWatcher.fetchAllStatus()
|
||||
db.save()
|
||||
ThreadWatcher.timeout = setTimeout ThreadWatcher.fetchAuto, interval
|
||||
fetchAllStatus: ->
|
||||
ThreadWatcher.db.forceSync()
|
||||
ThreadWatcher.unreaddb.forceSync()
|
||||
@ -151,7 +158,8 @@ ThreadWatcher =
|
||||
for thread in threads
|
||||
ThreadWatcher.fetchStatus thread
|
||||
return
|
||||
fetchStatus: ({boardID, threadID, data}) ->
|
||||
fetchStatus: (thread) ->
|
||||
{boardID, threadID, data} = thread
|
||||
return if data.isDead and !Conf['Show Unread Count']
|
||||
{fetchCount} = ThreadWatcher
|
||||
if fetchCount.fetching is 0
|
||||
@ -160,61 +168,64 @@ ThreadWatcher =
|
||||
fetchCount.fetching++
|
||||
$.ajax "//a.4cdn.org/#{boardID}/thread/#{threadID}.json",
|
||||
onloadend: ->
|
||||
fetchCount.fetched++
|
||||
if fetchCount.fetched is fetchCount.fetching
|
||||
fetchCount.fetched = 0
|
||||
fetchCount.fetching = 0
|
||||
status = ''
|
||||
$.rmClass ThreadWatcher.refreshButton, 'fa-spin'
|
||||
else
|
||||
status = "#{Math.round fetchCount.fetched / fetchCount.fetching * 100}%"
|
||||
ThreadWatcher.status.textContent = status
|
||||
ThreadWatcher.parseStatus.call @, thread
|
||||
|
||||
if @status is 200 and @response
|
||||
isDead = !!@response.posts[0].archived
|
||||
if isDead and Conf['Auto Prune']
|
||||
ThreadWatcher.db.delete {boardID, threadID}
|
||||
ThreadWatcher.refresh()
|
||||
return
|
||||
parseStatus: ({boardID, threadID, data}) ->
|
||||
fetchCount.fetched++
|
||||
if fetchCount.fetched is fetchCount.fetching
|
||||
fetchCount.fetched = 0
|
||||
fetchCount.fetching = 0
|
||||
status = ''
|
||||
$.rmClass ThreadWatcher.refreshButton, 'fa-spin'
|
||||
else
|
||||
status = "#{Math.round fetchCount.fetched / fetchCount.fetching * 100}%"
|
||||
ThreadWatcher.status.textContent = status
|
||||
|
||||
lastReadPost = ThreadWatcher.unreaddb.get
|
||||
boardID: boardID
|
||||
threadID: threadID
|
||||
defaultValue: 0
|
||||
if @status is 200 and @response
|
||||
isDead = !!@response.posts[0].archived
|
||||
if isDead and Conf['Auto Prune']
|
||||
ThreadWatcher.db.delete {boardID, threadID}
|
||||
ThreadWatcher.refresh()
|
||||
return
|
||||
|
||||
unread = quotingYou = 0
|
||||
lastReadPost = ThreadWatcher.unreaddb.get
|
||||
boardID: boardID
|
||||
threadID: threadID
|
||||
defaultValue: 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
|
||||
unread = quotingYou = 0
|
||||
|
||||
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()
|
||||
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
|
||||
|
||||
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()
|
||||
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: ->
|
||||
all = []
|
||||
@ -259,6 +270,7 @@ ThreadWatcher =
|
||||
$.addClass div, 'replies-quoting-you' if data.quotingYou
|
||||
$.add div, [x, $.tn(' '), link]
|
||||
div
|
||||
|
||||
refresh: ->
|
||||
nodes = []
|
||||
for {boardID, threadID, data} in ThreadWatcher.getAll()
|
||||
@ -277,6 +289,8 @@ ThreadWatcher =
|
||||
toggler.title = "#{helper[1]} Thread"
|
||||
$[helper[0]] thread.catalogView.nodes.root, 'watched' if thread.catalogView
|
||||
|
||||
ThreadWatcher.refreshIcon()
|
||||
|
||||
for refresher in ThreadWatcher.menu.refreshers
|
||||
refresher()
|
||||
|
||||
@ -284,6 +298,11 @@ ThreadWatcher =
|
||||
Index.sort()
|
||||
Index.buildIndex()
|
||||
|
||||
refreshIcon: ->
|
||||
for className in ['replies-unread', 'replies-quoting-you']
|
||||
ThreadWatcher.shortcut.classList.toggle className, !!$(".#{className}", ThreadWatcher.dialog)
|
||||
return
|
||||
|
||||
update: (boardID, threadID, newData) ->
|
||||
return unless data = ThreadWatcher.db?.get {boardID, threadID}
|
||||
if newData.isDead and Conf['Auto Prune']
|
||||
@ -300,6 +319,7 @@ ThreadWatcher =
|
||||
if line = $ "#watched-threads > [data-full-i-d='#{boardID}.#{threadID}']", ThreadWatcher.dialog
|
||||
newLine = ThreadWatcher.makeLine boardID, threadID, data
|
||||
$.replace line, newLine
|
||||
ThreadWatcher.refreshIcon()
|
||||
else
|
||||
ThreadWatcher.refresh()
|
||||
|
||||
@ -310,6 +330,7 @@ ThreadWatcher =
|
||||
ThreadWatcher.rm boardID, threadID
|
||||
else
|
||||
ThreadWatcher.add thread
|
||||
|
||||
add: (thread) ->
|
||||
data = {}
|
||||
boardID = thread.board.ID
|
||||
@ -324,6 +345,7 @@ ThreadWatcher =
|
||||
ThreadWatcher.refresh()
|
||||
if Conf['Show Unread Count']
|
||||
ThreadWatcher.fetchStatus {boardID, threadID, data}
|
||||
|
||||
rm: (boardID, threadID) ->
|
||||
ThreadWatcher.db.delete {boardID, threadID}
|
||||
ThreadWatcher.refresh()
|
||||
@ -401,8 +423,8 @@ ThreadWatcher =
|
||||
createSubEntry: (name, desc) ->
|
||||
entry =
|
||||
type: 'thread watcher'
|
||||
el: UI.checkbox name, name
|
||||
el: UI.checkbox name, " #{name.replace ' Thread Watcher', ''}"
|
||||
input = entry.el.firstElementChild
|
||||
$.on input, 'change', $.cb.checked
|
||||
$.on input, 'change', ThreadWatcher.refresh if name in ['Current Board', 'Show Unread Count']
|
||||
$.on input, 'change', ThreadWatcher.refresh if name in ['Current Board', 'Show Unread Count']
|
||||
$.on input, 'change', ThreadWatcher.fetchAuto if name in ['Show Unread Count', 'Auto Update Thread Watcher']
|
||||
entry
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
Quotify =
|
||||
init: ->
|
||||
return if g.VIEW not in ['index', 'thread'] or !Conf['Resurrect Quotes'] and g.BOARD.ID isnt 'pol'
|
||||
return if g.VIEW not in ['index', 'thread'] or !Conf['Resurrect Quotes']
|
||||
|
||||
if Conf['Comment Expansion']
|
||||
ExpandComment.callbacks.push @node
|
||||
@ -57,16 +57,7 @@ Quotify =
|
||||
textContent: "#{quote}\u00A0(Dead)"
|
||||
$.extend a.dataset, {boardID, threadID: post.thread.ID, postID}
|
||||
|
||||
else if @board.ID is boardID is 'pol' and postID.length is 9 and postID[-2] is postID[-1]
|
||||
# XXX Misquotes due to fake doubles on /pol/. Assume they are all intra-thread.
|
||||
postID = postID[...-1]
|
||||
quoteID = "#{boardID}.#{postID}"
|
||||
a = $.el 'a',
|
||||
href: Build.postURL boardID, @thread.ID, postID
|
||||
className: 'quotelink'
|
||||
textContent: quote
|
||||
|
||||
else if Conf['Resurrect Quotes']
|
||||
else
|
||||
redirect = Redirect.to 'thread', {boardID, threadID: 0, postID}
|
||||
fetchable = Redirect.to 'post', {boardID, postID}
|
||||
if redirect or fetchable
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user