From 10f5f60b7f4e5c4fb15b7c9434f98077c23aed0c Mon Sep 17 00:00:00 2001 From: ccd0 Date: Fri, 19 Jul 2019 10:26:29 -0700 Subject: [PATCH 01/33] Changes in preparation for Coffeescript 2. #829 --- src/classes/Callbacks.coffee | 2 +- src/classes/Post.Clone.coffee | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/classes/Callbacks.coffee b/src/classes/Callbacks.coffee index 31aee94f5..89aa2f552 100644 --- a/src/classes/Callbacks.coffee +++ b/src/classes/Callbacks.coffee @@ -11,7 +11,7 @@ class Callbacks @keys.push name unless @[name] @[name] = cb - execute: (node, keys=@keys, force) -> + execute: (node, keys=@keys, force=false) -> return if node.callbacksExecuted and !force node.callbacksExecuted = true for name in keys diff --git a/src/classes/Post.Clone.coffee b/src/classes/Post.Clone.coffee index 8f87315ea..efa3089b7 100644 --- a/src/classes/Post.Clone.coffee +++ b/src/classes/Post.Clone.coffee @@ -1,7 +1,12 @@ Post.Clone = class extends Post isClone: true - constructor: (@origin, @context, contractThumb) -> + constructor: -> + that = Object.create(Post.Clone.prototype) + that.construct arguments... + return that + + construct: (@origin, @context, contractThumb) -> for key in ['ID', 'postID', 'threadID', 'boardID', 'siteID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply'] # Copy or point to the origin's key value. @[key] = @origin[key] From a2b80dce048c2ea2ebb17c500ef317831e61ba65 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Sat, 20 Jul 2019 13:52:08 -0700 Subject: [PATCH 02/33] Make this settings code easier to maintain. --- src/General/Settings.coffee | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index 133164d82..cccf7968b 100644 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -561,10 +561,13 @@ Settings = $.id('lastarchivecheck').textContent = 'never' items = {} - for name in ['archiveLists', 'archiveAutoUpdate', 'fourchanImageHost', 'captchaLanguage', 'captchaServiceDomain', 'boardnav', 'time', 'timeLocale', 'backlink', 'pastedname', 'fileInfo', 'QR.personas', 'favicon', 'usercss', 'customCooldown', 'jsWhitelist'] + for name, input of inputs when name not in ['captchaServiceKey', 'Interval', 'Custom CSS'] items[name] = Conf[name] - input = inputs[name] - event = if name in ['archiveLists', 'archiveAutoUpdate', 'QR.personas', 'favicon', 'usercss'] then 'change' else 'input' + event = if ( + input.nodeName is 'SELECT' or + input.type in ['checkbox', 'radio'] or + (input.nodeName is 'TEXTAREA' and name not of Settings) + ) then 'change' else 'input' $.on input, event, $.cb[if input.type is 'checkbox' then 'checked' else 'value'] $.on input, event, Settings[name] if name of Settings From 0aeed5edaa63e88cea7db8f19efcdd97cbaf5f9c Mon Sep 17 00:00:00 2001 From: ccd0 Date: Sun, 21 Jul 2019 07:51:02 -0700 Subject: [PATCH 03/33] Make list of boards with external catalog user-editable. #570, #525 --- src/General/Settings/Advanced.html | 10 +++++++ src/Miscellaneous/CatalogLinks.coffee | 39 ++++++++++++++++++++------- src/config/Config.coffee | 4 +++ src/css/style.css | 3 ++- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/General/Settings/Advanced.html b/src/General/Settings/Advanced.html index 36333e97e..03d81f883 100644 --- a/src/General/Settings/Advanced.html +++ b/src/General/Settings/Advanced.html @@ -19,6 +19,16 @@ Last updated: +
+ External Catalog +
External Catalog is disabled.
+
+ URLs of external catalog sites, where %board is to be replaced by the board name.
+ Each URL should be followed by ;boards: and optionally ;exclude: and a list of supported/excluded boards in the format explained in the Filter guide. +
+ +
+
Override 4chan Image Host
Change 4chan image links to this domain. Leave blank for no change.
diff --git a/src/Miscellaneous/CatalogLinks.coffee b/src/Miscellaneous/CatalogLinks.coffee index 91c91a8c3..e0ae26d5e 100644 --- a/src/Miscellaneous/CatalogLinks.coffee +++ b/src/Miscellaneous/CatalogLinks.coffee @@ -1,4 +1,5 @@ CatalogLinks = + init: -> if g.SITE.software is 'yotsuba' and (Conf['External Catalog'] or Conf['JSON Index']) and !(Conf['JSON Index'] and g.VIEW is 'index') selector = switch g.VIEW @@ -58,13 +59,15 @@ CatalogLinks = return unless (CatalogLinks.enabled ? Conf['Catalog Links']) and list for a in $$('a:not([data-only])', list) - continue if ( - a.hostname not in ['boards.4chan.org', 'boards.4channel.org', 'catalog.neet.tv'] or - !(board = a.pathname.split('/')[1]) or - board in ['f', 'status', '4chan'] or - a.pathname.split('/')[2] is 'archive' or - $.hasClass a, 'external' - ) + unless (board = a.dataset.board) + continue if ( + a.hostname not in ['boards.4chan.org', 'boards.4channel.org'] or + !(board = a.pathname.split('/')[1]) or + board in ['f', 'status', '4chan'] or + a.pathname.split('/')[2] is 'archive' or + $.hasClass a, 'external' + ) + a.dataset.board = board # Href is easier than pathname because then we don't have # conditions where External Catalog has been disabled between switches. @@ -74,9 +77,27 @@ CatalogLinks = a.href += (if a.hash then '/' else '#') + a.dataset.indexOptions return + externalParse: -> + CatalogLinks.externalList = {} + for line in Conf['externalCatalogURLs'].split '\n' + continue if line[0] is '#' + url = line.split(';')[0] + boards = Filter.parseBoards(line.match(/;boards:([^;]+)/)?[1] or '*') + excludes = Filter.parseBoards(line.match(/;exclude:([^;]+)/)?[1]) or {} + for board of boards + unless excludes[board] or excludes[board.split('/')[0] + '/*'] + CatalogLinks.externalList[board] = url + return + + external: ({siteID, boardID}) -> + CatalogLinks.externalParse() unless CatalogLinks.externalList + external = (CatalogLinks.externalList["#{siteID}/#{boardID}"] or CatalogLinks.externalList["#{siteID}/*"]) + if external then external.replace(/%board/g, boardID) else undefined + catalog: (board=g.BOARD.ID) -> - if Conf['External Catalog'] and board in ['3', 'a', 'adv', 'an', 'asp', 'biz', 'c', 'cgl', 'ck', 'cm', 'co', 'diy', 'f', 'fa', 'fit', 'g', 'gd', 'his', 'i', 'int', 'jp', 'k', 'lgbt', 'lit', 'm', 'mlp', 'mu', 'n', 'news', 'o', 'out', 'p', 'po', 'pol', 's4s', 'sci', 'sp', 'tg', 'toy', 'trv', 'tv', 'v', 'vg', 'vip', 'vp', 'vr', 'w', 'wg', 'wsg', 'wsr', 'x'] - "//catalog.neet.tv/#{board}/" + siteID = '4chan.org' + if Conf['External Catalog'] and (external = CatalogLinks.external({siteID, boardID: board})) + external else if Conf['JSON Index'] and Conf['Use <%= meta.name %> Catalog'] if location.hostname in ['boards.4chan.org', 'boards.4channel.org'] and g.BOARD.ID is board and g.VIEW is 'index' then '#catalog' else "//#{BoardConfig.domain(board)}/#{board}/#catalog" else diff --git a/src/config/Config.coffee b/src/config/Config.coffee index 57be795fe..6c13b63dc 100644 --- a/src/config/Config.coffee +++ b/src/config/Config.coffee @@ -825,6 +825,10 @@ Config = lastarchivecheck: 0 archiveAutoUpdate: true + externalCatalogURLs: """ + //catalog.neet.tv/%board/;boards:4chan.org:3,a,adv,an,asp,biz,c,cgl,ck,cm,co,diy,f,fa,fit,g,gd,his,i,int,jp,k,lgbt,lit,m,mlp,mu,n,news,o,out,p,po,pol,s4s,sci,sp,tg,toy,trv,tv,v,vg,vip,vp,vr,w,wg,wsg,wsr,x + """ + boardnav: """ [ toggle-all ] a-replace diff --git a/src/css/style.css b/src/css/style.css index 7fd7f1c70..0ba73a513 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -658,7 +658,8 @@ div[data-checked="false"] > .suboption-list { .section-advanced textarea { height: 150px; } -.section-advanced textarea[name="archiveLists"] { +.section-advanced textarea[name="archiveLists"], +.section-advanced textarea[name="externalCatalogURLs"] { height: 75px; } .section-advanced .archive-cell { From 99d01bf59b114f1be592644ae1ed11d2201b7b12 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Sun, 21 Jul 2019 08:07:49 -0700 Subject: [PATCH 04/33] Link from native catalog to external catalog should read 'External Catalog' --- src/Miscellaneous/CatalogLinks.coffee | 9 +++++---- src/site/SW.tinyboard.coffee | 1 + src/site/SW.yotsuba.coffee | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Miscellaneous/CatalogLinks.coffee b/src/Miscellaneous/CatalogLinks.coffee index e0ae26d5e..982366c7b 100644 --- a/src/Miscellaneous/CatalogLinks.coffee +++ b/src/Miscellaneous/CatalogLinks.coffee @@ -14,10 +14,11 @@ CatalogLinks = link.href = CatalogLinks.index() when "/#{g.BOARD}/catalog" link.href = CatalogLinks.catalog() - if g.VIEW is 'catalog' and Conf['JSON Index'] and Conf['Use <%= meta.name %> Catalog'] + if g.VIEW is 'catalog' and (catalogURL = CatalogLinks.catalog()) isnt g.SITE.urls.catalog?(g.BOARD) catalogLink = link.parentNode.cloneNode true - catalogLink.firstElementChild.textContent = '<%= meta.name %> Catalog' - catalogLink.firstElementChild.href = CatalogLinks.catalog() + link2 = catalogLink.firstElementChild + link2.href = catalogURL + link2.textContent = if link2.hostname is location.hostname then '<%= meta.name %> Catalog' else 'External Catalog' $.after link.parentNode, [$.tn(' '), catalogLink] return @@ -101,7 +102,7 @@ CatalogLinks = else if Conf['JSON Index'] and Conf['Use <%= meta.name %> Catalog'] if location.hostname in ['boards.4chan.org', 'boards.4channel.org'] and g.BOARD.ID is board and g.VIEW is 'index' then '#catalog' else "//#{BoardConfig.domain(board)}/#{board}/#catalog" else - "//#{BoardConfig.domain(board)}/#{board}/catalog" + g.sites[siteID].urls.catalog?({siteID, boardID: board}) index: (board=g.BOARD.ID) -> if Conf['JSON Index'] and board isnt 'f' diff --git a/src/site/SW.tinyboard.coffee b/src/site/SW.tinyboard.coffee index cc78ec912..333bafc1b 100644 --- a/src/site/SW.tinyboard.coffee +++ b/src/site/SW.tinyboard.coffee @@ -47,6 +47,7 @@ SW.tinyboard = urls: thread: ({siteID, boardID, threadID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/res/#{threadID}.html" + catalog: ({siteID, boardID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/catalog.html" threadJSON: ({siteID, boardID, threadID}) -> root = Conf['siteProperties'][siteID]?.root if root then "#{root}#{boardID}/res/#{threadID}.json" else '' diff --git a/src/site/SW.yotsuba.coffee b/src/site/SW.yotsuba.coffee index 38a74994c..ea28a78ac 100644 --- a/src/site/SW.yotsuba.coffee +++ b/src/site/SW.yotsuba.coffee @@ -4,6 +4,7 @@ SW.yotsuba = urls: thread: ({boardID, threadID}) -> "#{location.protocol}//#{BoardConfig.domain(boardID)}/#{boardID}/thread/#{threadID}" + catalog: ({boardID}) -> if boardID is 'f' then undefined else "#{location.protocol}//#{BoardConfig.domain(boardID)}/#{boardID}/catalog" threadJSON: ({boardID, threadID}) -> "#{location.protocol}//a.4cdn.org/#{boardID}/thread/#{threadID}.json" threadsListJSON: ({boardID}) -> "#{location.protocol}//a.4cdn.org/#{boardID}/threads.json" archiveListJSON: ({boardID}) -> if BoardConfig.isArchived(boardID) then "#{location.protocol}//a.4cdn.org/#{boardID}/archive.json" else '' From ee4e72e40fba66a13fb2f534128127dc0d910251 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Sun, 21 Jul 2019 08:47:47 -0700 Subject: [PATCH 05/33] Fire IndexRefresh event on Tinyboard/vichan boards. --- src/General/Index.coffee | 4 ++-- src/site/SW.tinyboard.coffee | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/General/Index.coffee b/src/General/Index.coffee index 7164acd18..d94ffef44 100644 --- a/src/General/Index.coffee +++ b/src/General/Index.coffee @@ -3,13 +3,13 @@ Index = changed: {} init: -> - return unless g.VIEW is 'index' and g.BOARD.ID isnt 'f' + return unless g.VIEW is 'index' # For IndexRefresh events $.one d, '4chanXInitFinished', @cb.initFinished $.on d, 'PostsInserted', @cb.postsInserted - return unless Conf['JSON Index'] + return unless Conf['JSON Index'] and g.SITE.software is 'yotsuba' and g.BOARD.ID isnt 'f' @enabled = true diff --git a/src/site/SW.tinyboard.coffee b/src/site/SW.tinyboard.coffee index 333bafc1b..b848e7bf2 100644 --- a/src/site/SW.tinyboard.coffee +++ b/src/site/SW.tinyboard.coffee @@ -4,7 +4,6 @@ SW.tinyboard = threadModTimeIgnoresSage: true disabledFeatures: [ - 'Index Generator' 'Resurrect Quotes' 'Quick Reply Personas' 'Quick Reply' From 57ab3cf5d13faf8d605431e8a6dc3293818ca6cf Mon Sep 17 00:00:00 2001 From: ccd0 Date: Sun, 21 Jul 2019 10:49:07 -0700 Subject: [PATCH 06/33] Make parsing of location on init available for arbitrary URLs. --- src/main/Main.coffee | 67 +++++++++++++++++------------------- src/site/SW.tinyboard.coffee | 4 +-- src/site/SW.yotsuba.coffee | 33 +++++++++++++++--- src/site/Site.coffee | 24 ++++++++----- 4 files changed, 78 insertions(+), 50 deletions(-) diff --git a/src/main/Main.coffee b/src/main/Main.coffee index 812513430..7cdb7129e 100644 --- a/src/main/Main.coffee +++ b/src/main/Main.coffee @@ -123,11 +123,30 @@ Main = <%= html(meta.name + ' has been updated to version ${g.VERSION}.') %> new Notice 'info', el, 15 - initFeatures: -> - {hostname, search} = location - pathname = location.pathname.split /\/+/ - g.BOARD = new Board pathname[1] unless hostname in ['www.4chan.org', 'www.4channel.org'] + parseURL: (site=g.SITE, url=location) -> + r = {} + return r if !site + r.siteID = site.ID + + return r if site.isBoardlessPage?(url) + pathname = url.pathname.split /\/+/ + r.boardID = pathname[1] + + if site.isFileURL(url) + r.VIEW = 'file' + else if site.isAuxiliaryPage?(url) + # pass + else if pathname[2] in ['thread', 'res'] + r.VIEW = 'thread' + r.threadID = r.THREADID = +pathname[3].replace(/\.\w+$/, '') + else if /^(?:catalog|archive)(?:\.\w+)?$/.test(pathname[2]) + r.VIEW = pathname[2].replace(/\.\w+$/, '') + else if /^(?:index|\d*)(?:\.\w+)?$/.test(pathname[2]) + r.VIEW = 'index' + r + + initFeatures: -> $.global -> document.documentElement.classList.add 'js-enabled' window.FCX = {} @@ -136,29 +155,17 @@ Main = # XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638 $.ajaxPageInit?() - switch hostname - when 'www.4chan.org', 'www.4channel.org' - $.onExists doc, 'body', -> $.addStyle CSS.www - Captcha.replace.init() - return - when 'sys.4chan.org', 'sys.4channel.org' - if pathname[2] is 'imgboard.php' - if /\bmode=report\b/.test search - Report.init() - else if (match = search.match /\bres=(\d+)/) - $.ready -> - if Conf['404 Redirect'] and $.id('errmsg')?.textContent is 'Error: Specified thread does not exist.' - Redirect.navigate 'thread', { - boardID: g.BOARD.ID - postID: +match[1] - } - else if pathname[2] is 'post' - PostSuccessful.init() - return + $.extend g, Main.parseURL() + g.BOARD = new Board g.boardID if g.boardID - if g.SITE.isFileURL() + if !g.VIEW + g.SITE.initAuxiliary?() + return + + if g.VIEW is 'file' $.asap (-> d.readyState isnt 'loading'), -> if g.SITE.software is 'yotsuba' and Conf['404 Redirect'] and g.SITE.is404?() + pathname = location.pathname.split /\/+/ Redirect.navigate 'file', { boardID: g.BOARD.ID filename: pathname[pathname.length - 1] @@ -173,18 +180,6 @@ Main = ImageCommon.addControls video return - return if g.SITE.isAuxiliaryPage?() - - if pathname[2] in ['thread', 'res'] - g.VIEW = 'thread' - g.THREADID = +pathname[3].replace(/\.\w+$/, '') - else if /^(?:catalog|archive)(?:\.\w+)?$/.test(pathname[2]) - g.VIEW = pathname[2].replace(/\.\w+$/, '') - else if /^(?:index|\d*)(?:\.\w+)?$/.test(pathname[2]) - g.VIEW = 'index' - else - return - g.threads = new SimpleDict() g.posts = new SimpleDict() diff --git a/src/site/SW.tinyboard.coffee b/src/site/SW.tinyboard.coffee index b848e7bf2..318d7f207 100644 --- a/src/site/SW.tinyboard.coffee +++ b/src/site/SW.tinyboard.coffee @@ -154,8 +154,8 @@ SW.tinyboard = bgColoredEl: -> $.el 'div', className: 'post reply' - isFileURL: -> - /\/src\/[^\/]+/.test(location.pathname) + isFileURL: (url) -> + /\/src\/[^\/]+/.test(url.pathname) parseNodes: (post, nodes) -> # Add vichan's span.poster_id around the ID if not already present. diff --git a/src/site/SW.yotsuba.coffee b/src/site/SW.yotsuba.coffee index ea28a78ac..d56ea7f08 100644 --- a/src/site/SW.yotsuba.coffee +++ b/src/site/SW.yotsuba.coffee @@ -106,11 +106,36 @@ SW.yotsuba = isIncomplete: -> return g.VIEW in ['index', 'thread'] and not $('.board + *') - isAuxiliaryPage: -> - location.hostname not in ['boards.4chan.org', 'boards.4channel.org'] + isBoardlessPage: (url) -> + url.hostname in ['www.4chan.org', 'www.4channel.org'] - isFileURL: -> - ImageHost.test(location.hostname) + isAuxiliaryPage: (url) -> + url.hostname not in ['boards.4chan.org', 'boards.4channel.org'] + + isFileURL: (url) -> + ImageHost.test(url.hostname) + + initAuxiliary: -> + switch location.hostname + when 'www.4chan.org', 'www.4channel.org' + $.onExists doc, 'body', -> $.addStyle CSS.www + Captcha.replace.init() + return + when 'sys.4chan.org', 'sys.4channel.org' + pathname = location.pathname.split /\/+/ + if pathname[2] is 'imgboard.php' + if /\bmode=report\b/.test location.search + Report.init() + else if (match = location.search.match /\bres=(\d+)/) + $.ready -> + if Conf['404 Redirect'] and $.id('errmsg')?.textContent is 'Error: Specified thread does not exist.' + Redirect.navigate 'thread', { + boardID: g.BOARD.ID + postID: +match[1] + } + else if pathname[2] is 'post' + PostSuccessful.init() + return scriptData: -> for script in $$ 'script:not([src])', d.head diff --git a/src/site/Site.coffee b/src/site/Site.coffee index 33ceaff0e..317f8bed9 100644 --- a/src/site/Site.coffee +++ b/src/site/Site.coffee @@ -6,14 +6,10 @@ Site = init: (cb) -> $.extend Conf['siteProperties'], Site.defaultProperties - {hostname} = location - while hostname and hostname not of Conf['siteProperties'] - hostname = hostname.replace(/^[^.]*\.?/, '') - if hostname - hostname = canonical if (canonical = Conf['siteProperties'][hostname].canonical) - if Conf['siteProperties'][hostname].software of SW - @set hostname - cb() + hostname = Site.resolve() + if hostname and Conf['siteProperties'][hostname].software of SW + @set hostname + cb() $.onExists doc, 'body', => for software of SW when (changes = SW[software].detect?()) changes.software = software @@ -31,6 +27,18 @@ Site = return return + resolve: (url=location) -> + {hostname} = url + while hostname and hostname not of Conf['siteProperties'] + hostname = hostname.replace(/^[^.]*\.?/, '') + if hostname + hostname = canonical if (canonical = Conf['siteProperties'][hostname].canonical) + hostname + + parseURL: (url) -> + siteID = Site.resolve url + Main.parseURL g.sites[siteID], url + set: (hostname) -> for ID, properties of Conf['siteProperties'] continue if properties.canonical From 699c1f7be55f4f180103a276464f04db5ad609b7 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Sun, 21 Jul 2019 17:14:18 -0700 Subject: [PATCH 07/33] Make Catalog Links toggle work on Tinyboard/vichan. #2171 --- src/General/Get.coffee | 2 + src/General/Header.coffee | 7 +++- src/General/Index.coffee | 5 ++- src/Miscellaneous/CatalogLinks.coffee | 59 +++++++++++++++------------ src/site/SW.tinyboard.coffee | 1 + src/site/SW.yotsuba.coffee | 1 + 6 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/General/Get.coffee b/src/General/Get.coffee index b2438e5e7..4dae77b0f 100644 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -1,4 +1,6 @@ Get = + url: (type, IDs, args...) -> + g.sites[IDs.siteID]?.urls[type] IDs, args... threadExcerpt: (thread) -> {OP} = thread excerpt = ("/#{decodeURIComponent thread.board.ID}/ - ") + ( diff --git a/src/General/Header.coffee b/src/General/Header.coffee index 941440aa9..f1bf97a4a 100644 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -234,7 +234,10 @@ Header = href: "/#{g.BOARD.ID}/" textContent: text or g.BOARD.ID className: 'current' - if /-catalog/.test(t) + if /-index/.test(t) + a.dataset.only = 'index' + else if /-catalog/.test(t) + a.dataset.only = 'catalog' a.href += 'catalog.html' else if /-(archive|expired)/.test(t) a = a.firstChild # Its text node. @@ -265,7 +268,7 @@ Header = if m = t.match /-(index|catalog)/ unless boardID is 'f' and m[1] is 'catalog' a.dataset.only = m[1] - a.href = CatalogLinks[m[1]] boardID + a.href = CatalogLinks[m[1]] {siteID: '4chan.org', boardID} $.addClass a, 'catalog' if m[1] is 'catalog' else return a.firstChild # Its text node. diff --git a/src/General/Index.coffee b/src/General/Index.coffee index d94ffef44..1ae8e5bef 100644 --- a/src/General/Index.coffee +++ b/src/General/Index.coffee @@ -2,6 +2,9 @@ Index = showHiddenThreads: false changed: {} + enabledOn: ({siteID, boardID}) -> + Conf['JSON Index'] and g.sites[siteID].software is 'yotsuba' and boardID isnt 'f' + init: -> return unless g.VIEW is 'index' @@ -9,7 +12,7 @@ Index = $.one d, '4chanXInitFinished', @cb.initFinished $.on d, 'PostsInserted', @cb.postsInserted - return unless Conf['JSON Index'] and g.SITE.software is 'yotsuba' and g.BOARD.ID isnt 'f' + return unless @enabledOn g.BOARD @enabled = true diff --git a/src/Miscellaneous/CatalogLinks.coffee b/src/Miscellaneous/CatalogLinks.coffee index 982366c7b..7462f2256 100644 --- a/src/Miscellaneous/CatalogLinks.coffee +++ b/src/Miscellaneous/CatalogLinks.coffee @@ -1,5 +1,4 @@ CatalogLinks = - init: -> if g.SITE.software is 'yotsuba' and (Conf['External Catalog'] or Conf['JSON Index']) and !(Conf['JSON Index'] and g.VIEW is 'index') selector = switch g.VIEW @@ -59,23 +58,26 @@ CatalogLinks = setLinks: (list) -> return unless (CatalogLinks.enabled ? Conf['Catalog Links']) and list + # do not transform links unless they differ from the expected value at most by this tail + tail = /(?:index)?(?:\.\w+)?$/ + for a in $$('a:not([data-only])', list) - unless (board = a.dataset.board) - continue if ( - a.hostname not in ['boards.4chan.org', 'boards.4channel.org'] or - !(board = a.pathname.split('/')[1]) or - board in ['f', 'status', '4chan'] or - a.pathname.split('/')[2] is 'archive' or - $.hasClass a, 'external' + {siteID, boardID} = a.dataset + unless siteID and boardID + {siteID, boardID, VIEW} = Site.parseURL a + continue unless ( + siteID and boardID and + VIEW in ['index', 'catalog'] and + (a.dataset.indexOptions or a.href.replace(tail, '') is Get.url(VIEW, {siteID, boardID}).replace(tail, '')) ) - a.dataset.board = board + $.extend a.dataset, {siteID, boardID} - # Href is easier than pathname because then we don't have - # conditions where External Catalog has been disabled between switches. - a.href = if Conf['Header catalog links'] then CatalogLinks.catalog(board) else "//#{BoardConfig.domain(board)}/#{board}/" - - if a.dataset.indexOptions and a.hostname in ['boards.4chan.org', 'boards.4channel.org'] and a.pathname.split('/')[2] is '' - a.href += (if a.hash then '/' else '#') + a.dataset.indexOptions + board = {siteID, boardID} + url = if Conf['Header catalog links'] then CatalogLinks.catalog(board) else Get.url('index', board) + if url + a.href = url + if a.dataset.indexOptions and url.split('#')[0] is Get.url('index', board) + a.href += (if a.hash then '/' else '#') + a.dataset.indexOptions return externalParse: -> @@ -95,17 +97,22 @@ CatalogLinks = external = (CatalogLinks.externalList["#{siteID}/#{boardID}"] or CatalogLinks.externalList["#{siteID}/*"]) if external then external.replace(/%board/g, boardID) else undefined - catalog: (board=g.BOARD.ID) -> - siteID = '4chan.org' - if Conf['External Catalog'] and (external = CatalogLinks.external({siteID, boardID: board})) - external - else if Conf['JSON Index'] and Conf['Use <%= meta.name %> Catalog'] - if location.hostname in ['boards.4chan.org', 'boards.4channel.org'] and g.BOARD.ID is board and g.VIEW is 'index' then '#catalog' else "//#{BoardConfig.domain(board)}/#{board}/#catalog" + jsonIndex: (board, hash) -> + if g.SITE.ID is board.siteID and g.BOARD.ID is board.boardID and g.VIEW is 'index' + hash else - g.sites[siteID].urls.catalog?({siteID, boardID: board}) + Get.url('index', board) + hash - index: (board=g.BOARD.ID) -> - if Conf['JSON Index'] and board isnt 'f' - if location.hostname in ['boards.4chan.org', 'boards.4channel.org'] and g.BOARD.ID is board and g.VIEW is 'index' then '#index' else "//#{BoardConfig.domain(board)}/#{board}/#index" + catalog: (board=g.BOARD) -> + if Conf['External Catalog'] and (external = CatalogLinks.external board) + external + else if Index.enabledOn(board) and Conf['Use <%= meta.name %> Catalog'] + CatalogLinks.jsonIndex board, '#catalog' else - "//#{BoardConfig.domain(board)}/#{board}/" + Get.url 'catalog', board + + index: (board=g.BOARD) -> + if Index.enabledOn(board) + CatalogLinks.jsonIndex board, '#index' + else + Get.url 'index', board diff --git a/src/site/SW.tinyboard.coffee b/src/site/SW.tinyboard.coffee index 318d7f207..2ef79e827 100644 --- a/src/site/SW.tinyboard.coffee +++ b/src/site/SW.tinyboard.coffee @@ -46,6 +46,7 @@ SW.tinyboard = urls: thread: ({siteID, boardID, threadID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/res/#{threadID}.html" + index: ({siteID, boardID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/" catalog: ({siteID, boardID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/catalog.html" threadJSON: ({siteID, boardID, threadID}) -> root = Conf['siteProperties'][siteID]?.root diff --git a/src/site/SW.yotsuba.coffee b/src/site/SW.yotsuba.coffee index d56ea7f08..e6779212c 100644 --- a/src/site/SW.yotsuba.coffee +++ b/src/site/SW.yotsuba.coffee @@ -4,6 +4,7 @@ SW.yotsuba = urls: thread: ({boardID, threadID}) -> "#{location.protocol}//#{BoardConfig.domain(boardID)}/#{boardID}/thread/#{threadID}" + index: ({boardID}) -> "#{location.protocol}//#{BoardConfig.domain(boardID)}/#{boardID}/" catalog: ({boardID}) -> if boardID is 'f' then undefined else "#{location.protocol}//#{BoardConfig.domain(boardID)}/#{boardID}/catalog" threadJSON: ({boardID, threadID}) -> "#{location.protocol}//a.4cdn.org/#{boardID}/thread/#{threadID}.json" threadsListJSON: ({boardID}) -> "#{location.protocol}//a.4cdn.org/#{boardID}/threads.json" From 898c8e0df9c5ccd76735e569a9a4205fadd70373 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Sun, 21 Jul 2019 17:20:05 -0700 Subject: [PATCH 08/33] Make External Catalog work on /f/. --- src/General/Header.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/General/Header.coffee b/src/General/Header.coffee index f1bf97a4a..f2f003cab 100644 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -266,9 +266,10 @@ Header = text or boardID if m = t.match /-(index|catalog)/ - unless boardID is 'f' and m[1] is 'catalog' + urlIC = CatalogLinks[m[1]] {siteID: '4chan.org', boardID} + if urlIC a.dataset.only = m[1] - a.href = CatalogLinks[m[1]] {siteID: '4chan.org', boardID} + a.href = urlIC $.addClass a, 'catalog' if m[1] is 'catalog' else return a.firstChild # Its text node. From 6a9508371e2cf4ffae7e0ec5c196e160e2509b6c Mon Sep 17 00:00:00 2001 From: ccd0 Date: Sun, 21 Jul 2019 21:00:58 -0700 Subject: [PATCH 09/33] Use external catalog as fallback when there is no native catalog. --- src/General/Settings/Advanced.html | 2 +- src/Miscellaneous/CatalogLinks.coffee | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/General/Settings/Advanced.html b/src/General/Settings/Advanced.html index 03d81f883..25768954b 100644 --- a/src/General/Settings/Advanced.html +++ b/src/General/Settings/Advanced.html @@ -21,7 +21,7 @@
External Catalog -
External Catalog is disabled.
+
External Catalog is disabled. This will be used only as a fallback.
URLs of external catalog sites, where %board is to be replaced by the board name.
Each URL should be followed by ;boards: and optionally ;exclude: and a list of supported/excluded boards in the format explained in the Filter guide. diff --git a/src/Miscellaneous/CatalogLinks.coffee b/src/Miscellaneous/CatalogLinks.coffee index 7462f2256..27af6d5a6 100644 --- a/src/Miscellaneous/CatalogLinks.coffee +++ b/src/Miscellaneous/CatalogLinks.coffee @@ -108,8 +108,10 @@ CatalogLinks = external else if Index.enabledOn(board) and Conf['Use <%= meta.name %> Catalog'] CatalogLinks.jsonIndex board, '#catalog' + else if (nativeCatalog = Get.url 'catalog', board) + nativeCatalog else - Get.url 'catalog', board + CatalogLinks.external board index: (board=g.BOARD) -> if Index.enabledOn(board) From 5814dadcf772b90faa76ceaed550e0fe543f29d0 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Sun, 21 Jul 2019 23:11:49 -0700 Subject: [PATCH 10/33] Make list of banners configurable. --- src/General/Settings/Advanced.html | 6 ++++++ src/Miscellaneous/Banner.coffee | 4 +--- src/config/Config.coffee | 2 ++ src/{Miscellaneous/Banner => config}/banners.json | 0 src/css/style.css | 3 ++- tools/banners.py | 2 +- 6 files changed, 12 insertions(+), 5 deletions(-) rename src/{Miscellaneous/Banner => config}/banners.json (100%) diff --git a/src/General/Settings/Advanced.html b/src/General/Settings/Advanced.html index 25768954b..d0066b3a0 100644 --- a/src/General/Settings/Advanced.html +++ b/src/General/Settings/Advanced.html @@ -183,3 +183,9 @@
+ +
+ Known Banners +
List of known banners, used for click-to-change feature.
+ +
diff --git a/src/Miscellaneous/Banner.coffee b/src/Miscellaneous/Banner.coffee index 271deaa0d..87705d9bd 100644 --- a/src/Miscellaneous/Banner.coffee +++ b/src/Miscellaneous/Banner.coffee @@ -1,6 +1,4 @@ Banner = - banners: `<%= JSON.stringify(readJSON('banners.json')) %>` - init: -> if Conf['Custom Board Titles'] @db = new DataBoard 'customTitles', null, true @@ -44,7 +42,7 @@ Banner = cb: toggle: -> unless Banner.choices?.length - Banner.choices = Banner.banners.slice() + Banner.choices = Conf['knownBanners'].split(',').slice() i = Math.floor(Banner.choices.length * Math.random()) banner = Banner.choices.splice i, 1 $('img', @parentNode).src = "//s.4cdn.org/image/title/#{banner}" diff --git a/src/config/Config.coffee b/src/config/Config.coffee index 6c13b63dc..cc0331269 100644 --- a/src/config/Config.coffee +++ b/src/config/Config.coffee @@ -1181,3 +1181,5 @@ Config = fourchanImageHost: 'i.4cdn.org' hiddenPSAList: [{}] + + knownBanners: '<%= readJSON('banners.json').join(',') %>' diff --git a/src/Miscellaneous/Banner/banners.json b/src/config/banners.json similarity index 100% rename from src/Miscellaneous/Banner/banners.json rename to src/config/banners.json diff --git a/src/css/style.css b/src/css/style.css index 0ba73a513..8447debbd 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -659,7 +659,8 @@ div[data-checked="false"] > .suboption-list { height: 150px; } .section-advanced textarea[name="archiveLists"], -.section-advanced textarea[name="externalCatalogURLs"] { +.section-advanced textarea[name="externalCatalogURLs"], +.section-advanced textarea[name="knownBanners"] { height: 75px; } .section-advanced .archive-cell { diff --git a/tools/banners.py b/tools/banners.py index 192a88479..cfd3bef44 100755 --- a/tools/banners.py +++ b/tools/banners.py @@ -15,5 +15,5 @@ for ext in ['jpg', 'png', 'gif']: print(banner, status) if status == 200: banners.append(banner) -with open('src/Miscellaneous/Banner/banners.json', 'w') as f: +with open('src/config/banners.json', 'w') as f: f.write(json.dumps(banners)) From 0176c350a0c3e256dc573e6759b44b38b5011f1e Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 00:25:28 -0700 Subject: [PATCH 11/33] Remember number of replies to contract thread to instead of hardcoding it for each board. #525 --- src/Miscellaneous/ExpandThread.coffee | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee index 3ac687784..12a07e175 100644 --- a/src/Miscellaneous/ExpandThread.coffee +++ b/src/Miscellaneous/ExpandThread.coffee @@ -58,6 +58,7 @@ ExpandThread = return if @ isnt status.req # aborted delete status.req ExpandThread.parse @, thread, a + status.numReplies = $$(g.SITE.selectors.postContainer + g.SITE.selectors.relative.replyPost, thread.nodes.root).length contract: (thread, a, threadRoot) -> status = ExpandThread.statuses[thread] @@ -69,15 +70,7 @@ ExpandThread = return replies = $$ '.thread > .replyContainer', threadRoot - if !Conf['JSON Index'] or Conf['Show Replies'] - num = if thread.isSticky - 1 - else switch g.BOARD.ID - # XXX boards config - when 'b', 'vg', 'bant' then 3 - when 't' then 1 - else 5 - replies = replies[...-num] + replies = replies[...(-status.numReplies)] if status.numReplies postsCount = 0 filesCount = 0 for reply in replies From 2d0c5b9006e83b07097b832f1d05ebc1471b4418 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 00:37:09 -0700 Subject: [PATCH 12/33] Use Index.enabledOn in Index.menu. --- src/General/Index.coffee | 2 +- src/site/SW.tinyboard.coffee | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/General/Index.coffee b/src/General/Index.coffee index 1ae8e5bef..6cbd4fe54 100644 --- a/src/General/Index.coffee +++ b/src/General/Index.coffee @@ -200,7 +200,7 @@ Index = menu: init: -> - return if g.VIEW isnt 'index' or !Conf['JSON Index'] or !Conf['Menu'] or !Conf['Thread Hiding Link'] or g.BOARD.ID is 'f' + return unless g.VIEW is 'index' and Conf['Menu'] and Conf['Thread Hiding Link'] and Index.enabledOn(g.BOARD) Menu.menu.addEntry el: $.el 'a', diff --git a/src/site/SW.tinyboard.coffee b/src/site/SW.tinyboard.coffee index 2ef79e827..3fa22b003 100644 --- a/src/site/SW.tinyboard.coffee +++ b/src/site/SW.tinyboard.coffee @@ -8,7 +8,6 @@ SW.tinyboard = 'Quick Reply Personas' 'Quick Reply' 'Cooldown' - 'Index Generator (Menu)' 'Report Link' 'Delete Link' 'Edit Link' From 285cd4cb7248f1aede2cbea28285d7bb640cd58f Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 00:48:19 -0700 Subject: [PATCH 13/33] Read boards with custom flags from boards.json. #525 --- 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 2d541b43b..397d7a0ae 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -628,7 +628,7 @@ QR = $.rm nodes.flag delete nodes.flag - if g.BOARD.ID is 'pol' + if g.BOARD.config.troll_flags flag = QR.flags() flag.dataset.name = 'flag' flag.dataset.default = '0' From 35e7500afe29704b0b70ed897376b84a9d0a597e Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 16:30:12 -0700 Subject: [PATCH 14/33] Make Index Navigation keybinds/buttons work on Tinyboard/vichan. #2171 --- src/Miscellaneous/Keybinds.coffee | 6 +----- src/Miscellaneous/Nav.coffee | 9 +++++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index a10896f59..ee1643ba1 100644 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -21,11 +21,7 @@ Keybinds = {target} = e if target.nodeName in ['INPUT', 'TEXTAREA'] return unless /(Esc|Alt|Ctrl|Meta|Shift\+\w{2,})/.test(key) and not /^Alt\+(\d|Up|Down|Left|Right)$/.test(key) - unless ( - g.VIEW not in ['index', 'thread'] or - g.VIEW is 'index' and Conf['JSON Index'] and Conf['Index Mode'] is 'catalog' or - g.VIEW is 'index' and g.BOARD.ID is 'f' - ) + if g.VIEW in ['index', 'thread'] threadRoot = Nav.getThread() if op = $ '.op', threadRoot thread = Get.postFromNode(op).thread diff --git a/src/Miscellaneous/Nav.coffee b/src/Miscellaneous/Nav.coffee index 6b9de63c5..ff2a0885f 100644 --- a/src/Miscellaneous/Nav.coffee +++ b/src/Miscellaneous/Nav.coffee @@ -39,22 +39,23 @@ Nav = Nav.scroll +1 getThread: -> - return $ '.board' if $.hasClass doc, 'catalog-mode' - for threadRoot in $$ '.thread' + return if $.hasClass doc, 'catalog-mode' + for threadRoot in $$ g.SITE.selectors.thread thread = Get.threadFromRoot threadRoot continue if thread.isHidden and !thread.stub if Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height # not scrolled past return threadRoot - return $ '.board' + return scroll: (delta) -> d.activeElement?.blur() thread = Nav.getThread() + return unless thread axis = if delta is +1 'following' else 'preceding' - if next = $.x "#{axis}-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread + if next = $.x "#{axis}-sibling::#{g.SITE.xpath.thread}[not(@hidden)][1]", thread # Unless we're not at the beginning of the current thread, # and thus wanting to move to beginning, # or we're above the first thread and don't want to skip it. From 990bc968bbd62816f555e97158f070c3ceb279d6 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 16:56:12 -0700 Subject: [PATCH 15/33] Simplify and remove hardcoded selector. --- src/Miscellaneous/Keybinds.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index ee1643ba1..277fe2c04 100644 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -23,8 +23,7 @@ Keybinds = return unless /(Esc|Alt|Ctrl|Meta|Shift\+\w{2,})/.test(key) and not /^Alt\+(\d|Up|Down|Left|Right)$/.test(key) if g.VIEW in ['index', 'thread'] threadRoot = Nav.getThread() - if op = $ '.op', threadRoot - thread = Get.postFromNode(op).thread + thread = Get.threadFromRoot threadRoot switch key # QR & Options when Conf['Toggle board list'] From 875f2c06a52832895d90b478226527f1b4f44e71 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 17:13:30 -0700 Subject: [PATCH 16/33] Simplify and remove a needless condition. --- src/Miscellaneous/Keybinds.coffee | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index 277fe2c04..45a0ca636 100644 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -113,10 +113,11 @@ Keybinds = # Images when Conf['Expand image'] return unless ImageExpand.enabled and threadRoot - Keybinds.img threadRoot + post = Get.postFromNode Keybinds.post threadRoot + ImageExpand.toggle post if post.file when Conf['Expand images'] - return unless ImageExpand.enabled and threadRoot - Keybinds.img threadRoot, true + return unless ImageExpand.enabled + ImageExpand.cb.toggleAll() when Conf['Open Gallery'] return unless Gallery.enabled Gallery.cb.toggle() @@ -304,13 +305,6 @@ Keybinds = "" else "sage" - img: (thread, all) -> - if all - ImageExpand.cb.toggleAll() - else - post = Get.postFromNode Keybinds.post thread - ImageExpand.toggle post if post.file - open: (thread, tab) -> return if g.VIEW isnt 'index' url = "/#{thread.board}/thread/#{thread}" From 02fa06d4ef74b2096b64730b0ea2501e900c09ec Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 17:19:33 -0700 Subject: [PATCH 17/33] Nav.getThread() should always return current thread in thread view regardless of scrolling. --- src/Miscellaneous/Nav.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Miscellaneous/Nav.coffee b/src/Miscellaneous/Nav.coffee index ff2a0885f..8cfd93294 100644 --- a/src/Miscellaneous/Nav.coffee +++ b/src/Miscellaneous/Nav.coffee @@ -39,6 +39,7 @@ Nav = Nav.scroll +1 getThread: -> + return g.threads["#{g.BOARD}.#{g.THREADID}"].nodes.root if g.VIEW is 'thread' return if $.hasClass doc, 'catalog-mode' for threadRoot in $$ g.SITE.selectors.thread thread = Get.threadFromRoot threadRoot From 8688bfc9550de8a9a9e7e00456b7040435d40d0e Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 17:35:17 -0700 Subject: [PATCH 18/33] Fix bug from v1.14.10.0 causing full image and thumbnail to briefly appear simultaneously while contracting images and causing unwanted scrolling. (bug from commit 829ef77159652710df48e3a85eef5e097f7052e1) --- src/css/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/css/style.css b/src/css/style.css index 8447debbd..038cef4fa 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -1316,7 +1316,7 @@ textarea.copy-text-element { .expanded-image > .post > .file > .fileThumb > img[data-md5] { display: none; } -.full-image[data-full-i-d] { +.full-image[data-file-i-d] { display: none; cursor: pointer; } From 093bbc9905134738d85870fc8622be43fd26f8fa Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 17:46:15 -0700 Subject: [PATCH 19/33] Fix exception when Thread Watcher keybind pressed on Tinyboard/vichan sites. --- src/Miscellaneous/Keybinds.coffee | 2 +- src/Monitoring/ThreadUpdater.coffee | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index 45a0ca636..00a702ea4 100644 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -88,7 +88,7 @@ Keybinds = when Conf['Update'] switch g.VIEW when 'thread' - return unless Conf['Thread Updater'] + return unless ThreadUpdater.enabled ThreadUpdater.update() when 'index' return unless Conf['JSON Index'] and g.BOARD.ID isnt 'f' diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index d4cbf5abc..de4a9f460 100644 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -1,6 +1,7 @@ ThreadUpdater = init: -> return if g.VIEW isnt 'thread' or !Conf['Thread Updater'] + @enabled = true # Chromium won't play audio created in an inactive tab until the tab has been focused, so set it up now. # XXX Sometimes the loading stalls in Firefox, esp. when opening in private browsing window followed by normal window. From df5a31a26e2f514c6d4446ab435cd92210a62ce8 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 18:28:57 -0700 Subject: [PATCH 20/33] Make index-related keybinds work on Tinyboard/vichan sites. #2171 --- src/Miscellaneous/Keybinds.coffee | 36 +++++++++++++++++-------------- src/site/SW.tinyboard.coffee | 3 +++ src/site/SW.yotsuba.coffee | 7 +++++- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index 00a702ea4..2db3006ea 100644 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -91,7 +91,7 @@ Keybinds = return unless ThreadUpdater.enabled ThreadUpdater.update() when 'index' - return unless Conf['JSON Index'] and g.BOARD.ID isnt 'f' + return unless Index.enabled Index.update() else return @@ -129,47 +129,51 @@ Keybinds = FappeTyme.toggle 'werk' # Board Navigation when Conf['Front page'] - if Conf['JSON Index'] and g.VIEW is 'index' and g.BOARD.ID isnt 'f' + if Index.enabled Index.userPageNav 1 else location.href = "/#{g.BOARD}/" when Conf['Open front page'] $.open "#{location.origin}/#{g.BOARD}/" when Conf['Next page'] - return unless g.VIEW is 'index' and g.BOARD.ID isnt 'f' - if Conf['JSON Index'] + return unless g.VIEW is 'index' and !g.SITE.isOnePage?(g.BOARD) + if Index.enabled return unless Conf['Index Mode'] in ['paged', 'infinite'] $('.next button', Index.pagelist).click() else - if form = $ '.next form' - location.href = form.action + $(g.SITE.selectors.nav.next)?.click() when Conf['Previous page'] - return unless g.VIEW is 'index' and g.BOARD.ID isnt 'f' - if Conf['JSON Index'] + return unless g.VIEW is 'index' and !g.SITE.isOnePage?(g.BOARD) + if Index.enabled return unless Conf['Index Mode'] in ['paged', 'infinite'] $('.prev button', Index.pagelist).click() else - if form = $ '.prev form' - location.href = form.action + $(g.SITE.selectors.nav.prev)?.click() when Conf['Search form'] - return unless g.VIEW is 'index' and g.BOARD.ID isnt 'f' - searchInput = if Conf['JSON Index'] then Index.searchInput else $.id('search-box') + return unless g.VIEW is 'index' + searchInput = if Index.enabled + Index.searchInput + else if g.SITE.selectors.searchBox + $ g.SITE.selectors.searchBox + else + undefined + return unless searchInput Header.scrollToIfNeeded searchInput searchInput.focus() when Conf['Paged mode'] - return unless Conf['JSON Index'] and g.BOARD.ID isnt 'f' + return unless Index.enabledOn(g.BOARD) location.href = if g.VIEW is 'index' then '#paged' else "/#{g.BOARD}/#paged" when Conf['Infinite scrolling mode'] - return unless Conf['JSON Index'] and g.BOARD.ID isnt 'f' + return unless Index.enabledOn(g.BOARD) location.href = if g.VIEW is 'index' then '#infinite' else "/#{g.BOARD}/#infinite" when Conf['All pages mode'] - return unless Conf['JSON Index'] and g.BOARD.ID isnt 'f' + return unless Index.enabledOn(g.BOARD) location.href = if g.VIEW is 'index' then '#all-pages' else "/#{g.BOARD}/#all-pages" when Conf['Open catalog'] return if g.BOARD.ID is 'f' location.href = CatalogLinks.catalog() when Conf['Cycle sort type'] - return unless Conf['JSON Index'] and g.VIEW is 'index' and g.BOARD.ID isnt 'f' + return unless Index.enabled Index.cycleSortType() # Thread Navigation when Conf['Next thread'] diff --git a/src/site/SW.tinyboard.coffee b/src/site/SW.tinyboard.coffee index 3fa22b003..f1f78319f 100644 --- a/src/site/SW.tinyboard.coffee +++ b/src/site/SW.tinyboard.coffee @@ -106,6 +106,9 @@ SW.tinyboard = boardListBottom: '.boardlist.bottom' styleSheet: '#stylesheet' psa: '.blotter' + nav: + prev: '.pages > form > [value=Previous]' + next: '.pages > form > [value=Next]' xpath: thread: 'div[starts-with(@id,"thread_")]' diff --git a/src/site/SW.yotsuba.coffee b/src/site/SW.yotsuba.coffee index e6779212c..87438498b 100644 --- a/src/site/SW.yotsuba.coffee +++ b/src/site/SW.yotsuba.coffee @@ -16,8 +16,9 @@ SW.yotsuba = thumb: ({boardID}, filename) -> "#{location.protocol}//#{ImageHost.thumbHost()}/#{boardID}/#{filename}" - isPrunedByAge: ({boardID}) -> boardID is 'f' + isPrunedByAge: ({boardID}) -> boardID is 'f' areMD5sDeferred: ({boardID}) -> boardID is 'f' + isOnePage: ({boardID}) -> boardID is 'f' noAudio: ({boardID}) -> BoardConfig.noAudio(boardID) selectors: @@ -69,6 +70,10 @@ SW.yotsuba = styleSheet: 'link[title=switch]' psa: '#globalMessage' psaTop: '#globalToggle' + searchBox: '#search-box' + nav: + prev: '.prev > form > [type=submit]' + next: '.next > form > [type=submit]' xpath: thread: 'div[contains(concat(" ",@class," ")," thread ")]' From 576a223fc8085017f815c477868b01673a6446f0 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 18:36:18 -0700 Subject: [PATCH 21/33] Make catalog keybind work on /f/. --- src/Miscellaneous/Keybinds.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index 2db3006ea..f2b043a66 100644 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -170,8 +170,8 @@ Keybinds = return unless Index.enabledOn(g.BOARD) location.href = if g.VIEW is 'index' then '#all-pages' else "/#{g.BOARD}/#all-pages" when Conf['Open catalog'] - return if g.BOARD.ID is 'f' - location.href = CatalogLinks.catalog() + return unless (catalog = CatalogLinks.catalog()) + location.href = catalog when Conf['Cycle sort type'] return unless Index.enabled Index.cycleSortType() From 4e84b284cac4d2866d5af54fb5c4037193c21d1f Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 19:21:37 -0700 Subject: [PATCH 22/33] Rename site.selectors.relative.replyPost to site.selectors.relative.replyHighlight --- src/Filtering/ThreadHiding.coffee | 2 +- src/Miscellaneous/ExpandThread.coffee | 2 +- src/css/spooky.css | 6 +++--- src/css/style.css | 6 +++--- src/css/tomorrow.css | 6 +++--- src/site/SW.tinyboard.coffee | 6 +++--- src/site/SW.yotsuba.coffee | 6 +++--- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Filtering/ThreadHiding.coffee b/src/Filtering/ThreadHiding.coffee index bf3329246..ac298a308 100644 --- a/src/Filtering/ThreadHiding.coffee +++ b/src/Filtering/ThreadHiding.coffee @@ -145,7 +145,7 @@ ThreadHiding = a makeStub: (thread, root) -> - numReplies = $$(g.SITE.selectors.postContainer + g.SITE.selectors.relative.replyPost, root).length + numReplies = $$(g.SITE.selectors.postContainer + g.SITE.selectors.relative.replyHighlight, root).length numReplies += +summary.textContent.match /\d+/ if summary = $ g.SITE.selectors.summary, root a = ThreadHiding.makeButton thread, 'show' diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee index 12a07e175..1bf08e752 100644 --- a/src/Miscellaneous/ExpandThread.coffee +++ b/src/Miscellaneous/ExpandThread.coffee @@ -58,7 +58,7 @@ ExpandThread = return if @ isnt status.req # aborted delete status.req ExpandThread.parse @, thread, a - status.numReplies = $$(g.SITE.selectors.postContainer + g.SITE.selectors.relative.replyPost, thread.nodes.root).length + status.numReplies = $$(g.SITE.selectors.postContainer + g.SITE.selectors.relative.replyHighlight, thread.nodes.root).length contract: (thread, a, threadRoot) -> status = ExpandThread.statuses[thread] diff --git a/src/css/spooky.css b/src/css/spooky.css index 89ad1b3f7..f3e11afb9 100644 --- a/src/css/spooky.css +++ b/src/css/spooky.css @@ -68,15 +68,15 @@ outline: 2px solid rgba(145, 182, 214, .8); } :root.spooky.highlight-you .quotesYou$site$relative$opHighlight, -:root.spooky.highlight-you .quotesYou$site$relative$replyPost { +:root.spooky.highlight-you .quotesYou$site$relative$replyHighlight { border-left: 3px solid rgba(145, 182, 214, .8); } :root.spooky.highlight-own .yourPost$site$relative$opHighlight, -:root.spooky.highlight-own .yourPost$site$relative$replyPost { +:root.spooky.highlight-own .yourPost$site$relative$replyHighlight { border-left: 3px dashed rgba(145, 182, 214, .8); } :root.spooky .filter-highlight$site$relative$opHighlight, -:root.spooky .filter-highlight$site$relative$replyPost { +:root.spooky .filter-highlight$site$relative$replyHighlight { box-shadow: inset 5px 0 rgba(145, 182, 214, .5); } :root.spooky.highlight-own .yourPost > $site$sideArrows, diff --git a/src/css/style.css b/src/css/style.css index 038cef4fa..5928b614e 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -1441,15 +1441,15 @@ input[name="Default Volume"] { outline: 2px solid rgba(216, 94, 49, .8); } :root.highlight-you .quotesYou$site$relative$opHighlight, -:root.highlight-you .quotesYou$site$relative$replyPost { +:root.highlight-you .quotesYou$site$relative$replyHighlight { border-left: 3px solid rgba(221, 0, 0, .8); } :root.highlight-own .yourPost$site$relative$opHighlight, -:root.highlight-own .yourPost$site$relative$replyPost { +:root.highlight-own .yourPost$site$relative$replyHighlight { border-left: 3px dashed rgba(221, 0, 0, .8); } .filter-highlight$site$relative$opHighlight, -.filter-highlight$site$relative$replyPost { +.filter-highlight$site$relative$replyHighlight { box-shadow: inset 5px 0 rgba(221, 0, 0, .5); } :root.highlight-own .yourPost > $site$sideArrows, diff --git a/src/css/tomorrow.css b/src/css/tomorrow.css index d1b2ad5af..3220cc5eb 100644 --- a/src/css/tomorrow.css +++ b/src/css/tomorrow.css @@ -64,15 +64,15 @@ outline: 2px solid rgba(145, 182, 214, .8); } :root.tomorrow.highlight-you .quotesYou$site$relative$opHighlight, -:root.tomorrow.highlight-you .quotesYou$site$relative$replyPost { +:root.tomorrow.highlight-you .quotesYou$site$relative$replyHighlight { border-left: 3px solid rgba(145, 182, 214, .8); } :root.tomorrow.highlight-own .yourPost$site$relative$opHighlight, -:root.tomorrow.highlight-own .yourPost$site$relative$replyPost { +:root.tomorrow.highlight-own .yourPost$site$relative$replyHighlight { border-left: 3px dashed rgba(145, 182, 214, .8); } :root.tomorrow .filter-highlight$site$relative$opHighlight, -:root.tomorrow .filter-highlight$site$relative$replyPost { +:root.tomorrow .filter-highlight$site$relative$replyHighlight { box-shadow: inset 5px 0 rgba(145, 182, 214, .5); } :root.tomorrow.highlight-own .yourPost > $site$sideArrows, diff --git a/src/site/SW.tinyboard.coffee b/src/site/SW.tinyboard.coffee index f1f78319f..038248470 100644 --- a/src/site/SW.tinyboard.coffee +++ b/src/site/SW.tinyboard.coffee @@ -91,9 +91,9 @@ SW.tinyboard = thumbLink: '.file > a' multifile: '.files > .file' relative: - opHighlight: ' > .op' - replyPost: '.reply' - replyOriginal: 'div[id^="reply_"]:not(.hidden)' + opHighlight: ' > .op' + replyHighlight: '.reply' + replyOriginal: 'div[id^="reply_"]:not(.hidden)' catalogHighlight: ' > .thread' comment: '.body' spoiler: '.spoiler' diff --git a/src/site/SW.yotsuba.coffee b/src/site/SW.yotsuba.coffee index 87438498b..c074c2e67 100644 --- a/src/site/SW.yotsuba.coffee +++ b/src/site/SW.yotsuba.coffee @@ -54,9 +54,9 @@ SW.yotsuba = thumb: 'a.fileThumb > [data-md5]' thumbLink: 'a.fileThumb' relative: - opHighlight: '.opContainer' - replyPost: ' > .reply' - replyOriginal: '.replyContainer:not([data-clone])' + opHighlight: '.opContainer' + replyHighlight: ' > .reply' + replyOriginal: '.replyContainer:not([data-clone])' catalogHighlight: '' comment: '.postMessage' spoiler: 's' From 9a3051ba71396441ecfe3ae2b6a1457ad5c80c6d Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 19:30:44 -0700 Subject: [PATCH 23/33] Move replyOriginal up level in hierarchy. --- src/css/style.css | 4 ++-- src/site/SW.tinyboard.coffee | 2 +- src/site/SW.yotsuba.coffee | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/css/style.css b/src/css/style.css index 5928b614e..9147806b8 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -1385,8 +1385,8 @@ input[name="Default Volume"] { margin: 0px; } /* Fappe and Werk Tyme */ -:root.fappeTyme $site$relative$replyOriginal.noFile, -:root.fappeTyme $site$relative$replyOriginal.noFile + br { +:root.fappeTyme $site$replyOriginal.noFile, +:root.fappeTyme $site$replyOriginal.noFile + br { display: none; } :root.werkTyme $site$thumbLink, diff --git a/src/site/SW.tinyboard.coffee b/src/site/SW.tinyboard.coffee index 038248470..3ceb60060 100644 --- a/src/site/SW.tinyboard.coffee +++ b/src/site/SW.tinyboard.coffee @@ -68,6 +68,7 @@ SW.tinyboard = summary: '.omitted' postContainer: 'div[id^="reply_"]:not(.hidden)' # postContainer is thread for OP opBottom: '.op' + replyOriginal: 'div[id^="reply_"]:not(.hidden)' infoRoot: '.intro' info: subject: '.subject' @@ -93,7 +94,6 @@ SW.tinyboard = relative: opHighlight: ' > .op' replyHighlight: '.reply' - replyOriginal: 'div[id^="reply_"]:not(.hidden)' catalogHighlight: ' > .thread' comment: '.body' spoiler: '.spoiler' diff --git a/src/site/SW.yotsuba.coffee b/src/site/SW.yotsuba.coffee index c074c2e67..331527677 100644 --- a/src/site/SW.yotsuba.coffee +++ b/src/site/SW.yotsuba.coffee @@ -27,6 +27,7 @@ SW.yotsuba = threadDivider: '.board > hr' summary: '.summary' postContainer: '.postContainer' + replyOriginal: '.replyContainer:not([data-clone])' sideArrows: 'div.sideArrows' post: '.post' infoRoot: '.postInfo' @@ -56,7 +57,6 @@ SW.yotsuba = relative: opHighlight: '.opContainer' replyHighlight: ' > .reply' - replyOriginal: '.replyContainer:not([data-clone])' catalogHighlight: '' comment: '.postMessage' spoiler: 's' From ce099ae6de69e305693bac1f9a8cf38670a50614 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 19:35:40 -0700 Subject: [PATCH 24/33] Use g.SITE.selectors.replyOriginal to count replies. --- src/Filtering/ThreadHiding.coffee | 2 +- src/Miscellaneous/ExpandThread.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Filtering/ThreadHiding.coffee b/src/Filtering/ThreadHiding.coffee index ac298a308..4e250227a 100644 --- a/src/Filtering/ThreadHiding.coffee +++ b/src/Filtering/ThreadHiding.coffee @@ -145,7 +145,7 @@ ThreadHiding = a makeStub: (thread, root) -> - numReplies = $$(g.SITE.selectors.postContainer + g.SITE.selectors.relative.replyHighlight, root).length + numReplies = $$(g.SITE.selectors.replyOriginal, root).length numReplies += +summary.textContent.match /\d+/ if summary = $ g.SITE.selectors.summary, root a = ThreadHiding.makeButton thread, 'show' diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee index 1bf08e752..6accc9719 100644 --- a/src/Miscellaneous/ExpandThread.coffee +++ b/src/Miscellaneous/ExpandThread.coffee @@ -58,7 +58,7 @@ ExpandThread = return if @ isnt status.req # aborted delete status.req ExpandThread.parse @, thread, a - status.numReplies = $$(g.SITE.selectors.postContainer + g.SITE.selectors.relative.replyHighlight, thread.nodes.root).length + status.numReplies = $$(g.SITE.selectors.replyOriginal, thread.nodes.root).length contract: (thread, a, threadRoot) -> status = ExpandThread.statuses[thread] From 114d234c87269225499d92fc3c4bc269a75f3163 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 19:58:06 -0700 Subject: [PATCH 25/33] Rename site.selectors.relative to site.selectors.highlightable --- src/css/spooky.css | 12 ++++++------ src/css/style.css | 20 ++++++++++---------- src/css/tomorrow.css | 12 ++++++------ src/site/SW.tinyboard.coffee | 8 ++++---- src/site/SW.yotsuba.coffee | 8 ++++---- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/css/spooky.css b/src/css/spooky.css index f3e11afb9..0b698b178 100644 --- a/src/css/spooky.css +++ b/src/css/spooky.css @@ -67,16 +67,16 @@ :root.spooky .qphl { outline: 2px solid rgba(145, 182, 214, .8); } -:root.spooky.highlight-you .quotesYou$site$relative$opHighlight, -:root.spooky.highlight-you .quotesYou$site$relative$replyHighlight { +:root.spooky.highlight-you .quotesYou$site$highlightable$op, +:root.spooky.highlight-you .quotesYou$site$highlightable$reply { border-left: 3px solid rgba(145, 182, 214, .8); } -:root.spooky.highlight-own .yourPost$site$relative$opHighlight, -:root.spooky.highlight-own .yourPost$site$relative$replyHighlight { +:root.spooky.highlight-own .yourPost$site$highlightable$op, +:root.spooky.highlight-own .yourPost$site$highlightable$reply { border-left: 3px dashed rgba(145, 182, 214, .8); } -:root.spooky .filter-highlight$site$relative$opHighlight, -:root.spooky .filter-highlight$site$relative$replyHighlight { +:root.spooky .filter-highlight$site$highlightable$op, +:root.spooky .filter-highlight$site$highlightable$reply { box-shadow: inset 5px 0 rgba(145, 182, 214, .5); } :root.spooky.highlight-own .yourPost > $site$sideArrows, diff --git a/src/css/style.css b/src/css/style.css index 9147806b8..a86d8c0e2 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -1440,16 +1440,16 @@ input[name="Default Volume"] { .qphl { outline: 2px solid rgba(216, 94, 49, .8); } -:root.highlight-you .quotesYou$site$relative$opHighlight, -:root.highlight-you .quotesYou$site$relative$replyHighlight { +:root.highlight-you .quotesYou$site$highlightable$op, +:root.highlight-you .quotesYou$site$highlightable$reply { border-left: 3px solid rgba(221, 0, 0, .8); } -:root.highlight-own .yourPost$site$relative$opHighlight, -:root.highlight-own .yourPost$site$relative$replyHighlight { +:root.highlight-own .yourPost$site$highlightable$op, +:root.highlight-own .yourPost$site$highlightable$reply { border-left: 3px dashed rgba(221, 0, 0, .8); } -.filter-highlight$site$relative$opHighlight, -.filter-highlight$site$relative$replyHighlight { +.filter-highlight$site$highlightable$op, +.filter-highlight$site$highlightable$reply { box-shadow: inset 5px 0 rgba(221, 0, 0, .5); } :root.highlight-own .yourPost > $site$sideArrows, @@ -1457,9 +1457,9 @@ input[name="Default Volume"] { .filter-highlight > $site$sideArrows { color: rgba(221, 0, 0, .8); } -:root.highlight-own .yourPost$site$relative$opHighlight::after, -:root.highlight-you .quotesYou$site$relative$opHighlight::after, -.filter-highlight$site$relative$opHighlight::after { +:root.highlight-own .yourPost$site$highlightable$op::after, +:root.highlight-you .quotesYou$site$highlightable$op::after, +.filter-highlight$site$highlightable$op::after { content: ""; display: block; clear: both; @@ -1468,7 +1468,7 @@ input[name="Default Volume"] { :root.werkTyme .catalog-thread.filter-highlight:not(:hover), :root.werkTyme:not(.catalog-hover-expand) .catalog-thread.filter-highlight, :root.werkTyme.catalog-hover-expand .catalog-thread.filter-highlight > .catalog-container:hover > .catalog-post, -:root.catalog $site$catalog$thread.filter-highlight$site$relative$catalogHighlight { +:root.catalog $site$catalog$thread.filter-highlight$site$highlightable$catalog { box-shadow: 0 0 3px 3px rgba(255, 0, 0, .5); } :root:not(.werkTyme) .catalog-thread.watched .catalog-thumb, diff --git a/src/css/tomorrow.css b/src/css/tomorrow.css index 3220cc5eb..8a1b8b675 100644 --- a/src/css/tomorrow.css +++ b/src/css/tomorrow.css @@ -63,16 +63,16 @@ :root.tomorrow .qphl { outline: 2px solid rgba(145, 182, 214, .8); } -:root.tomorrow.highlight-you .quotesYou$site$relative$opHighlight, -:root.tomorrow.highlight-you .quotesYou$site$relative$replyHighlight { +:root.tomorrow.highlight-you .quotesYou$site$highlightable$op, +:root.tomorrow.highlight-you .quotesYou$site$highlightable$reply { border-left: 3px solid rgba(145, 182, 214, .8); } -:root.tomorrow.highlight-own .yourPost$site$relative$opHighlight, -:root.tomorrow.highlight-own .yourPost$site$relative$replyHighlight { +:root.tomorrow.highlight-own .yourPost$site$highlightable$op, +:root.tomorrow.highlight-own .yourPost$site$highlightable$reply { border-left: 3px dashed rgba(145, 182, 214, .8); } -:root.tomorrow .filter-highlight$site$relative$opHighlight, -:root.tomorrow .filter-highlight$site$relative$replyHighlight { +:root.tomorrow .filter-highlight$site$highlightable$op, +:root.tomorrow .filter-highlight$site$highlightable$reply { box-shadow: inset 5px 0 rgba(145, 182, 214, .5); } :root.tomorrow.highlight-own .yourPost > $site$sideArrows, diff --git a/src/site/SW.tinyboard.coffee b/src/site/SW.tinyboard.coffee index 3ceb60060..948a7729c 100644 --- a/src/site/SW.tinyboard.coffee +++ b/src/site/SW.tinyboard.coffee @@ -91,10 +91,10 @@ SW.tinyboard = thumb: 'a > .post-image' thumbLink: '.file > a' multifile: '.files > .file' - relative: - opHighlight: ' > .op' - replyHighlight: '.reply' - catalogHighlight: ' > .thread' + highlightable: + op: ' > .op' + reply: '.reply' + catalog: ' > .thread' comment: '.body' spoiler: '.spoiler' quotelink: 'a[onclick^="highlightReply("]' diff --git a/src/site/SW.yotsuba.coffee b/src/site/SW.yotsuba.coffee index 331527677..2cc8160d0 100644 --- a/src/site/SW.yotsuba.coffee +++ b/src/site/SW.yotsuba.coffee @@ -54,10 +54,10 @@ SW.yotsuba = link: '.fileText > a' thumb: 'a.fileThumb > [data-md5]' thumbLink: 'a.fileThumb' - relative: - opHighlight: '.opContainer' - replyHighlight: ' > .reply' - catalogHighlight: '' + highlightable: + op: '.opContainer' + reply: ' > .reply' + catalog: '' comment: '.postMessage' spoiler: 's' quotelink: ':not(pre) > .quotelink' # XXX https://github.com/4chan/4chan-JS/issues/77: 4chan currently creates quote links inside [code] tags; ignore them From 4da095626c6da6dba1437a7db71112b69f4f94f7 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 20:54:10 -0700 Subject: [PATCH 26/33] Make reply navigation keybinds work on Tinyboard/vichan. #2171 --- src/Miscellaneous/Keybinds.coffee | 32 ++++++++++++++++++------------- src/site/SW.tinyboard.coffee | 8 ++++++-- src/site/SW.yotsuba.coffee | 8 ++++++-- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index f2b043a66..ce4cae17c 100644 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -269,7 +269,11 @@ Keybinds = key post: (thread) -> - $('.post.highlight', thread) or $('.op', thread) + s = g.SITE.selectors + ( + $("#{s.postContainer}#{s.highlightable.reply}.#{g.SITE.classes.highlight}", thread) or + $("#{if g.SITE.isOPContainerThread then s.thread else s.postContainer}#{s.highlightable.op}", thread) + ) qr: (thread) -> QR.open() @@ -318,33 +322,35 @@ Keybinds = location.href = url hl: (delta, thread) -> - postEl = $ '.reply.highlight', thread + replySelector = "#{g.SITE.selectors.postContainer}#{g.SITE.selectors.highlightable.reply}" + {highlight} = g.SITE.classes + + postEl = $ "#{replySelector}.#{highlight}", thread unless delta - $.rmClass postEl, 'highlight' if postEl + $.rmClass postEl, highlight if postEl return if postEl {height} = postEl.getBoundingClientRect() if Header.getTopOf(postEl) >= -height and Header.getBottomOf(postEl) >= -height # We're at least partially visible - root = postEl.parentNode + {root} = Get.postFromNode(postEl).nodes axis = if delta is +1 'following' else 'preceding' - return if not (next = $.x "#{axis}-sibling::div[contains(@class,'replyContainer') and not(@hidden) and not(child::div[@class='stub'])][1]/child::div[contains(@class,'reply')]", root) + return unless (next = $.x "#{axis}-sibling::#{g.SITE.xpath.replyContainer}[not(@hidden) and not(child::div[@class='stub'])][1]", root) + next = $ replySelector, next unless next.matches(replySelector) Header.scrollToIfNeeded next, delta is +1 - @focus next - $.rmClass postEl, 'highlight' + $.addClass next, highlight + $.rmClass postEl, highlight return - $.rmClass postEl, 'highlight' + $.rmClass postEl, highlight - replies = $$ '.reply', thread + replies = $$ replySelector, thread replies.reverse() if delta is -1 for reply in replies if delta is +1 and Header.getTopOf(reply) > 0 or delta is -1 and Header.getBottomOf(reply) > 0 - @focus reply + $.addClass reply, highlight return - - focus: (post) -> - $.addClass post, 'highlight' + return diff --git a/src/site/SW.tinyboard.coffee b/src/site/SW.tinyboard.coffee index 948a7729c..74aea824e 100644 --- a/src/site/SW.tinyboard.coffee +++ b/src/site/SW.tinyboard.coffee @@ -110,9 +110,13 @@ SW.tinyboard = prev: '.pages > form > [value=Previous]' next: '.pages > form > [value=Next]' + classes: + highlight: 'highlighted' + xpath: - thread: 'div[starts-with(@id,"thread_")]' - postContainer: 'div[starts-with(@id,"reply_") or starts-with(@id,"thread_")]' + thread: 'div[starts-with(@id,"thread_")]' + postContainer: 'div[starts-with(@id,"reply_") or starts-with(@id,"thread_")]' + replyContainer: 'div[starts-with(@id,"reply_")]' regexp: quotelink: diff --git a/src/site/SW.yotsuba.coffee b/src/site/SW.yotsuba.coffee index 2cc8160d0..d9af44255 100644 --- a/src/site/SW.yotsuba.coffee +++ b/src/site/SW.yotsuba.coffee @@ -75,9 +75,13 @@ SW.yotsuba = prev: '.prev > form > [type=submit]' next: '.next > form > [type=submit]' + classes: + highlight: 'highlight' + xpath: - thread: 'div[contains(concat(" ",@class," ")," thread ")]' - postContainer: 'div[contains(@class,"postContainer")]' + thread: 'div[contains(concat(" ",@class," ")," thread ")]' + postContainer: 'div[contains(@class,"postContainer")]' + replyContainer: 'div[contains(@class,"replyContainer")]' regexp: quotelink: From 23e1bceba1a3826e77bd0f052b3a066a096c8539 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 21:33:23 -0700 Subject: [PATCH 27/33] Don't try to quick add MD5s if there's no MD5 available. --- src/Filtering/Filter.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Filtering/Filter.coffee b/src/Filtering/Filter.coffee index f5790e148..2d62b6320 100644 --- a/src/Filtering/Filter.coffee +++ b/src/Filtering/Filter.coffee @@ -248,8 +248,9 @@ Filter = quickFilterMD5: -> post = Get.postFromNode @ - return unless post.file - Filter.addFilter 'MD5', "/#{post.file.MD5}/" + files = post.files.filter((f) -> f.MD5) + return unless files.length + Filter.addFilter 'MD5', files.map((f) -> "/#{f.MD5}/").join('\n') origin = post.origin or post if origin.isReply PostHiding.hide origin From 1272b52451d8feb648f542a584115d58ade8e2c6 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 22:31:10 -0700 Subject: [PATCH 28/33] Display notification when MD5s are quick filtered with link to undo. #2221 --- src/Filtering/Filter.coffee | 64 +++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/src/Filtering/Filter.coffee b/src/Filtering/Filter.coffee index 2d62b6320..ba09d260f 100644 --- a/src/Filtering/Filter.coffee +++ b/src/Filtering/Filter.coffee @@ -246,19 +246,64 @@ Filter = re $.set type, save, cb + removeFilters: (type, res, cb) -> + $.get type, Conf[type], (item) -> + save = item[type] + res = res.map(Filter.escape).join('|') + save = save.replace RegExp("(?:$\n|^)(?:#{res})$", 'mg'), '' + $.set type, save, cb + + showFilters: (type) -> + # Open the settings and display & focus the relevant filter textarea. + Settings.open 'Filter' + section = $ '.section-container' + select = $ 'select[name=filter]', section + select.value = type + Settings.selectFilter.call select + $.onExists section, 'textarea', (ta) -> + tl = ta.textLength + ta.setSelectionRange tl, tl + ta.focus() + quickFilterMD5: -> post = Get.postFromNode @ files = post.files.filter((f) -> f.MD5) return unless files.length - Filter.addFilter 'MD5', files.map((f) -> "/#{f.MD5}/").join('\n') + filter = files.map((f) -> "/#{f.MD5}/").join('\n') + Filter.addFilter 'MD5', filter origin = post.origin or post if origin.isReply PostHiding.hide origin else if g.VIEW is 'index' ThreadHiding.hide origin.thread - # If post is still visible, give an indication that the MD5 was filtered. - if post.nodes.post.getBoundingClientRect().height - new Notice 'info', 'MD5 filtered.', 2 + {notice} = Filter.quickFilterMD5 + if notice + notice.filters.push filter + notice.posts.push origin + $('span', notice.el).textContent = "#{notice.filters.length} MD5s filtered." + else + msg = $.el 'div', + <%= html('MD5 filtered. [show] [undo]') %> + notice = Filter.quickFilterMD5.notice = new Notice 'info', msg, undefined, -> + delete Filter.quickFilterMD5.notice + notice.filters = [filter] + notice.posts = [origin] + links = $$ 'a', msg + $.on links[0], 'click', Filter.quickFilterCB.show.bind(notice) + $.on links[1], 'click', Filter.quickFilterCB.undo.bind(notice) + + quickFilterCB: + show: -> + Filter.showFilters 'MD5' + @close() + undo: -> + Filter.removeFilters 'MD5', @filters + for post in @posts + if post.isReply + PostHiding.show post + else if g.VIEW is 'index' + ThreadHiding.show post.thread + @close() escape: (value) -> value.replace /// @@ -347,13 +392,4 @@ Filter = ).join('\n') Filter.addFilter type, res, -> - # Open the settings and display & focus the relevant filter textarea. - Settings.open 'Filter' - section = $ '.section-container' - select = $ 'select[name=filter]', section - select.value = type - Settings.selectFilter.call select - $.onExists section, 'textarea', (ta) -> - tl = ta.textLength - ta.setSelectionRange tl, tl - ta.focus() + Filter.showFilters type From 4b3e2c6e131dcce0c5c25c3f1e9cda81bf041ee6 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 23:31:58 -0700 Subject: [PATCH 29/33] Don't need sw-yotsuba selector here since stylesheet classes are only applied on 4chan. --- src/css/photon.css | 2 +- src/css/spooky.css | 2 +- src/css/tomorrow.css | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/css/photon.css b/src/css/photon.css index bda9dfafb..4c3fd4b28 100644 --- a/src/css/photon.css +++ b/src/css/photon.css @@ -9,7 +9,7 @@ } /* 4chan style fixes */ -:root.photon.sw-yotsuba #arc-list tr:nth-of-type(odd) span.quote { +:root.photon #arc-list tr:nth-of-type(odd) span.quote { color: #C0E17A; } diff --git a/src/css/spooky.css b/src/css/spooky.css index 0b698b178..874d22c7e 100644 --- a/src/css/spooky.css +++ b/src/css/spooky.css @@ -9,7 +9,7 @@ } /* 4chan style fixes */ -:root.spooky.sw-yotsuba #arc-list span.quote { +:root.spooky #arc-list span.quote { color: #634C2C; } diff --git a/src/css/tomorrow.css b/src/css/tomorrow.css index 8a1b8b675..0a561ed97 100644 --- a/src/css/tomorrow.css +++ b/src/css/tomorrow.css @@ -5,7 +5,7 @@ } /* 4chan style fixes */ -:root.tomorrow.sw-yotsuba #arc-list span.quote { +:root.tomorrow #arc-list span.quote { color: #B5BD68; } From 4649ae8a8226ac258448a37f107aeb95ea3e371a Mon Sep 17 00:00:00 2001 From: ccd0 Date: Mon, 22 Jul 2019 23:53:17 -0700 Subject: [PATCH 30/33] Fix indication of posts by/quoting you when post is highlighted. #585 --- src/css/photon.css | 6 ++++++ src/css/spooky.css | 6 ++++++ src/css/tomorrow.css | 6 ++++++ src/css/yotsuba-b.css | 8 ++++++++ src/css/yotsuba.css | 8 ++++++++ 5 files changed, 34 insertions(+) diff --git a/src/css/photon.css b/src/css/photon.css index 4c3fd4b28..4cb64bc66 100644 --- a/src/css/photon.css +++ b/src/css/photon.css @@ -12,6 +12,12 @@ :root.photon #arc-list tr:nth-of-type(odd) span.quote { color: #C0E17A; } +:root.photon.highlight-you .quotesYou$site$highlightable$reply { + border-left: 3px solid rgba(221, 0, 0, .8) !important; +} +:root.photon.highlight-own .yourPost$site$highlightable$reply { + border-left: 3px dashed rgba(221, 0, 0, .8) !important; +} /* Header */ :root.photon #header-bar.dialog { diff --git a/src/css/spooky.css b/src/css/spooky.css index 874d22c7e..7211d6a52 100644 --- a/src/css/spooky.css +++ b/src/css/spooky.css @@ -12,6 +12,12 @@ :root.spooky #arc-list span.quote { color: #634C2C; } +:root.spooky.highlight-you .quotesYou$site$highlightable$reply { + border-left: 3px solid rgba(145, 182, 214, .8) !important; +} +:root.spooky.highlight-own .yourPost$site$highlightable$reply { + border-left: 3px dashed rgba(145, 182, 214, .8) !important; +} /* Header */ :root.spooky #header-bar.dialog { diff --git a/src/css/tomorrow.css b/src/css/tomorrow.css index 0a561ed97..e3ce82ddd 100644 --- a/src/css/tomorrow.css +++ b/src/css/tomorrow.css @@ -8,6 +8,12 @@ :root.tomorrow #arc-list span.quote { color: #B5BD68; } +:root.tomorrow.highlight-you .quotesYou$site$highlightable$reply { + border-left: 3px solid rgba(145, 182, 214, .8) !important; +} +:root.tomorrow.highlight-own .yourPost$site$highlightable$reply { + border-left: 3px dashed rgba(145, 182, 214, .8) !important; +} /* Header */ :root.tomorrow #header-bar.dialog { diff --git a/src/css/yotsuba-b.css b/src/css/yotsuba-b.css index abfafa5f7..b04f9246c 100644 --- a/src/css/yotsuba-b.css +++ b/src/css/yotsuba-b.css @@ -8,6 +8,14 @@ border-color: #98E; } +/* 4chan style fixes */ +:root.yotsuba-b.highlight-you .quotesYou$site$highlightable$reply { + border-left: 3px solid rgba(221, 0, 0, .8) !important; +} +:root.yotsuba-b.highlight-own .yourPost$site$highlightable$reply { + border-left: 3px dashed rgba(221, 0, 0, .8) !important; +} + /* Header */ :root.yotsuba-b #header-bar.dialog { background-color: rgba(214,218,240,0.98); diff --git a/src/css/yotsuba.css b/src/css/yotsuba.css index 7ed23a580..ea8b8d799 100644 --- a/src/css/yotsuba.css +++ b/src/css/yotsuba.css @@ -8,6 +8,14 @@ border-color: #EA8; } +/* 4chan style fixes */ +:root.yotsuba.highlight-you .quotesYou$site$highlightable$reply { + border-left: 3px solid rgba(221, 0, 0, .8) !important; +} +:root.yotsuba.highlight-own .yourPost$site$highlightable$reply { + border-left: 3px dashed rgba(221, 0, 0, .8) !important; +} + /* Header */ :root.yotsuba #header-bar.dialog { background-color: rgba(240,224,214,0.98); From 32ad98a0d6c4b80005a8ef8f03c313b6bb53e6c8 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Tue, 23 Jul 2019 00:17:24 -0700 Subject: [PATCH 31/33] Make Open Thread keybinds work on Tinyboard/vichan. #2171 --- src/Miscellaneous/Keybinds.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index ce4cae17c..f89cc9d94 100644 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -315,9 +315,9 @@ Keybinds = open: (thread, tab) -> return if g.VIEW isnt 'index' - url = "/#{thread.board}/thread/#{thread}" + url = Get.url 'thread', thread if tab - $.open location.origin + url + $.open url else location.href = url From f89f9a2bbb7894f6bbfc3c33b79262457a3f38d3 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Tue, 23 Jul 2019 02:13:34 -0700 Subject: [PATCH 32/33] Limit number of autoretries on posting error. #1302 --- src/Posting/QR.coffee | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 397d7a0ae..bc7a655db 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -775,19 +775,24 @@ QR = delete QR.currentCaptcha if err + QR.errorCount = (QR.errorCount or 0) + 1 if /captcha|verification/i.test(err.textContent) or connErr # Remove the obnoxious 4chan Pass ad. if /mistyped/i.test err.textContent err = 'You mistyped the CAPTCHA, or the CAPTCHA malfunctioned.' else if /expired/i.test err.textContent err = 'This CAPTCHA is no longer valid because it has expired.' - # Something must've gone terribly wrong if you get captcha errors without captchas. - # Don't auto-post indefinitely in that case. - QR.cooldown.auto = QR.captcha.isEnabled or connErr - # Too many frequent mistyped captchas will auto-ban you! - # On connection error, the post most likely didn't go through. - # If the post did go through, it should be stopped by the duplicate reply cooldown. - QR.cooldown.addDelay post, 2 + if QR.errorCount >= 5 + # Too many posting errors can ban you. Stop autoposting after 5 errors. + QR.cooldown.auto = false + else + # Something must've gone terribly wrong if you get captcha errors without captchas. + # Don't auto-post indefinitely in that case. + QR.cooldown.auto = QR.captcha.isEnabled or connErr + # Too many frequent mistyped captchas will auto-ban you! + # On connection error, the post most likely didn't go through. + # If the post did go through, it should be stopped by the duplicate reply cooldown. + QR.cooldown.addDelay post, 2 else if err.textContent and (m = err.textContent.match /(?:(\d+)\s+minutes?\s+)?(\d+)\s+second/i) and !/duplicate|hour/i.test(err.textContent) QR.cooldown.auto = !/have\s+been\s+muted/i.test(err.textContent) seconds = 60 * (+(m[1]||0)) + (+m[2]) @@ -802,6 +807,8 @@ QR = QR.error err return + delete QR.errorCount + h1 = $ 'h1', @response [_, threadID, postID] = h1.nextSibling.textContent.match /thread:(\d+),no:(\d+)/ From d41c0cf96dbd417ed6d0a51ff2992f3229bc05f1 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Wed, 24 Jul 2019 14:41:49 -0700 Subject: [PATCH 33/33] Make scroll to post quoting you keybinds work on Tinyboard/vichan. #2171 --- src/Quotelinks/QuoteYou.coffee | 17 +++++++++++------ src/site/SW.tinyboard.coffee | 1 + src/site/SW.yotsuba.coffee | 1 + 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Quotelinks/QuoteYou.coffee b/src/Quotelinks/QuoteYou.coffee index b3d49600b..d49c5457b 100644 --- a/src/Quotelinks/QuoteYou.coffee +++ b/src/Quotelinks/QuoteYou.coffee @@ -92,7 +92,8 @@ QuoteYou = cb: seek: (type) -> - $.rmClass highlight, 'highlight' if highlight = $ '.highlight' + {highlight} = g.SITE.classes + $.rmClass highlighted, highlight if (highlighted = $ ".#{highlight}") unless QuoteYou.lastRead and doc.contains(QuoteYou.lastRead) and $.hasClass(QuoteYou.lastRead, 'quotesYou') if not (post = QuoteYou.lastRead = $ '.quotesYou') @@ -111,12 +112,16 @@ QuoteYou = QuoteYou.cb.scroll posts[if type is 'following' then 0 else posts.length - 1] scroll: (root) -> - post = $ '.post', root - if !post.getBoundingClientRect().height + post = Get.postFromRoot root + if !post.nodes.post.getBoundingClientRect().height return false else QuoteYou.lastRead = root - location.href = "##{post.id}" - Header.scrollTo post - $.addClass post, 'highlight' + location.href = Get.url('post', post) + Header.scrollTo post.nodes.post + if post.isReply + sel = "#{g.SITE.selectors.postContainer}#{g.SITE.selectors.highlightable.reply}" + node = post.nodes.root + node = $ sel, node unless node.matches(sel) + $.addClass node, g.SITE.classes.highlight return true diff --git a/src/site/SW.tinyboard.coffee b/src/site/SW.tinyboard.coffee index 74aea824e..b30327cc9 100644 --- a/src/site/SW.tinyboard.coffee +++ b/src/site/SW.tinyboard.coffee @@ -45,6 +45,7 @@ SW.tinyboard = urls: thread: ({siteID, boardID, threadID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/res/#{threadID}.html" + post: ({postID}) -> "##{postID}" index: ({siteID, boardID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/" catalog: ({siteID, boardID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/catalog.html" threadJSON: ({siteID, boardID, threadID}) -> diff --git a/src/site/SW.yotsuba.coffee b/src/site/SW.yotsuba.coffee index d9af44255..44521b85a 100644 --- a/src/site/SW.yotsuba.coffee +++ b/src/site/SW.yotsuba.coffee @@ -4,6 +4,7 @@ SW.yotsuba = urls: thread: ({boardID, threadID}) -> "#{location.protocol}//#{BoardConfig.domain(boardID)}/#{boardID}/thread/#{threadID}" + post: ({postID}) -> "#p#{postID}" index: ({boardID}) -> "#{location.protocol}//#{BoardConfig.domain(boardID)}/#{boardID}/" catalog: ({boardID}) -> if boardID is 'f' then undefined else "#{location.protocol}//#{BoardConfig.domain(boardID)}/#{boardID}/catalog" threadJSON: ({boardID, threadID}) -> "#{location.protocol}//a.4cdn.org/#{boardID}/thread/#{threadID}.json"