From 20d936c1ca64d35baad981fcdab2635ed2b9a7b0 Mon Sep 17 00:00:00 2001 From: Zixaphir Date: Wed, 14 Jan 2015 08:07:40 -0700 Subject: [PATCH] Update to ccd0/4chan-x@31c262e531a --- builds/appchan-x.user.js | 247 +++++++++++++++------------- builds/crx/script.js | 247 +++++++++++++++------------- src/Archive/Redirect.coffee | 2 + src/General/Config.coffee | 8 +- src/General/Settings.coffee | 4 +- src/Menu/ArchiveLink.coffee | 1 + src/Monitoring/Favicon.coffee | 2 +- src/Monitoring/ThreadWatcher.coffee | 138 +++++++++------- src/Quotelinks/Quotify.coffee | 13 +- 9 files changed, 360 insertions(+), 302 deletions(-) diff --git a/builds/appchan-x.user.js b/builds/appchan-x.user.js index 052fea54b..b87513fcb 100644 --- a/builds/appchan-x.user.js +++ b/builds/appchan-x.user.js @@ -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 = /]*\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 = /]*\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' }); diff --git a/builds/crx/script.js b/builds/crx/script.js index dc1abee12..b70e44c9d 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -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 = /]*\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 = /]*\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' }); diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee index e7f360b6e..f7f5b1703 100755 --- a/src/Archive/Redirect.coffee +++ b/src/Archive/Redirect.coffee @@ -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 diff --git a/src/General/Config.coffee b/src/General/Config.coffee index 1b78501e3..9b78790b4 100644 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -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.' diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index 777674112..386df8e8b 100755 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -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 diff --git a/src/Menu/ArchiveLink.coffee b/src/Menu/ArchiveLink.coffee index 3f8a55eb4..f537d70c1 100755 --- a/src/Menu/ArchiveLink.coffee +++ b/src/Menu/ArchiveLink.coffee @@ -16,6 +16,7 @@ ArchiveLink = ['Post', 'post'] ['Name', 'name'] ['Tripcode', 'tripcode'] + ['Unique ID', 'uniqueID'] ['Subject', 'subject'] ['Filename', 'filename'] ['Image MD5', 'MD5'] diff --git a/src/Monitoring/Favicon.coffee b/src/Monitoring/Favicon.coffee index 28db26bc1..efcb055c1 100755 --- a/src/Monitoring/Favicon.coffee +++ b/src/Monitoring/Favicon.coffee @@ -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: -> diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee index afd8258d8..59b606ae9 100755 --- a/src/Monitoring/ThreadWatcher.coffee +++ b/src/Monitoring/ThreadWatcher.coffee @@ -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 = /]*\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 = /]*\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 diff --git a/src/Quotelinks/Quotify.coffee b/src/Quotelinks/Quotify.coffee index 820e5abe3..ff4be4a31 100755 --- a/src/Quotelinks/Quotify.coffee +++ b/src/Quotelinks/Quotify.coffee @@ -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