From cec8bf186e1c210ddf939bdfaa7d6e4958301191 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Sun, 25 Aug 2013 00:17:42 +0200 Subject: [PATCH 01/39] Fix #1262. --- src/General/Get.coffee | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/General/Get.coffee b/src/General/Get.coffee index eba405cae..d19e8db11 100644 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -118,17 +118,18 @@ Get = Build.spoilerRange[boardID] = posts[0].custom_spoiler for post in posts break if post.no is postID # we found it! - if post.no > postID - # The post can be deleted by the time we check a quote. - if url = Redirect.to 'post', {boardID, postID} - $.cache url, - -> Get.archivedPost @, boardID, postID, root, context - , - withCredentials: url.archive.withCredentials - else - $.addClass root, 'warning' - root.textContent = "Post No.#{postID} was not found." - return + + if post.no isnt postID + # The post can be deleted by the time we check a quote. + if url = Redirect.to 'post', {boardID, postID} + $.cache url, + -> Get.archivedPost @, boardID, postID, root, context + , + withCredentials: url.archive.withCredentials + else + $.addClass root, 'warning' + root.textContent = "Post No.#{postID} was not found." + return board = g.boards[boardID] or new Board boardID From 386888e4d14ee644b241d19578c77b1e2ed396ac Mon Sep 17 00:00:00 2001 From: Mayhem Date: Sun, 25 Aug 2013 22:18:17 +0200 Subject: [PATCH 02/39] Tiny linkifier fix. --- src/Linkification/Linkify.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 5136cdfce..caaedd069 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -26,7 +26,7 @@ Linkify = Linkify.cleanLink anchor, link if Conf['Clean Links'] walker.currentNode = anchor.lastChild else - walker.currentNode = boundaries.endNode + walker.previousNode() range.detach() find: (link, walker) -> From 5c36d646a5267098d98b404d4269940c020a6533 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Mon, 26 Aug 2013 21:31:30 +0200 Subject: [PATCH 03/39] Start desktop notification closing timeout only after they appeared. --- src/Monitoring/Unread.coffee | 7 ++++--- src/Posting/QR.coffee | 16 +++++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index fdcbc7f16..6ccdf0244 100644 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -104,9 +104,10 @@ Unread = notif.onclick = -> Header.scrollToPost post.nodes.root window.focus() - setTimeout -> - notif.close() - , 7 * $.SECOND + notif.onshow = -> + setTimeout -> + notif.close() + , 7 * $.SECOND onUpdate: (e) -> if e.detail[404] diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index cad8ea4b4..2091d9045 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -122,10 +122,11 @@ QR = # Firefox automatically closes notifications # so we can't control the onclose properly. notif.onclose = -> notice.close() - setTimeout -> - notif.onclose = null - notif.close() - , 7 * $.SECOND + notif.onshow = -> + setTimeout -> + notif.onclose = null + notif.close() + , 7 * $.SECOND <% } %> notifications: [] cleanNotifications: -> @@ -1108,9 +1109,10 @@ QR = QR.open() QR.captcha.nodes.input.focus() window.focus() - setTimeout -> - notif.close() - , 7 * $.SECOND + notif.onshow = -> + setTimeout -> + notif.close() + , 7 * $.SECOND unless Conf['Persistent QR'] or QR.cooldown.auto QR.close() From 44bde8781f54635d8bbcf9a6b0f572941d97ac4e Mon Sep 17 00:00:00 2001 From: Mayhem Date: Sun, 18 Aug 2013 19:04:05 +0200 Subject: [PATCH 04/39] Drop support for prefixed flexbox. --- css/style.css | 31 ------------------------------- src/General/UI.coffee | 6 ++---- src/Meta/manifest.json | 2 +- 3 files changed, 3 insertions(+), 36 deletions(-) diff --git a/css/style.css b/css/style.css index 63ad3c85a..c2646837c 100644 --- a/css/style.css +++ b/css/style.css @@ -16,7 +16,6 @@ margin: 0; padding: 2px 4px 3px; outline: none; - transition: color .25s, border-color .25s, -webkit-flex .25s; transition: color .25s, border-color .25s, flex .25s; } .field::-moz-placeholder, @@ -118,7 +117,6 @@ a[href="javascript:;"] { } #header-bar { border-width: 0; - display: -webkit-flex; display: flex; padding: 3px; position: relative; @@ -132,9 +130,7 @@ a[href="javascript:;"] { border-top-width: 1px; } #board-list { - -webkit-flex: 1; flex: 1; - -webkit-align-self: center; align-self: center; text-align: center; } @@ -245,7 +241,6 @@ a[href="javascript:;"] { } #overlay { background-color: rgba(0, 0, 0, .5); - display: -webkit-flex; display: flex; position: fixed; top: 0; @@ -263,13 +258,10 @@ a[href="javascript:;"] { max-width: 100%; margin: auto; padding: 3px; - display: -webkit-flex; display: flex; - -webkit-flex-direction: column; flex-direction: column; } #fourchanx-settings > nav { - display: -webkit-flex; display: flex; padding: 2px 2px 0; } @@ -281,14 +273,12 @@ a[href="javascript:;"] { padding: 2px; } .sections-list { - -webkit-flex: 1; flex: 1; } .tab-selected { font-weight: 700; } .section-container { - -webkit-flex: 1; flex: 1; position: relative; } @@ -419,13 +409,10 @@ a.hide-announcement { position: absolute; } #thread-watcher > div:first-child { - display: -webkit-flex; display: flex; - -webkit-align-items: center; align-items: center; } #thread-watcher .move { - -webkit-flex: 1; flex: 1; } #watcher-status:not(:empty)::before { @@ -587,15 +574,11 @@ a.hide-announcement { } #qr > div { min-width: 300px; - display: -webkit-flex; display: flex; - -webkit-align-items: center; align-items: center; } #qr .move { - -webkit-align-self: stretch; align-self: stretch; - -webkit-flex: 1; flex: 1; } #qr select { @@ -615,22 +598,17 @@ a.hide-announcement { padding: 0 3px; } #qr > form { - display: -webkit-flex; display: flex; - -webkit-flex-direction: column; flex-direction: column; } .persona { - display: -webkit-flex; display: flex; } .persona .field { - -webkit-flex: 1; flex: 1; } .persona .field:hover, .persona .field:focus { - -webkit-flex: 3; flex: 3; } #dump-button { @@ -807,9 +785,7 @@ a.hide-announcement { right: 0; bottom: 0; left: 0; - display: -webkit-flex; display: flex; - -webkit-align-items: center; align-items: center; } #file-n-submit-container input[type='file'] { @@ -821,7 +797,6 @@ a.hide-announcement { padding: 0; } #file-n-submit input[type='submit'] { - -webkit-order: 1; order: 1; } #file-n-submit.has-file #qr-no-file, @@ -841,7 +816,6 @@ a.hide-announcement { } #qr-no-file { cursor: default; - -webkit-flex: 1; flex: 1; } #qr-filename { @@ -852,7 +826,6 @@ a.hide-announcement { border: none !important; color: inherit; font: inherit; - -webkit-flex: 1; flex: 1; text-overflow: ellipsis; } @@ -888,10 +861,8 @@ a.hide-announcement { } #menu { border-bottom: 0; - display: -webkit-flex; display: flex; margin: 2px 0; - -webkit-flex-direction: column; flex-direction: column; position: absolute; outline: none; @@ -937,9 +908,7 @@ a.hide-announcement { } .submenu { border-bottom: 0; - display: -webkit-flex; display: flex; - -webkit-flex-direction: column; flex-direction: column; position: absolute; margin: -1px 0; diff --git a/src/General/UI.coffee b/src/General/UI.coffee index c2ad0d09d..dff2be93f 100644 --- a/src/General/UI.coffee +++ b/src/General/UI.coffee @@ -105,8 +105,7 @@ UI = do -> findNextEntry: (entry, direction) -> entries = [entry.parentNode.children...] - entries.sort (first, second) -> - +(first.style.order or first.style.webkitOrder) - +(second.style.order or second.style.webkitOrder) + entries.sort (first, second) -> first.style.order - second.style.order entries[entries.indexOf(entry) + direction] keybinds: (e) => @@ -179,8 +178,7 @@ UI = do -> e.stopPropagation() @focus el ).bind @ - {style} = el - style.webkitOrder = style.order = entry.order or 100 + el.style.order = entry.order or 100 return unless subEntries $.addClass el, 'has-submenu' for subEntry in subEntries diff --git a/src/Meta/manifest.json b/src/Meta/manifest.json index 53039fd37..b7c607807 100644 --- a/src/Meta/manifest.json +++ b/src/Meta/manifest.json @@ -15,7 +15,7 @@ "run_at": "document_start" }], "homepage_url": "<%= meta.page %>", - "minimum_chrome_version": "27", + "minimum_chrome_version": "29", "permissions": [ "storage" ] From a1efe855e24d97f02e41c89cdc853ebaba599fa8 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Wed, 28 Aug 2013 00:12:36 +0200 Subject: [PATCH 05/39] Make use of xhr.timeout. --- src/Monitoring/ThreadUpdater.coffee | 48 ++++++++++++++++------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index fa3d81963..206401e3c 100644 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -56,7 +56,7 @@ ThreadUpdater = online: -> if ThreadUpdater.online = navigator.onLine ThreadUpdater.outdateCount = 0 - ThreadUpdater.set 'timer', ThreadUpdater.getInterval() + ThreadUpdater.setInterval() ThreadUpdater.update() if ThreadUpdater.isUpdating ThreadUpdater.set 'status', null, null else @@ -72,7 +72,7 @@ ThreadUpdater = # Reset the counter when we focus this tab. ThreadUpdater.outdateCount = 0 if ThreadUpdater.seconds > ThreadUpdater.interval - ThreadUpdater.set 'timer', ThreadUpdater.getInterval() + ThreadUpdater.setInterval() scrollBG: -> ThreadUpdater.scrollBG = if Conf['Scroll BG'] -> true @@ -81,32 +81,38 @@ ThreadUpdater = autoUpdate: (e) -> ThreadUpdater.isUpdating = @checked if e if ThreadUpdater.isUpdating and ThreadUpdater.online - ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000 + ThreadUpdater.timeout() else clearTimeout ThreadUpdater.timeoutID interval: (e) -> val = Math.max 5, parseInt @value, 10 ThreadUpdater.interval = @value = val $.cb.value.call @ if e - load: -> + load: (e) -> {req} = ThreadUpdater + if e.type isnt 'loadend' # timeout or abort + req.onloadend = null + delete ThreadUpdater.req + if e.type is 'timeout' + ThreadUpdater.set 'status', 'Retrying', null + ThreadUpdater.update() + return switch req.status when 200 g.DEAD = false ThreadUpdater.parse JSON.parse(req.response).posts - ThreadUpdater.set 'timer', ThreadUpdater.getInterval() + ThreadUpdater.setInterval() when 404 g.DEAD = true ThreadUpdater.set 'timer', null ThreadUpdater.set 'status', '404', 'warning' - clearTimeout ThreadUpdater.timeoutID ThreadUpdater.thread.kill() $.event 'ThreadUpdate', 404: true thread: ThreadUpdater.thread else ThreadUpdater.outdateCount++ - ThreadUpdater.set 'timer', ThreadUpdater.getInterval() + ThreadUpdater.setInterval() [text, klass] = if req.status is 304 [null, null] else @@ -114,13 +120,16 @@ ThreadUpdater = ThreadUpdater.set 'status', text, klass delete ThreadUpdater.req - getInterval: -> + setInterval: -> i = ThreadUpdater.interval j = Math.min ThreadUpdater.outdateCount, 10 unless d.hidden # Lower the max refresh rate limit on visible tabs. j = Math.min j, 7 ThreadUpdater.seconds = Math.max i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j] + ThreadUpdater.set 'timer', ThreadUpdater.seconds + clearTimeout ThreadUpdater.timeoutID + ThreadUpdater.timeout() set: (name, text, klass) -> el = ThreadUpdater[name] @@ -134,24 +143,21 @@ ThreadUpdater = timeout: -> ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000 - unless n = --ThreadUpdater.seconds - ThreadUpdater.update() - else if n <= -60 - ThreadUpdater.set 'status', 'Retrying', null - ThreadUpdater.update() - else if n > 0 - ThreadUpdater.set 'timer', n + ThreadUpdater.set 'timer', --ThreadUpdater.seconds + ThreadUpdater.update() if ThreadUpdater.seconds <= 0 update: -> return unless ThreadUpdater.online - ThreadUpdater.seconds = 0 + clearTimeout ThreadUpdater.timeoutID ThreadUpdater.set 'timer', '...' - if ThreadUpdater.req - # abort() triggers onloadend, we don't want that. - ThreadUpdater.req.onloadend = null - ThreadUpdater.req.abort() + ThreadUpdater.req.abort() if ThreadUpdater.req url = "//api.4chan.org/#{ThreadUpdater.thread.board}/res/#{ThreadUpdater.thread}.json" - ThreadUpdater.req = $.ajax url, onloadend: ThreadUpdater.cb.load, + ThreadUpdater.req = $.ajax url, + onabort: ThreadUpdater.cb.load + onloadend: ThreadUpdater.cb.load + ontimeout: ThreadUpdater.cb.load + timeout: $.MINUTE + , whenModified: true updateThreadStatus: (title, OP) -> From 455778f865be3ecdce872a243670e6190d5fd700 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Wed, 28 Aug 2013 20:32:58 +0200 Subject: [PATCH 06/39] Fix image contraction not always moving us back to the post. --- src/Images/ImageExpand.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index b9153461f..a64b75458 100644 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -57,7 +57,6 @@ ImageExpand = unless post.file.isExpanded or $.hasClass thumb, 'expanding' ImageExpand.expand post return - ImageExpand.contract post # Scroll back to the thumbnail when contracting the image # to avoid being left miles away from the relevant post. @@ -70,6 +69,7 @@ ImageExpand = if rect.left < 0 x = -window.scrollX window.scrollBy x, y if x or y + ImageExpand.contract post contract: (post) -> $.rmClass post.nodes.root, 'expanded-image' From 4742e7dac6c5d77cbbc0b738b47277a2050a5734 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Wed, 28 Aug 2013 20:38:32 +0200 Subject: [PATCH 07/39] Fix the QR showing incorrect file data when opening multiple files at once. --- src/Posting/QR.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 2091d9045..b13b83f15 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -586,7 +586,7 @@ QR = @filesize = $.bytesToString file.size @nodes.label.hidden = false if QR.spoiler URL.revokeObjectURL @URL - @showFileData() + @showFileData() if @ is QR.selected unless /^image/.test file.type @nodes.el.style.backgroundImage = null return From 0e3222312cf2d767cf697f4022650f88fab07eff Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 29 Aug 2013 16:18:52 +0200 Subject: [PATCH 08/39] Remove a Chrome bug notice as it didn't seem to actually work. --- src/General/Main.coffee | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/General/Main.coffee b/src/General/Main.coffee index afcdbe910..4ba842fc2 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -32,18 +32,6 @@ Main = Conf['archives'] = Redirect.archives $.get Conf, (items) -> $.extend Conf, items - <% if (type === 'crx') { %> - unless items - new Notice 'error', $.el 'span', - innerHTML: """ - It seems like your <%= meta.name %> settings became corrupted due to a Chrome bug.
- Unfortunately, you'll have to fix it yourself. - """ - # Track resolution of this bug. - Main.logError - message: 'Chrome Storage API bug' - error: new Error '~' - <% } %> Main.initFeatures() $.on d, '4chanMainInit', Main.initStyle From fde52b440245cee92354598038d6d1cf08a44540 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Fri, 30 Aug 2013 22:19:27 +0200 Subject: [PATCH 09/39] Uncomment that for xtra stability. --- src/Linkification/Linkify.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index caaedd069..db0e22bce 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -16,7 +16,7 @@ Linkify = range = d.createRange() for link in links boundaries = Linkify.find link, walker - # break unless boundaries + continue unless boundaries anchor = Linkify.createLink link if Linkify.surround anchor, range, boundaries if (parent = anchor.parentNode).href is anchor.href @@ -35,7 +35,7 @@ Linkify = while node = walker.nextNode() text += node.data break if text.indexOf(link) > -1 - # return unless node + return unless node startNode = endNode = node # Walk backwards to find the startNode. From f28ea1833a451ed9dd6d35271ecec020c29bf0da Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 5 Sep 2013 18:05:22 +0200 Subject: [PATCH 10/39] Deconstruction of a URL-matching regular expression. --- src/Linkification/Linkify.coffee | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index db0e22bce..e81b79722 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -4,7 +4,33 @@ Linkify = # gruber revised + magnet support # http://df4.us/fv9 - @catchAll = /\b([a-z][\w-]+:(\/{1,3}|[a-z0-9%]|\?(dn|x[lts]|as|kt|mt|tr)=)|www\d{0,3}\.|[a-z0-9.\-]+\.[a-z]{2,4}\/)([^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’])/g + @catchAll = /// + \b + ( + [a-z][\w-]+: # URL protocol and colon + ( + /{1,3} # 1-3 slashes + | # or + [a-z0-9%] # Single letter or digit or '%' + | # or + \?(dn|x[lts]|as|kt|mt|tr)= # Magnet parameters + ) + | # or + www\d{0,3}[.] # "www.", "www1.", "www2." … "www999." + | # or + [a-z0-9.\-]+[.][a-z]{2,4}/ # looks like domain name followed by a slash + ) + ( # One or more: + [^\s()<>]+ # Run of non-space, non-()<> + | # or + \(([^\s()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels + )+ + ( # End with: + \(([^\s()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels + | # or + [^\s`!()\[\]{};:'".,<>?«»“”‘’] # not a space or one of these punct chars + ) + ///g Post::callbacks.push name: 'Linkify' From 2ee7a064cae5338694cd3274328789258b4ec730 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 5 Sep 2013 18:20:25 +0200 Subject: [PATCH 11/39] Only allow specific protocols to avoid matching random stuff like fma:brotherhood. --- src/Linkification/Linkify.coffee | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index e81b79722..85617b39d 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -7,18 +7,25 @@ Linkify = @catchAll = /// \b ( - [a-z][\w-]+: # URL protocol and colon + ( # Allowed URL protocols and colon + https? + | ftps? + | about + | bitcoin + | git + | irc[s6]? + ): ( - /{1,3} # 1-3 slashes - | # or - [a-z0-9%] # Single letter or digit or '%' - | # or - \?(dn|x[lts]|as|kt|mt|tr)= # Magnet parameters + /{1,3} # 1-3 slashes + | # or + [a-z0-9%] # Single letter or digit or '%' ) - | # or - www\d{0,3}[.] # "www.", "www1.", "www2." … "www999." - | # or - [a-z0-9.\-]+[.][a-z]{2,4}/ # looks like domain name followed by a slash + | # or + www\d{0,3}[.] # "www.", "www1.", "www2." … "www999." + | # or + [a-z0-9.\-]+[.][a-z]{2,4}/ # looks like domain name followed by a slash + | # or + magnet:\?(dn|x[lts]|as|kt|mt|tr)= # magnet protocol with its parameters ) ( # One or more: [^\s()<>]+ # Run of non-space, non-()<> From fff4de5fa41ab51d3adada076716e6b87a002f01 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 5 Sep 2013 18:37:44 +0200 Subject: [PATCH 12/39] Take
s into account when walking down the nodes. --- src/Linkification/Linkify.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 85617b39d..91247de5a 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -45,7 +45,11 @@ Linkify = node: -> return if @isClone or !links = @info.comment.match Linkify.catchAll - walker = d.createTreeWalker @nodes.comment, 4 + walker = d.createTreeWalker @nodes.comment, 1 | 4, acceptNode: (node) -> + return if node.nodeName in ['#text', 'BR'] + 1 # NodeFilter.FILTER_ACCEPT + else + 3 # NodeFilter.FILTER_SKIP range = d.createRange() for link in links boundaries = Linkify.find link, walker @@ -66,6 +70,8 @@ Linkify = # Walk through the nodes until we find the entire link. text = '' while node = walker.nextNode() + if node.nodeName is 'BR' + return Linkify.find link, walker text += node.data break if text.indexOf(link) > -1 return unless node From 15309e7968669eded4b46e16329e98386b204359 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Fri, 6 Sep 2013 02:43:29 +0200 Subject: [PATCH 13/39] Tiny refactor in ThreadUpdater. --- src/Monitoring/ThreadUpdater.coffee | 35 +++++++++++++---------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index 206401e3c..9b91c1c5d 100644 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -24,7 +24,6 @@ ThreadUpdater = ThreadUpdater.thread = @ ThreadUpdater.root = @OP.nodes.root.parentNode ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0] - ThreadUpdater.outdateCount = 0 for input in $$ 'input', ThreadUpdater.dialog if input.type is 'checkbox' @@ -36,7 +35,6 @@ ThreadUpdater = when 'Auto Update This' $.off input, 'change', $.cb.checked $.on input, 'change', ThreadUpdater.cb.autoUpdate - $.event 'change', null, input when 'Interval' $.on input, 'change', ThreadUpdater.cb.interval ThreadUpdater.cb.interval.call input @@ -54,15 +52,14 @@ ThreadUpdater = cb: online: -> - if ThreadUpdater.online = navigator.onLine + if navigator.onLine ThreadUpdater.outdateCount = 0 ThreadUpdater.setInterval() - ThreadUpdater.update() if ThreadUpdater.isUpdating ThreadUpdater.set 'status', null, null else ThreadUpdater.set 'timer', null ThreadUpdater.set 'status', 'Offline', 'warning' - ThreadUpdater.cb.autoUpdate() + ThreadUpdater.count true post: (e) -> return unless ThreadUpdater.isUpdating and e.detail.threadID is ThreadUpdater.thread.ID ThreadUpdater.outdateCount = 0 @@ -71,28 +68,23 @@ ThreadUpdater = return if d.hidden # Reset the counter when we focus this tab. ThreadUpdater.outdateCount = 0 - if ThreadUpdater.seconds > ThreadUpdater.interval - ThreadUpdater.setInterval() + ThreadUpdater.seconds = Math.min ThreadUpdater.seconds, ThreadUpdater.interval scrollBG: -> ThreadUpdater.scrollBG = if Conf['Scroll BG'] -> true else -> not d.hidden autoUpdate: (e) -> - ThreadUpdater.isUpdating = @checked if e - if ThreadUpdater.isUpdating and ThreadUpdater.online - ThreadUpdater.timeout() - else - clearTimeout ThreadUpdater.timeoutID + ThreadUpdater.count ThreadUpdater.isUpdating = @checked interval: (e) -> val = Math.max 5, parseInt @value, 10 ThreadUpdater.interval = @value = val $.cb.value.call @ if e load: (e) -> {req} = ThreadUpdater + delete ThreadUpdater.req if e.type isnt 'loadend' # timeout or abort req.onloadend = null - delete ThreadUpdater.req if e.type is 'timeout' ThreadUpdater.set 'status', 'Retrying', null ThreadUpdater.update() @@ -118,7 +110,6 @@ ThreadUpdater = else ["#{req.statusText} (#{req.status})", 'warning'] ThreadUpdater.set 'status', text, klass - delete ThreadUpdater.req setInterval: -> i = ThreadUpdater.interval @@ -128,8 +119,7 @@ ThreadUpdater = j = Math.min j, 7 ThreadUpdater.seconds = Math.max i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j] ThreadUpdater.set 'timer', ThreadUpdater.seconds - clearTimeout ThreadUpdater.timeoutID - ThreadUpdater.timeout() + ThreadUpdater.count true set: (name, text, klass) -> el = ThreadUpdater[name] @@ -141,14 +131,19 @@ ThreadUpdater = el.textContent = text el.className = klass if klass isnt undefined + count: (start) -> + clearTimeout ThreadUpdater.timeoutID + ThreadUpdater.timeout() if start and ThreadUpdater.isUpdating and navigator.onLine + timeout: -> ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000 - ThreadUpdater.set 'timer', --ThreadUpdater.seconds - ThreadUpdater.update() if ThreadUpdater.seconds <= 0 + sec = ThreadUpdater.seconds-- + ThreadUpdater.set 'timer', sec + ThreadUpdater.update() if sec <= 0 update: -> - return unless ThreadUpdater.online - clearTimeout ThreadUpdater.timeoutID + return unless navigator.onLine + ThreadUpdater.count() ThreadUpdater.set 'timer', '...' ThreadUpdater.req.abort() if ThreadUpdater.req url = "//api.4chan.org/#{ThreadUpdater.thread.board}/res/#{ThreadUpdater.thread}.json" From 2f2aedb70e6654f839b47a9b56cfbdcb4bac7c82 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Fri, 6 Sep 2013 15:20:17 +0200 Subject: [PATCH 14/39] Release 4chan X v3.11.0. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47bcd50eb..fe8f191eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## 3.11.0 - *2013-09-06* + - **New feature**: `Color User IDs`, enabled by default ## 3.10.0 - *2013-08-22* diff --git a/package.json b/package.json index a8957cbb0..019cb484d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "4chan-X", - "version": "3.10.0", + "version": "3.11.0", "description": "Cross-browser extension for productive lurking on 4chan.", "meta": { "name": "4chan X", From 995695bcef700ed5b0bc7633172845da1834205e Mon Sep 17 00:00:00 2001 From: Mayhem Date: Sat, 7 Sep 2013 13:05:19 +0200 Subject: [PATCH 15/39] Grunt task options can work on a global level. --- Gruntfile.coffee | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 753ac3df2..01b005c59 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -1,21 +1,14 @@ module.exports = (grunt) -> - concatOptions = - process: Object.create(null, data: - get: -> grunt.config 'pkg' - enumerable: true - ) - shellOptions = - stdout: true - stderr: true - failOnError: true - # Project configuration. grunt.initConfig pkg: grunt.file.readJSON 'package.json' concat: + options: process: Object.create(null, data: + get: -> grunt.config 'pkg' + enumerable: true + ) coffee: - options: concatOptions src: [ 'src/General/Config.coffee' 'src/General/Globals.coffee' @@ -46,7 +39,6 @@ module.exports = (grunt) -> ] dest: 'tmp-<%= pkg.type %>/script.coffee' crx: - options: concatOptions files: 'builds/crx/manifest.json': 'src/Meta/manifest.json' 'builds/crx/script.js': [ @@ -55,7 +47,6 @@ module.exports = (grunt) -> 'tmp-<%= pkg.type %>/script.js' ] userscript: - options: concatOptions files: 'builds/<%= pkg.name %>.meta.js': 'src/Meta/metadata.js' 'builds/<%= pkg.name %>.user.js': [ @@ -83,8 +74,11 @@ module.exports = (grunt) -> createTag: false push: false shell: + options: + stdout: true + stderr: true + failOnError: true commit: - options: shellOptions command: [ 'git checkout <%= pkg.meta.mainBranch %>' 'git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>."' @@ -92,12 +86,11 @@ module.exports = (grunt) -> 'git tag -af stable-v3 -m "<%= pkg.meta.name %> v<%= pkg.version %>."' ].join ' && ' push: - options: shellOptions command: 'git push origin --tags -f && git push origin --all' watch: + options: + interrupt: true all: - options: - interrupt: true files: [ 'Gruntfile.coffee' 'package.json' From 62c04886ccfad37e5a1e83b7ceb65d361e3e7a09 Mon Sep 17 00:00:00 2001 From: Noble pleb Date: Mon, 9 Sep 2013 18:50:15 +0300 Subject: [PATCH 16/39] Update archives.json SSL on 4plebs --- json/archives.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json/archives.json b/json/archives.json index 578fbee55..7ba478894 100644 --- a/json/archives.json +++ b/json/archives.json @@ -30,7 +30,7 @@ "name": "4plebs", "domain": "archive.4plebs.org", "http": true, - "https": false, + "https": true, "software": "foolfuuka", "boards": ["hr", "tg", "tv", "x"], "files": ["hr", "tg", "tv", "x"] From 5523d9a6a84b053eda06721b493cf10e8323a4c1 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 12 Sep 2013 17:53:22 +0200 Subject: [PATCH 17/39] Fix the padding of QR inputs on Windows. --- css/style.css | 1 - 1 file changed, 1 deletion(-) diff --git a/css/style.css b/css/style.css index c2646837c..4b7c27012 100644 --- a/css/style.css +++ b/css/style.css @@ -794,7 +794,6 @@ a.hide-announcement { } #file-n-submit-container input { margin: 0; - padding: 0; } #file-n-submit input[type='submit'] { order: 1; From 1208e9623354820a2090c836d640085abf7d7ef3 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Fri, 13 Sep 2013 16:41:18 +0200 Subject: [PATCH 18/39] Add support for the flag selector on /pol/. #1270 --- css/style.css | 2 +- src/Posting/QR.coffee | 36 ++++++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/css/style.css b/css/style.css index 4b7c27012..dbba61fa3 100644 --- a/css/style.css +++ b/css/style.css @@ -581,7 +581,7 @@ a.hide-announcement { align-self: stretch; flex: 1; } -#qr select { +#qr select[data-name=thread] { margin: 0; -webkit-appearance: none; -moz-appearance: none; diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index b13b83f15..ff4f86599 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -522,8 +522,9 @@ QR = lock: (lock=true) -> @isLocked = lock return unless @ is QR.selected - for name in ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler'] - QR.nodes[name].disabled = lock + for name in ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag'] + continue unless node = QR.nodes[name] + node.disabled = lock @nodes.rm.style.visibility = if lock then 'hidden' else '' (if lock then $.off else $.on) QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput @nodes.spoiler.disabled = lock @@ -545,8 +546,9 @@ QR = $.event 'QRPostSelection', @ load: -> # Load this post's values. - for name in ['thread', 'name', 'email', 'sub', 'com', 'filename'] - QR.nodes[name].value = @[name] or null + for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag'] + continue unless node = QR.nodes[name] + node.value = @[name] or node.dataset.default or null @showFileData() QR.characterCount() save: (input) -> @@ -554,7 +556,7 @@ QR = @spoiler = input.checked return {name} = input.dataset - @[name] = input.value + @[name] = input.value or input.dataset.default or null switch name when 'thread' QR.status() @@ -578,8 +580,9 @@ QR = return unless @ is QR.selected # Do this in case people use extensions # that do not trigger the `input` event. - for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler'] - @save QR.nodes[name] + for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler', 'flag'] + continue unless node = QR.nodes[name] + @save node return setFile: (@file) -> @filename = file.name @@ -856,7 +859,13 @@ QR = """ + nodes.flashTag.dataset.default = '4' $.add nodes.form, nodes.flashTag + if flagSelector = $ '.flagSelector' + nodes.flag = flagSelector.cloneNode true + nodes.flag.dataset.name = 'flag' + nodes.flag.dataset.default = '0' + $.add nodes.form, nodes.flag # Make a list of threads. for thread of g.BOARD.threads @@ -882,9 +891,11 @@ QR = $.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click() $.on nodes.fileInput, 'change', QR.handleFiles # save selected post's data - for name in ['name', 'email', 'sub', 'com', 'filename'] - $.on nodes[name], 'input', -> QR.selected.save @ - $.on nodes.thread, 'change', -> QR.selected.save @ + save = -> QR.selected.save @ + for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag'] + continue unless node = nodes[name] + event = if node.nodeName is 'SELECT' then 'change' else 'input' + $.on nodes[name], event, save <% if (type === 'userscript') { %> if Conf['Remember QR Size'] @@ -965,7 +976,7 @@ QR = post.lock() - postData = + formData = resto: threadID name: post.name email: post.email @@ -974,6 +985,7 @@ QR = upfile: post.file filetag: filetag spoiler: post.spoiler + flag: post.flag textonly: textOnly mode: 'regist' pwd: QR.persona.pwd @@ -997,7 +1009,7 @@ QR = [FAQ] """ extra = - form: $.formData postData + form: $.formData formData upCallbacks: onload: -> # Upload done, waiting for server response. From f606b5fbd52809c060729fc14a4e4ef8908e56b4 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Fri, 13 Sep 2013 17:03:42 +0200 Subject: [PATCH 19/39] Remember the flag selection. Close #1270 --- CHANGELOG.md | 2 ++ src/Posting/QR.coffee | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe8f191eb..d81a56f17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- Added support for the flag selector on /pol/. + ## 3.11.0 - *2013-09-06* - **New feature**: `Color User IDs`, enabled by default diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index ff4f86599..86cfa9510 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -221,6 +221,7 @@ QR = name: post.name email: if /^sage$/.test post.email then persona.email else post.email sub: if Conf['Remember Subject'] then post.sub else undefined + flag: post.flag $.set 'QR.persona', persona cooldown: @@ -504,6 +505,12 @@ QR = if prev then prev.sub else persona.sub else '' + + if QR.nodes.flag + @flag = if prev + prev.flag + else + persona.flag @load() if QR.selected is @ # load persona @select() if select @unlock() From 8dc39192dd7eb99a4aa06bedeb42fbd14d4465bd Mon Sep 17 00:00:00 2001 From: Mayhem Date: Fri, 13 Sep 2013 17:05:12 +0200 Subject: [PATCH 20/39] Release 4chan X v3.11.1. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d81a56f17..4666ca078 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +### 3.11.1 - *2013-09-13* + - Added support for the flag selector on /pol/. ## 3.11.0 - *2013-09-06* diff --git a/package.json b/package.json index 019cb484d..5c585bfb0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "4chan-X", - "version": "3.11.0", + "version": "3.11.1", "description": "Cross-browser extension for productive lurking on 4chan.", "meta": { "name": "4chan X", From 6286629e884acb61b21f63838325aaed0d720c2b Mon Sep 17 00:00:00 2001 From: NemDiggers Date: Sun, 15 Sep 2013 21:26:11 -0400 Subject: [PATCH 21/39] Add /soc/ support --- json/archives.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json/archives.json b/json/archives.json index 7ba478894..6c1b15415 100644 --- a/json/archives.json +++ b/json/archives.json @@ -59,8 +59,8 @@ "http": true, "https": true, "software": "foolfuuka", - "boards": ["b", "e", "h", "hc", "p", "s", "u"], - "files": ["b", "e", "h", "hc", "p", "s", "u"] + "boards": ["b", "e", "h", "hc", "p", "s", "soc", "u"], + "files": ["b", "e", "h", "hc", "p", "s", "soc", "u"] }, { "uid": 7, "name": "Install Gentoo", From 917add41e898b20f4664f5c8260fd0e3652c3752 Mon Sep 17 00:00:00 2001 From: NemDiggers Date: Mon, 16 Sep 2013 20:32:45 -0400 Subject: [PATCH 22/39] Add /sp/ support --- json/archives.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json/archives.json b/json/archives.json index 6c1b15415..e2654302b 100644 --- a/json/archives.json +++ b/json/archives.json @@ -59,8 +59,8 @@ "http": true, "https": true, "software": "foolfuuka", - "boards": ["b", "e", "h", "hc", "p", "s", "soc", "u"], - "files": ["b", "e", "h", "hc", "p", "s", "soc", "u"] + "boards": ["b", "e", "h", "hc", "p", "s", "soc", "sp", "u"], + "files": ["b", "e", "h", "hc", "p", "s", "soc", "sp", "u"] }, { "uid": 7, "name": "Install Gentoo", From d3654f907989b80d3083e6d6ecd4c5c3ef16a9df Mon Sep 17 00:00:00 2001 From: Mayhem Date: Tue, 17 Sep 2013 09:45:34 +0200 Subject: [PATCH 23/39] Update post/delete cooldown timers. --- src/Menu/DeleteLink.coffee | 2 +- src/Posting/QR.coffee | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Menu/DeleteLink.coffee b/src/Menu/DeleteLink.coffee index 1912733c6..828436575 100644 --- a/src/Menu/DeleteLink.coffee +++ b/src/Menu/DeleteLink.coffee @@ -84,7 +84,7 @@ DeleteLink = delete DeleteLink.cooldown.counting return DeleteLink.cooldown.counting = post - length = 30 + length = 60 seconds = Math.ceil (length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND DeleteLink.cooldown.count post, seconds, length, node count: (post, seconds, length, node) -> diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 86cfa9510..067d217b4 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -233,9 +233,9 @@ QR = when 'q' then 86400 when 'b', 'soc', 'r9k' then 600 else 300 - sage: if board is 'q' then 600 else 60 - file: if board is 'q' then 300 else 30 - post: if board is 'q' then 150 else 30 + sage: if board is 'q' then 600 else 120 + file: if board is 'q' then 300 else 60 + post: if board is 'q' then 150 else 60 QR.cooldown.upSpd = 0 QR.cooldown.upSpdAccuracy = .5 $.get "cooldown.#{board}", {}, (item) -> From 641de541b4a7fa021287bab860305c081f4f1cbb Mon Sep 17 00:00:00 2001 From: Mayhem Date: Tue, 17 Sep 2013 09:48:58 +0200 Subject: [PATCH 24/39] Thread creation cooldown duration is 600 seconds. --- src/Posting/QR.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 067d217b4..bba2fc13a 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -231,8 +231,7 @@ QR = QR.cooldown.types = thread: switch board when 'q' then 86400 - when 'b', 'soc', 'r9k' then 600 - else 300 + else 600 sage: if board is 'q' then 600 else 120 file: if board is 'q' then 300 else 60 post: if board is 'q' then 150 else 60 From dd1c0f5d3163d8bf65ae08e79f8e1e868b502ca4 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Tue, 17 Sep 2013 09:50:58 +0200 Subject: [PATCH 25/39] Changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4666ca078..983238c3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +Updated post and deletion cooldown timers to match 4chan changes: they are now twice longer. + ### 3.11.1 - *2013-09-13* - Added support for the flag selector on /pol/. From 93c297b32413d59ba539a03d524ac85f630b3220 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Tue, 17 Sep 2013 09:51:21 +0200 Subject: [PATCH 26/39] Release 4chan X v3.11.2. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 983238c3e..38a9a0fa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +### 3.11.2 - *2013-09-17* + Updated post and deletion cooldown timers to match 4chan changes: they are now twice longer. ### 3.11.1 - *2013-09-13* diff --git a/package.json b/package.json index 5c585bfb0..b0945d19f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "4chan-X", - "version": "3.11.1", + "version": "3.11.2", "description": "Cross-browser extension for productive lurking on 4chan.", "meta": { "name": "4chan X", From 6a710cbf0ebf0e4ed2a91252edeab109efa9f109 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Wed, 18 Sep 2013 00:30:13 +0200 Subject: [PATCH 27/39] Fix quotifying link hrefs in certain cases. That's a 5 months old bug that didn't seem to affect anyone... --- src/Quotelinks/Quotify.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Quotelinks/Quotify.coffee b/src/Quotelinks/Quotify.coffee index 2e8ef7e10..d58923487 100644 --- a/src/Quotelinks/Quotify.coffee +++ b/src/Quotelinks/Quotify.coffee @@ -41,13 +41,13 @@ Quotify = # Don't (Dead) when quotifying in an archived post, # and we know the post still exists. a = $.el 'a', - href: "/#{boardID}/#{post.thread}/res/#p#{postID}" + href: "/#{boardID}/res/#{post.thread}#p#{postID}" className: 'quotelink' textContent: quote else # Replace the .deadlink span if we can redirect. a = $.el 'a', - href: "/#{boardID}/#{post.thread}/res/#p#{postID}" + href: "/#{boardID}/res/#{post.thread}#p#{postID}" className: 'quotelink deadlink' target: '_blank' textContent: "#{quote}\u00A0(Dead)" From a56b6765cb1cd766d9ff01a0834d63e7da0b2fcd Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 19 Sep 2013 09:22:24 +0200 Subject: [PATCH 28/39] Remove some /q/-only settings. --- src/Menu/DeleteLink.coffee | 2 +- src/Miscellaneous/ExpandThread.coffee | 2 +- src/Posting/QR.coffee | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Menu/DeleteLink.coffee b/src/Menu/DeleteLink.coffee index 828436575..1805047f8 100644 --- a/src/Menu/DeleteLink.coffee +++ b/src/Menu/DeleteLink.coffee @@ -31,7 +31,7 @@ DeleteLink = el: div order: 40 open: (post) -> - return false if post.isDead or post.board.ID is 'q' + return false if post.isDead DeleteLink.post = post node = div.firstChild node.textContent = 'Delete' diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee index 513a24368..b4733d397 100644 --- a/src/Miscellaneous/ExpandThread.coffee +++ b/src/Miscellaneous/ExpandThread.coffee @@ -55,7 +55,7 @@ ExpandThread = 1 else switch g.BOARD.ID # XXX boards config - when 'b', 'vg', 'q' then 3 + when 'b', 'vg' then 3 when 't' then 1 else 5 posts = $$ ".thread > .replyContainer", threadRoot diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index bba2fc13a..c3231706a 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -946,7 +946,7 @@ QR = # prevent errors if threadID is 'new' threadID = null - if g.BOARD.ID in ['vg', 'q'] and !post.sub + if g.BOARD.ID is 'vg' and !post.sub err = 'New threads require a subject.' else unless post.file or textOnly = !!$ 'input[name=textonly]', $.id 'postForm' err = 'No file selected.' From 499288e6da3cd7553a2a8f5badec66e42e4510be Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 19 Sep 2013 10:59:38 +0200 Subject: [PATCH 29/39] Update cooldown counting procedures. --- CHANGELOG.md | 6 ++++ src/Posting/QR.coffee | 83 +++++++++++++++++++------------------------ 2 files changed, 43 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38a9a0fa7..666524208 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Update posting cooldown timers to match 4chan settings: + - Cooldown may vary between inter-thread and intra-thread replies. + - Cooldown may vary when posting a file or not. + - Cooldown does not take sageing into account anymore. + - Timers vary across boards. + ### 3.11.2 - *2013-09-17* Updated post and deletion cooldown timers to match 4chan changes: they are now twice longer. diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index c3231706a..46905bfb1 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -227,20 +227,19 @@ QR = cooldown: init: -> return unless Conf['Cooldown'] - board = g.BOARD.ID - QR.cooldown.types = - thread: switch board - when 'q' then 86400 - else 600 - sage: if board is 'q' then 600 else 120 - file: if board is 'q' then 300 else 60 - post: if board is 'q' then 150 else 60 + setTimers = (e) => QR.cooldown.types = e.detail + $.on window, 'cooldown:timers', setTimers + $.globalEval 'window.dispatchEvent(new CustomEvent("cooldown:timers", {detail: cooldowns}))' + $.off window, 'cooldown:timers', setTimers + for type of QR.cooldown.types + QR.cooldown.types[type] = +QR.cooldown.types[type] QR.cooldown.upSpd = 0 QR.cooldown.upSpdAccuracy = .5 - $.get "cooldown.#{board}", {}, (item) -> - QR.cooldown.cooldowns = item["cooldown.#{board}"] + key = "cooldown.#{g.BOARD}" + $.get key, {}, (item) -> + QR.cooldown.cooldowns = item[key] QR.cooldown.start() - $.sync "cooldown.#{board}", QR.cooldown.sync + $.sync key, QR.cooldown.sync start: -> return unless Conf['Cooldown'] return if QR.cooldown.isCounting @@ -254,30 +253,17 @@ QR = QR.cooldown.start() set: (data) -> return unless Conf['Cooldown'] - {req, post, isReply, delay} = data + {req, post, isReply, threadID, delay} = data start = if req then req.uploadEndTime else Date.now() if delay cooldown = {delay} else if post.file - upSpd = post.file.size / ((req.uploadEndTime - req.uploadStartTime) / $.SECOND) + upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND) QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2 QR.cooldown.upSpd = upSpd - isSage = /sage/i.test post.email - hasFile = !!post.file - type = unless isReply - 'thread' - else if isSage - 'sage' - else if hasFile - 'file' - else - 'post' - cooldown = - isReply: isReply - isSage: isSage - hasFile: hasFile - timeout: start + QR.cooldown.types[type] * $.SECOND + hasFile = !!post.file + cooldown = {isReply, hasFile, threadID} QR.cooldown.cooldowns[start] = cooldown $.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns QR.cooldown.start() @@ -295,12 +281,12 @@ QR = QR.status() return - setTimeout QR.cooldown.count, $.SECOND + clearTimeout QR.cooldown.timeout + QR.cooldown.timeout = setTimeout QR.cooldown.count, $.SECOND now = Date.now() post = QR.posts[0] isReply = post.thread isnt 'new' - isSage = /sage/i.test post.email hasFile = !!post.file seconds = null {types, cooldowns, upSpd, upSpdAccuracy} = QR.cooldown @@ -314,26 +300,31 @@ QR = QR.cooldown.unset start continue + if 'timeout' of cooldown + # XXX tmp conversion from previous cooldowns + QR.cooldown.unset start + continue + if isReply is cooldown.isReply - # Only cooldowns relevant to this post can set the seconds value. - # Unset outdated cooldowns that can no longer impact us. + # Only cooldowns relevant to this post can set the seconds variable: + # reply cooldown with a reply, thread cooldown with a thread + elapsed = Math.floor (now - start) / $.SECOND + continue if elapsed < 0 # clock changed since then? type = unless isReply 'thread' - else if isSage and cooldown.isSage - 'sage' - else if hasFile and cooldown.hasFile - 'file' + else if hasFile + 'image' else - 'post' - elapsed = Math.floor (now - start) / $.SECOND - if elapsed >= 0 # clock changed since then? - seconds = Math.max seconds, types[type] - elapsed - if Conf['Cooldown Prediction'] and hasFile and upSpd - seconds -= Math.floor post.file.size / upSpd * upSpdAccuracy - seconds = Math.max seconds, 0 - unless start <= now <= cooldown.timeout - QR.cooldown.unset start + 'reply' + maxTimer = Math.max types[type], types[type + '_intra'] or 0 + unless start <= now <= start + maxTimer * $.SECOND + QR.cooldown.unset start + type += '_intra' if isReply and +post.thread is cooldown.threadID + seconds = Math.max seconds, types[type] - elapsed + if seconds and Conf['Cooldown Prediction'] and hasFile and upSpd + seconds -= Math.floor post.file.size / upSpd * upSpdAccuracy + seconds = Math.max seconds, 0 # Update the status when we change posting type. # Don't get stuck at some random number. # Don't interfere with progress status updates. @@ -1137,7 +1128,7 @@ QR = else post.rm() - QR.cooldown.set {req, post, isReply} + QR.cooldown.set {req, post, isReply, threadID} URL = if threadID is postID # new thread "/#{g.BOARD}/res/#{threadID}" From dcae3436879493041e83eae219479040874a96c6 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 19 Sep 2013 11:43:50 +0200 Subject: [PATCH 30/39] Remove some sage-related code that is not relevant anymore. --- src/General/Config.coffee | 5 +---- src/Miscellaneous/Anonymize.coffee | 7 ++----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/General/Config.coffee b/src/General/Config.coffee index d6a194787..a214386d5 100644 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -107,10 +107,7 @@ Config = # Set a custom class for moot: #/Admin$/;highlight:moot;op:yes """ - email: """ - # Filter any e-mails that are not `sage` on /a/ and /jp/: - #/^(?!sage$)/;boards:a,jp - """ + email: "" subject: """ # Filter Generals on /v/: #/general/i;boards:v;op:only diff --git a/src/Miscellaneous/Anonymize.coffee b/src/Miscellaneous/Anonymize.coffee index bcbca4833..d5b80f99c 100644 --- a/src/Miscellaneous/Anonymize.coffee +++ b/src/Miscellaneous/Anonymize.coffee @@ -14,8 +14,5 @@ Anonymize = $.rm tripcode delete @nodes.tripcode if @info.email - if /sage/i.test @info.email - email.href = 'mailto:sage' - else - $.replace email, name - delete @nodes.email + $.replace email, name + delete @nodes.email From 5ae957b0e220a92c53bef4b6c5a70a4010cbc25b Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 19 Sep 2013 11:53:11 +0200 Subject: [PATCH 31/39] Remove capcode replies-related code as it's not relevant anymore. --- src/General/Build.coffee | 35 +------------------------- src/General/Post.coffee | 6 ++--- src/Miscellaneous/ExpandComment.coffee | 5 ---- 3 files changed, 3 insertions(+), 43 deletions(-) diff --git a/src/General/Build.coffee b/src/General/Build.coffee index 0cbe5929f..783a774f5 100644 --- a/src/General/Build.coffee +++ b/src/General/Build.coffee @@ -27,7 +27,6 @@ Build = date: data.now dateUTC: data.time comment: data.com - capcodeReplies: data.capcode_replies # thread status isSticky: !!data.sticky isClosed: !!data.closed @@ -55,7 +54,7 @@ Build = postID, threadID, boardID name, capcode, tripcode, uniqueID, email, subject, flagCode, flagName, date, dateUTC isSticky, isClosed - comment, capcodeReplies + comment file } = o isOP = postID is threadID @@ -248,36 +247,4 @@ Build = continue if href[0] is '/' # Cross-board quote, or board link quote.href = "/#{boardID}/res/#{href}" # Fix pathnames - Build.capcodeReplies {boardID, threadID, root: container, capcodeReplies} - container - - capcodeReplies: ({boardID, threadID, bq, root, capcodeReplies}) -> - return unless capcodeReplies - - generateCapcodeReplies = (capcodeType, array) -> - "#{ - switch capcodeType - when 'admin' - 'Administrator' - when 'mod' - 'Moderator' - when 'developer' - 'Developer' - } Repl#{if array.length > 1 then 'ies' else 'y'}: #{ - array.map (ID) -> - ">>#{ID}" - .join ' ' - }
" - html = [] - for capcodeType, array of capcodeReplies - html.push generateCapcodeReplies capcodeType, array - - bq or= $ 'blockquote', root - $.add bq, [ - $.el 'br' - $.el 'br' - $.el 'span', - className: 'capcodeReplies' - innerHTML: html.join '' - ] diff --git a/src/General/Post.coffee b/src/General/Post.coffee index c792efa81..0d2cbc293 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -62,13 +62,12 @@ class Post #
-> \n # Remove: # 'Comment too long'... - # Admin/Mod/Dev replies. (/q/) # EXIF data. (/p/) # Rolls. (/tg/) # Preceding and following new lines. # Trailing spaces. bq = @nodes.comment.cloneNode true - for node in $$ '.abbr, .capcodeReplies, .exif, b', bq + for node in $$ '.abbr, .exif, b', bq $.rm node text = [] # XPathResult.ORDERED_NODE_SNAPSHOT_TYPE === 7 @@ -100,8 +99,7 @@ class Post @nodes.quotelinks.push quotelink - # Don't count capcode replies as quotes in OPs. (Admin/Mod/Dev Replies: ...) - return if @isClone or !@isReply and $.hasClass quotelink.parentNode.parentNode, 'capcodeReplies' + return if @isClone # ES6 Set when? fullID = "#{match[1]}.#{match[2]}" diff --git a/src/Miscellaneous/ExpandComment.coffee b/src/Miscellaneous/ExpandComment.coffee index 3567e9f84..e292d65ad 100644 --- a/src/Miscellaneous/ExpandComment.coffee +++ b/src/Miscellaneous/ExpandComment.coffee @@ -48,11 +48,6 @@ ExpandComment = href = quote.getAttribute 'href' continue if href[0] is '/' # Cross-board quote, or board link quote.href = "/#{post.board}/res/#{href}" # Fix pathnames - Build.capcodeReplies - boardID: post.board.ID - threadID: post.thread.ID - bq: clone - capcodeReplies: postObj.capcode_replies post.nodes.shortComment = comment $.replace comment, clone post.nodes.comment = post.nodes.longComment = clone From 7aaa91508cbccb9e7993f3835470a34837272f65 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 19 Sep 2013 13:53:24 +0200 Subject: [PATCH 32/39] Small cooldown fixes. --- src/Posting/QR.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 46905bfb1..3cfcc11e8 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -230,6 +230,7 @@ QR = setTimers = (e) => QR.cooldown.types = e.detail $.on window, 'cooldown:timers', setTimers $.globalEval 'window.dispatchEvent(new CustomEvent("cooldown:timers", {detail: cooldowns}))' + QR.cooldown.types or= {} # XXX tmp workaround until all pages and the catalogs get the cooldowns var. $.off window, 'cooldown:timers', setTimers for type of QR.cooldown.types QR.cooldown.types[type] = +QR.cooldown.types[type] @@ -316,7 +317,7 @@ QR = 'image' else 'reply' - maxTimer = Math.max types[type], types[type + '_intra'] or 0 + maxTimer = Math.max types[type] or 0, types[type + '_intra'] or 0 unless start <= now <= start + maxTimer * $.SECOND QR.cooldown.unset start type += '_intra' if isReply and +post.thread is cooldown.threadID From 15a51d2344cd06269df6f9f0d8218d2d5b75f93c Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 19 Sep 2013 14:22:53 +0200 Subject: [PATCH 33/39] Release 4chan X v3.11.3. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 666524208..833e89630 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +### 3.11.3 - *2013-09-19* + Update posting cooldown timers to match 4chan settings: - Cooldown may vary between inter-thread and intra-thread replies. - Cooldown may vary when posting a file or not. diff --git a/package.json b/package.json index b0945d19f..6a64e6c2c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "4chan-X", - "version": "3.11.2", + "version": "3.11.3", "description": "Cross-browser extension for productive lurking on 4chan.", "meta": { "name": "4chan X", From 3a13d0429ccd662c95d3a2780ff0e0b0611b5349 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 19 Sep 2013 15:02:43 +0200 Subject: [PATCH 34/39] Remove ThreadWatcher conversion from old format. --- src/Monitoring/ThreadWatcher.coffee | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee index de775049a..801890731 100644 --- a/src/Monitoring/ThreadWatcher.coffee +++ b/src/Monitoring/ThreadWatcher.coffee @@ -19,14 +19,6 @@ ThreadWatcher = ThreadWatcher.fetchAllStatus() @db.save() - # XXX tmp conversion from old to new format - $.get 'WatchedThreads', null, ({WatchedThreads}) -> - return unless WatchedThreads - for boardID, threads of ThreadWatcher.convert WatchedThreads - for threadID, data of threads - ThreadWatcher.db.set {boardID, threadID, val: data} - $.delete 'WatchedThreads' - Thread::callbacks.push name: 'Thread Watcher' cb: @node From 124a2ef4736e7f3ad61eedecece9fc656b48d16c Mon Sep 17 00:00:00 2001 From: Mayhem Date: Fri, 20 Sep 2013 00:01:19 +0200 Subject: [PATCH 35/39] No need to store hasFile. --- src/Posting/QR.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 3cfcc11e8..ca0ce1dbe 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -263,8 +263,7 @@ QR = upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND) QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2 QR.cooldown.upSpd = upSpd - hasFile = !!post.file - cooldown = {isReply, hasFile, threadID} + cooldown = {isReply, threadID} QR.cooldown.cooldowns[start] = cooldown $.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns QR.cooldown.start() From 47347d95f5820472eaf6f5a9e3b15c8c7422d235 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Fri, 20 Sep 2013 10:53:42 +0200 Subject: [PATCH 36/39] Posting an image after a reply has no cooldown. --- CHANGELOG.md | 3 +++ src/Posting/QR.coffee | 16 ++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 833e89630..33a7ea08b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +Tiny posting cooldown adjustment: + - You can post an image reply immediately after a non-image reply. + ### 3.11.3 - *2013-09-19* Update posting cooldown timers to match 4chan settings: diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index ca0ce1dbe..306907940 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -259,11 +259,11 @@ QR = if delay cooldown = {delay} else - if post.file + if hasFile = !!post.file upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND) QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2 QR.cooldown.upSpd = upSpd - cooldown = {isReply, threadID} + cooldown = {isReply, hasFile, threadID} QR.cooldown.cooldowns[start] = cooldown $.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns QR.cooldown.start() @@ -310,12 +310,16 @@ QR = # reply cooldown with a reply, thread cooldown with a thread elapsed = Math.floor (now - start) / $.SECOND continue if elapsed < 0 # clock changed since then? - type = unless isReply - 'thread' + unless isReply + type = 'thread' else if hasFile - 'image' + # You can post an image reply immediately after a non-image reply. + unless cooldown.hasFile + seconds = Math.max seconds, 0 + continue + type = 'image' else - 'reply' + type = 'reply' maxTimer = Math.max types[type] or 0, types[type + '_intra'] or 0 unless start <= now <= start + maxTimer * $.SECOND QR.cooldown.unset start From 380eda18d28437f031d7a949db688fd961f5e50d Mon Sep 17 00:00:00 2001 From: Mayhem Date: Fri, 20 Sep 2013 11:03:18 +0200 Subject: [PATCH 37/39] Don't show the "running low on captchas" when we actually have just enough. --- src/Posting/QR.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 306907940..e7515d60c 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -1111,9 +1111,9 @@ QR = postID } - # Enable auto-posting if we have stuff to post, disable it otherwise. - postsCount = QR.posts.length - QR.cooldown.auto = postsCount > 1 and isReply + # Enable auto-posting if we have stuff left to post, disable it otherwise. + postsCount = QR.posts.length - 1 + QR.cooldown.auto = postsCount and isReply if QR.cooldown.auto and QR.captcha.isEnabled and (captchasCount = QR.captcha.captchas.length) < 3 and captchasCount < postsCount notif = new Notification 'Quick reply warning', body: "You are running low on cached captchas. Cache count: #{captchasCount}." From acffb4b7de65d1b79ae56160d1696fa370932b0d Mon Sep 17 00:00:00 2001 From: Mayhem Date: Fri, 20 Sep 2013 11:26:46 +0200 Subject: [PATCH 38/39] Update deps. --- Gruntfile.coffee | 12 ++++++------ package.json | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 01b005c59..e7226450a 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -79,12 +79,12 @@ module.exports = (grunt) -> stderr: true failOnError: true commit: - command: [ - 'git checkout <%= pkg.meta.mainBranch %>' - 'git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>."' - 'git tag -a <%= pkg.version %> -m "<%= pkg.meta.name %> v<%= pkg.version %>."' - 'git tag -af stable-v3 -m "<%= pkg.meta.name %> v<%= pkg.version %>."' - ].join ' && ' + command: """ + git checkout <%= pkg.meta.mainBranch %> + git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>." + git tag -a <%= pkg.version %> -m "<%= pkg.meta.name %> v<%= pkg.version %>." + git tag -af stable-v3 -m "<%= pkg.meta.name %> v<%= pkg.version %>." + """ push: command: 'git push origin --tags -f && git push origin --all' watch: diff --git a/package.json b/package.json index 6a64e6c2c..9d548fa63 100644 --- a/package.json +++ b/package.json @@ -19,14 +19,14 @@ "font-awesome": "git://github.com/MayhemYDG/Font-Awesome.git#df4285951124f9ca1f3907438462e5ba9e464bcb", "grunt": "~0.4.1", "grunt-bump": "~0.0.11", - "grunt-concurrent": "~0.3.0", + "grunt-concurrent": "~0.3.1", "grunt-contrib-clean": "~0.5.0", "grunt-contrib-coffee": "~0.7.0", "grunt-contrib-compress": "~0.5.2", "grunt-contrib-concat": "~0.3.0", "grunt-contrib-copy": "~0.4.1", - "grunt-contrib-watch": "~0.5.0", - "grunt-shell": "~0.3.1", + "grunt-contrib-watch": "~0.5.3", + "grunt-shell": "~0.4.0", "load-grunt-tasks": "~0.1.0" }, "repository": { From 5b44045d55d95552bcde246b3f0effe70b566e15 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Sat, 21 Sep 2013 13:40:52 +0200 Subject: [PATCH 39/39] Remove foolz+hsts issue workaround. --- src/Archive/Redirect.coffee | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee index 24de31b4f..bb63f09ae 100644 --- a/src/Archive/Redirect.coffee +++ b/src/Archive/Redirect.coffee @@ -69,12 +69,7 @@ Redirect = post: (archive, {boardID, postID}) -> # For fuuka-based archives: # https://github.com/eksopl/fuuka/issues/27 - protocol = Redirect.protocol archive - # XXX foolz had HSTS set for 120 days, which broke XHR+CORS+Redirection when on HTTP. - # Remove necessary HTTPS procotol in September 2013. - if archive.name in ['Foolz', 'NSFW Foolz'] - protocol = 'https://' - URL = new String "#{protocol}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}" + URL = new String "#{Redirect.protocol archive}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}" URL.archive = archive URL